3 """ DB access functions
4 @group readonly: get_suite_id, get_section_id, get_priority_id, get_override_type_id,
5 get_architecture_id, get_archive_id, get_component_id, get_location_id,
6 get_source_id, get_suite_version, get_files_id, get_maintainer, get_suites
7 @group read/write: get_or_set*, set_files_id
9 @contact: Debian FTP Master <ftpmaster@debian.org>
10 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
11 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
12 @license: GNU General Public License version 2 or later
15 # This program is free software; you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation; either version 2 of the License, or
18 # (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 ################################################################################
35 ################################################################################
37 Cnf = None #: Configuration, apt_pkg.Configuration
38 projectB = None #: database connection, pgobject
39 suite_id_cache = {} #: cache for suites
40 section_id_cache = {} #: cache for sections
41 priority_id_cache = {} #: cache for priorities
42 override_type_id_cache = {} #: cache for overrides
43 architecture_id_cache = {} #: cache for architectures
44 archive_id_cache = {} #: cache for archives
45 component_id_cache = {} #: cache for components
46 location_id_cache = {} #: cache for locations
47 maintainer_id_cache = {} #: cache for maintainers
48 keyring_id_cache = {} #: cache for keyrings
49 source_id_cache = {} #: cache for sources
50 files_id_cache = {} #: cache for files
51 maintainer_cache = {} #: cache for maintainer names
52 fingerprint_id_cache = {} #: cache for fingerprints
53 queue_id_cache = {} #: cache for queues
54 uid_id_cache = {} #: cache for uids
55 suite_version_cache = {} #: cache for suite_versions (packages)
57 ################################################################################
59 def init (config, sql):
63 @type config: apt_pkg.Configuration
64 @param config: apt config, see U{http://apt.alioth.debian.org/python-apt-doc/apt_pkg/cache.html#Configuration}
67 @param sql: database connection
78 Executes a database query. Writes statistics / timing to stderr.
81 @param query: database query string, passed unmodified
85 @warning: The query is passed B{unmodified}, so be careful what you use this for.
87 sys.stderr.write("query: \"%s\" ... " % (query))
89 r = projectB.query(query)
90 time_diff = time.time()-before
91 sys.stderr.write("took %.3f seconds.\n" % (time_diff))
93 sys.stderr.write("int result: %s\n" % (r))
94 elif type(r) is types.NoneType:
95 sys.stderr.write("result: None\n")
97 sys.stderr.write("pgresult: %s\n" % (r.getresult()))
100 ################################################################################
102 def get_suite_id (suite):
104 Returns database id for given C{suite}.
105 Results are kept in a cache during runtime to minimize database queries.
108 @param suite: The name of the suite
111 @return: the database id for the given suite
114 global suite_id_cache
116 if suite_id_cache.has_key(suite):
117 return suite_id_cache[suite]
119 q = projectB.query("SELECT id FROM suite WHERE suite_name = '%s'" % (suite))
125 suite_id_cache[suite] = suite_id
129 def get_section_id (section):
131 Returns database id for given C{section}.
132 Results are kept in a cache during runtime to minimize database queries.
134 @type section: string
135 @param section: The name of the section
138 @return: the database id for the given section
141 global section_id_cache
143 if section_id_cache.has_key(section):
144 return section_id_cache[section]
146 q = projectB.query("SELECT id FROM section WHERE section = '%s'" % (section))
151 section_id = ql[0][0]
152 section_id_cache[section] = section_id
156 def get_priority_id (priority):
158 Returns database id for given C{priority}.
159 Results are kept in a cache during runtime to minimize database queries.
161 @type priority: string
162 @param priority: The name of the priority
165 @return: the database id for the given priority
168 global priority_id_cache
170 if priority_id_cache.has_key(priority):
171 return priority_id_cache[priority]
173 q = projectB.query("SELECT id FROM priority WHERE priority = '%s'" % (priority))
178 priority_id = ql[0][0]
179 priority_id_cache[priority] = priority_id
183 def get_override_type_id (type):
185 Returns database id for given override C{type}.
186 Results are kept in a cache during runtime to minimize database queries.
189 @param type: The name of the override type
192 @return: the database id for the given override type
195 global override_type_id_cache
197 if override_type_id_cache.has_key(type):
198 return override_type_id_cache[type]
200 q = projectB.query("SELECT id FROM override_type WHERE type = '%s'" % (type))
205 override_type_id = ql[0][0]
206 override_type_id_cache[type] = override_type_id
208 return override_type_id
210 def get_architecture_id (architecture):
212 Returns database id for given C{architecture}.
213 Results are kept in a cache during runtime to minimize database queries.
215 @type architecture: string
216 @param architecture: The name of the override type
219 @return: the database id for the given architecture
222 global architecture_id_cache
224 if architecture_id_cache.has_key(architecture):
225 return architecture_id_cache[architecture]
227 q = projectB.query("SELECT id FROM architecture WHERE arch_string = '%s'" % (architecture))
232 architecture_id = ql[0][0]
233 architecture_id_cache[architecture] = architecture_id
235 return architecture_id
237 def get_archive_id (archive):
239 Returns database id for given C{archive}.
240 Results are kept in a cache during runtime to minimize database queries.
242 @type archive: string
243 @param archive: The name of the override type
246 @return: the database id for the given archive
249 global archive_id_cache
251 archive = archive.lower()
253 if archive_id_cache.has_key(archive):
254 return archive_id_cache[archive]
256 q = projectB.query("SELECT id FROM archive WHERE lower(name) = '%s'" % (archive))
261 archive_id = ql[0][0]
262 archive_id_cache[archive] = archive_id
266 def get_component_id (component):
268 Returns database id for given C{component}.
269 Results are kept in a cache during runtime to minimize database queries.
271 @type component: string
272 @param component: The name of the component
275 @return: the database id for the given component
278 global component_id_cache
280 component = component.lower()
282 if component_id_cache.has_key(component):
283 return component_id_cache[component]
285 q = projectB.query("SELECT id FROM component WHERE lower(name) = '%s'" % (component))
290 component_id = ql[0][0]
291 component_id_cache[component] = component_id
295 def get_location_id (location, component, archive):
297 Returns database id for the location behind the given combination of
298 - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
299 - B{component} - the id of the component as returned by L{get_component_id}
300 - B{archive} - the id of the archive as returned by L{get_archive_id}
301 Results are kept in a cache during runtime to minimize database queries.
303 @type location: string
304 @param location: the path of the location
307 @param component: the id of the component
310 @param archive: the id of the archive
313 @return: the database id for the location
316 global location_id_cache
318 cache_key = location + '_' + component + '_' + location
319 if location_id_cache.has_key(cache_key):
320 return location_id_cache[cache_key]
322 archive_id = get_archive_id (archive)
324 component_id = get_component_id (component)
325 if component_id != -1:
326 q = projectB.query("SELECT id FROM location WHERE path = '%s' AND component = %d AND archive = %d" % (location, component_id, archive_id))
328 q = projectB.query("SELECT id FROM location WHERE path = '%s' AND archive = %d" % (location, archive_id))
333 location_id = ql[0][0]
334 location_id_cache[cache_key] = location_id
338 def get_source_id (source, version):
340 Returns database id for the combination of C{source} and C{version}
341 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
343 Results are kept in a cache during runtime to minimize database queries.
346 @param source: source package name
348 @type version: string
349 @param version: the source version
352 @return: the database id for the source
355 global source_id_cache
357 cache_key = source + '_' + version + '_'
358 if source_id_cache.has_key(cache_key):
359 return source_id_cache[cache_key]
361 q = projectB.query("SELECT id FROM source s WHERE s.source = '%s' AND s.version = '%s'" % (source, version))
363 if not q.getresult():
366 source_id = q.getresult()[0][0]
367 source_id_cache[cache_key] = source_id
371 def get_suite_version(source, suite):
373 Returns database id for a combination of C{source} and C{suite}.
375 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
376 - B{suite} - a suite name, eg. I{unstable}
378 Results are kept in a cache during runtime to minimize database queries.
381 @param source: source package name
384 @param suite: the suite name
387 @return: the version for I{source} in I{suite}
390 global suite_version_cache
391 cache_key = "%s_%s" % (source, suite)
393 if suite_version_cache.has_key(cache_key):
394 return suite_version_cache[cache_key]
396 q = projectB.query("""
397 SELECT s.version FROM source s, suite su, src_associations sa
400 AND su.suite_name='%s'
404 if not q.getresult():
407 version = q.getresult()[0][0]
408 suite_version_cache[cache_key] = version
412 ################################################################################
414 def get_or_set_maintainer_id (maintainer):
416 If C{maintainer} does not have an entry in the maintainer table yet, create one
417 and return the new id.
418 If C{maintainer} already has an entry, simply return the existing id.
420 Results are kept in a cache during runtime to minimize database queries.
422 @type maintainer: string
423 @param maintainer: the maintainer name
426 @return: the database id for the maintainer
429 global maintainer_id_cache
431 if maintainer_id_cache.has_key(maintainer):
432 return maintainer_id_cache[maintainer]
434 q = projectB.query("SELECT id FROM maintainer WHERE name = '%s'" % (maintainer))
435 if not q.getresult():
436 projectB.query("INSERT INTO maintainer (name) VALUES ('%s')" % (maintainer))
437 q = projectB.query("SELECT id FROM maintainer WHERE name = '%s'" % (maintainer))
438 maintainer_id = q.getresult()[0][0]
439 maintainer_id_cache[maintainer] = maintainer_id
443 ################################################################################
445 def get_or_set_keyring_id (keyring):
447 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
448 and return the new id.
449 If C{keyring} already has an entry, simply return the existing id.
451 Results are kept in a cache during runtime to minimize database queries.
453 @type keyring: string
454 @param keyring: the keyring name
457 @return: the database id for the keyring
460 global keyring_id_cache
462 if keyring_id_cache.has_key(keyring):
463 return keyring_id_cache[keyring]
465 q = projectB.query("SELECT id FROM keyrings WHERE name = '%s'" % (keyring))
466 if not q.getresult():
467 projectB.query("INSERT INTO keyrings (name) VALUES ('%s')" % (keyring))
468 q = projectB.query("SELECT id FROM keyrings WHERE name = '%s'" % (keyring))
469 keyring_id = q.getresult()[0][0]
470 keyring_id_cache[keyring] = keyring_id
474 ################################################################################
476 def get_or_set_uid_id (uid):
478 If C{uid} does not have an entry in the uid table yet, create one
479 and return the new id.
480 If C{uid} already has an entry, simply return the existing id.
482 Results are kept in a cache during runtime to minimize database queries.
488 @return: the database id for the uid
494 if uid_id_cache.has_key(uid):
495 return uid_id_cache[uid]
497 q = projectB.query("SELECT id FROM uid WHERE uid = '%s'" % (uid))
498 if not q.getresult():
499 projectB.query("INSERT INTO uid (uid) VALUES ('%s')" % (uid))
500 q = projectB.query("SELECT id FROM uid WHERE uid = '%s'" % (uid))
501 uid_id = q.getresult()[0][0]
502 uid_id_cache[uid] = uid_id
506 ################################################################################
508 def get_or_set_fingerprint_id (fingerprint):
510 If C{fingerprint} does not have an entry in the fingerprint table yet, create one
511 and return the new id.
512 If C{fingerprint} already has an entry, simply return the existing id.
514 Results are kept in a cache during runtime to minimize database queries.
516 @type fingerprint: string
517 @param fingerprint: the fingerprint
520 @return: the database id for the fingerprint
523 global fingerprint_id_cache
525 if fingerprint_id_cache.has_key(fingerprint):
526 return fingerprint_id_cache[fingerprint]
528 q = projectB.query("SELECT id FROM fingerprint WHERE fingerprint = '%s'" % (fingerprint))
529 if not q.getresult():
530 projectB.query("INSERT INTO fingerprint (fingerprint) VALUES ('%s')" % (fingerprint))
531 q = projectB.query("SELECT id FROM fingerprint WHERE fingerprint = '%s'" % (fingerprint))
532 fingerprint_id = q.getresult()[0][0]
533 fingerprint_id_cache[fingerprint] = fingerprint_id
535 return fingerprint_id
537 ################################################################################
539 def get_files_id (filename, size, md5sum, location_id):
541 Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
544 The database is queried using the C{filename} and C{location_id}. If a file does exist
545 at that location, the existing size and md5sum are checked against the provided
546 parameters. A size or checksum mismatch returns -2. If more than one entry is
547 found within the database, a -1 is returned, no result returns None, otherwise
550 Results are kept in a cache during runtime to minimize database queries.
552 @type filename: string
553 @param filename: the filename of the file to check against the DB
556 @param size: the size of the file to check against the DB
559 @param md5sum: the md5sum of the file to check against the DB
561 @type location_id: int
562 @param location_id: the id of the location as returned by L{get_location_id}
565 @return: Various return values are possible:
566 - -2: size/checksum error
567 - -1: more than one file found in database
568 - None: no file found in database
572 global files_id_cache
574 cache_key = "%s_%d" % (filename, location_id)
576 if files_id_cache.has_key(cache_key):
577 return files_id_cache[cache_key]
580 q = projectB.query("SELECT id, size, md5sum FROM files WHERE filename = '%s' AND location = %d" % (filename, location_id))
586 orig_size = int(ql[1])
588 if orig_size != size or orig_md5sum != md5sum:
590 files_id_cache[cache_key] = ql[0]
591 return files_id_cache[cache_key]
595 ################################################################################
597 def get_or_set_queue_id (queue):
599 If C{queue} does not have an entry in the queue table yet, create one
600 and return the new id.
601 If C{queue} already has an entry, simply return the existing id.
603 Results are kept in a cache during runtime to minimize database queries.
606 @param queue: the queue name (no full path)
609 @return: the database id for the queue
612 global queue_id_cache
614 if queue_id_cache.has_key(queue):
615 return queue_id_cache[queue]
617 q = projectB.query("SELECT id FROM queue WHERE queue_name = '%s'" % (queue))
618 if not q.getresult():
619 projectB.query("INSERT INTO queue (queue_name) VALUES ('%s')" % (queue))
620 q = projectB.query("SELECT id FROM queue WHERE queue_name = '%s'" % (queue))
621 queue_id = q.getresult()[0][0]
622 queue_id_cache[queue] = queue_id
626 ################################################################################
628 def set_files_id (filename, size, md5sum, sha1sum, sha256sum, location_id):
630 Insert a new entry into the files table and return its id.
632 @type filename: string
633 @param filename: the filename
636 @param size: the size in bytes
639 @param md5sum: md5sum of the file
641 @type sha1sum: string
642 @param sha1sum: sha1sum of the file
644 @type sha256sum: string
645 @param sha256sum: sha256sum of the file
647 @type location_id: int
648 @param location_id: the id of the location as returned by L{get_location_id}
651 @return: the database id for the new file
654 global files_id_cache
656 projectB.query("INSERT INTO files (filename, size, md5sum, sha1sum, sha256sum, location) VALUES ('%s', %d, '%s', '%s', '%s', %d)" % (filename, long(size), md5sum, sha1sum, sha256sum, location_id))
658 return get_files_id (filename, size, md5sum, location_id)
660 ### currval has issues with postgresql 7.1.3 when the table is big
661 ### it was taking ~3 seconds to return on auric which is very Not
664 ##q = projectB.query("SELECT id FROM files WHERE id = currval('files_id_seq')")
665 ##ql = q.getresult()[0]
666 ##cache_key = "%s_%d" % (filename, location_id)
667 ##files_id_cache[cache_key] = ql[0]
668 ##return files_id_cache[cache_key]
670 ################################################################################
672 def get_maintainer (maintainer_id):
674 Return the name of the maintainer behind C{maintainer_id}.
676 Results are kept in a cache during runtime to minimize database queries.
678 @type maintainer_id: int
679 @param maintainer_id: the id of the maintainer, eg. from L{get_or_set_maintainer_id}
682 @return: the name of the maintainer
685 global maintainer_cache
687 if not maintainer_cache.has_key(maintainer_id):
688 q = projectB.query("SELECT name FROM maintainer WHERE id = %s" % (maintainer_id))
689 maintainer_cache[maintainer_id] = q.getresult()[0][0]
691 return maintainer_cache[maintainer_id]
693 ################################################################################
695 def get_suites(pkgname, src=False):
697 Return the suites in which C{pkgname} can be found. If C{src} is True query for source
698 package, else binary package.
700 @type pkgname: string
701 @param pkgname: name of the package
704 @param src: if True look for source packages, false (default) looks for binary.
707 @return: list of suites, or empty list if no match
716 WHERE source.id = src_associations.source
717 AND source.source = '%s'
718 AND src_associations.suite = suite.id
726 WHERE binaries.id = bin_associations.bin
728 AND bin_associations.suite = suite.id
731 q = projectB.query(sql)
732 return map(lambda x: x[0], q.getresult())