5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from sqlalchemy import create_engine, Table, MetaData, select
41 from sqlalchemy.orm import sessionmaker, mapper
43 from singleton import Singleton
44 from config import Config
46 ################################################################################
49 def __init__(self, hashfunc=None):
51 self.hashfunc = hashfunc
53 self.hashfunc = lambda x: str(x)
57 def SetValue(self, keys, value):
58 self.data[self.hashfunc(keys)] = value
60 def GetValue(self, keys):
61 return self.data.get(self.hashfunc(keys))
63 ################################################################################
65 class Architecture(object):
66 def __init__(self, arch_id=None, arch_string=None, description=None):
67 self.arch_id = arch_id
68 self.arch_string = arch_string
69 self.description = description
72 return '<Architecture %s>' % self.arch_string
74 class Archive(object):
75 def __init__(self, archive_id=None, archive_name=None, origin_server=None,
77 self.archive_id = archive_id
78 self.archive_name = archive_name
79 self.origin_server = origin_server
80 self.description = description
83 return '<Archive %s>' % self.name
85 class BinAssociation(object):
86 def __init__(self, ba_id=None, suite_id=None, bin_id=None):
88 self.suite_id = suite_id
92 return '<BinAssociation %s>' % self.ba_id
95 def __init__(self, binary_id=None, package=None, version=None,
96 maintainer_id=None, source_id=None, arch_id=None,
97 file_id=None, filetype=None, fingerprint_id=None,
99 self.binary_id = binary_id
100 self.package = package
101 self.version = version
102 self.maintainer_id = maintainer_id
103 self.source_id = source_id
104 self.arch_id = arch_id
105 self.file_id = file_id
106 self.filetype = filetype
107 self.fingerprint_id = fingerprint_id
108 self.install_date = install_date
111 return '<Binary %s (%s) %s>' % (self.package, self.version, self.arch_id)
113 class Component(object):
114 def __init__(self, component_id=None, component_name=None,
115 description=None, meets_dfsg=None,):
116 self.component_id = component_id
117 self.component_name = component_name
118 self.description = description
119 self.meets_dfsg = meets_dfsg
122 return '<Component %s>' % self.component_name
124 class DBConfig(object):
125 def __init__(self, config_id=None, name=None, value=None):
126 self.config_id = config_id
131 return '<DBConfig %s>' % self.name
133 class ContentFilename(object):
134 def __init__(self, cafilename_id=None, filename=None):
135 self.cafilename_id = cafilename_id
136 self.filename = filename
139 return '<ContentFilename %s>' % self.filename
141 class ContentFilepath(object):
142 def __init__(self, cafilepath_id=None, filepath=None):
143 self.cafilepath_id = cafilepath_id
144 self.filepath = filepath
147 return '<ContentFilepath %s>' % self.filepath
149 class ContentAssociations(object):
150 def __init__(self, binary_id=None, filename_id=None, filepath_id=None,
152 self.binary_id = binary_id
153 self.filename_id = filename_id
154 self.filepath_id = filepath_id
158 return '<ContentAssociation %s>' % self.ca_id
160 class DSCFile(object):
161 def __init__(self, dscfile_id=None, source_id=None, file_id=None):
162 self.dscfile_id = dscfile_id
163 self.source_id = source_id
164 self.file_id = file_id
167 return '<DSCFile %s>' % self.dscfile_id
169 class PoolFile(object):
170 def __init__(self, file_id=None, filename=None, filesize=None,
171 location_id=None, last_used=None, md5sum=None,
172 sha1sum=None, sha256sum=None):
173 self.file_id = file_id
174 self.filename = filename
175 self.filesize = filesize
176 self.location_id = location_id
177 self.last_used = last_used
179 self.sha1sum = sha1sum
180 self.sha256sum = sha256sum
183 return '<PoolFile %s>' % self.filename
185 class Fingerprint(object):
186 def __init__(self, fingerprint_id=None, fingerprint=None,
187 uid_id=None, keyring_id=None):
188 self.fingerprint_id = fingerprint_id
189 self.fingerprint = fingerprint
191 self.keyring_id = keyring_id
194 return '<Fingerprint %s>' % self.fingerprint
196 class Keyring(object):
197 def __init__(self, keyring_id=None, keyring_name=None,
198 debian_maintainer=None):
199 self.keyring_id = keyring_id
200 self.keyring_name = keyring_name
201 self.debian_maintainer = debian_maintainer
204 return '<Keyring %s>' % self.keyring_name
206 class Location(object):
207 def __init__(self, location_id=None, path=None,
208 component_id=None, archive_id=None,
210 self.location_id = location_id
212 self.component_id = component_id
213 self.archive_id = archive_id
214 self.archive_type = archive_type
217 return '<Location %s (%s)>' % (self.path, self.location_id)
219 class Maintainer(object):
220 def __init__(self, maintainer_id=None, name=None):
221 self.maintainer_id = maintainer_id
225 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
227 class Override(object):
228 def __init__(self, package, suite_id=None, component_id=None,
229 priority_id=None, section_id=None, overridetype_id=None,
231 self.package = package
232 self.suite_id = suite_id
233 self.component_id = component_id
234 self.priority_id = priority_id
235 self.section_id = section_id
236 self.overridetype_id = overridetype_id
237 self.maintainer = maintainer
240 return '<Override %s (%s)>' % (self.package, self.suite_id)
242 class OverrideType(object):
243 def __init__(self, overridetype_id=None, overridetype=None):
244 self.overridetype_id = overridetype_id
245 self.overridetype = overridetype
248 return '<OverrideType %s>' % self.overridetype
250 class PendingContentAssociation(object):
251 def __init__(self, pca_id=None, package=None, version=None,
252 filepath_id=None, filename_id=None):
254 self.package = package
255 self.version = version
256 self.filepath_id = filepath_id
257 self.filename_id = filename_id
260 return '<PendingContentAssociation %s>' % self.pca_id
262 class Priority(object):
263 def __init__(self, priority_id=None, priority=None, level=None):
264 self.priority_id = priority_id
265 self.priority = priority
269 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
272 def __init__(self, queue_id=None, queue_name=None):
273 self.queue_id = queue_id
274 self.queue_name = queue_name
277 return '<Queue %s>' % self.queue_name
279 class QueueBuild(object):
280 def __init__(self, suite_id=None, queue_id=None, filename=None,
281 in_queue=None, last_used=None):
282 self.suite_id = suite_id
283 self.queue_id = queue_id
284 self.filename = filename
285 self.in_queue = in_queue
286 self.last_used = last_used
289 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
291 class Section(object):
292 def __init__(self, section_id=None, section=None):
293 self.section_id = section_id
294 self.section = section
297 return '<Section %s>' % self.section
299 class Source(object):
300 def __init__(self, source_id=None, source=None, version=None,
301 maintainer_id=None, file_id=None, fingerprint_id=None,
302 install_date=None, changedby_id=None, dm_upload_allowed=None):
303 self.source_id = source_id
305 self.version = version
306 self.maintainer_id = maintainer_id
307 self.file_id = file_id
308 self.fingerprint_id = fingerprint_id
309 self.install_date = install_date
310 self.changedby_id = changedby_id
311 self.dm_upload_allowed = dm_upload_allowed
314 return '<Source %s (%s)>' % (self.source, self.version)
316 class SrcAssociation(object):
317 def __init__(self, sa_id=None, suite_id=None, source_id=None):
319 self.suite_id = suite_id
320 self.source_id = source_id
323 return '<SrcAssociation %s>' % self.sa_id
325 class SrcUploader(object):
326 def __init__(self, uploader_id=None, source_id=None, maintainer_id=None):
327 self.uploader_id = uploader_id
328 self.source_id = source_id
329 self.maintainer_id = maintainer_id
332 return '<SrcUploader %s>' % self.uploader_id
335 def __init__(self, suite_id=None, suite_name=None, version=None,
336 origin=None, label=None, policy_engine=None,
337 description=None, untouchable=None, announce=None,
338 codename=None, overridecodename=None, validtime=None,
339 priority=None, notautomatic=None, copychanges=None,
340 copydotdak=None, commentsdir=None, overridesuite=None,
343 self.suite_id = suite_id
344 self.suite_name = suite_name
345 self.version = version
348 self.policy_engine = policy_engine
349 self.description = description
350 self.untouchable = untouchable
351 self.announce = announce
352 self.codename = codename
353 self.overridecodename = overridecodename
354 self.validtime = validtime
355 self.priority = priority
356 self.notautomatic = notautomatic
357 self.copychanges = copychanges
358 self.copydotdak = copydotdak
359 self.commentsdir = commentsdir
360 self.overridesuite = overridesuite
361 self.changelogbase = changelogbase
364 return '<Suite %s>' % self.suite_name
366 class SuiteArchitecture(object):
367 def __init__(self, suite_id=None, arch_id=None):
368 self.suite_id = suite_id
369 self.arch_id = arch_id
372 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
375 def __init__(self, uid_id=None, uid=None, name=None):
381 return '<Uid %s (%s)>' % (self.uid, self.name)
383 ################################################################################
385 class DBConn(Singleton):
387 database module init.
389 def __init__(self, *args, **kwargs):
390 super(DBConn, self).__init__(*args, **kwargs)
392 def _startup(self, *args, **kwargs):
396 def __setuptables(self):
397 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
398 mapper(Architecture, self.tbl_architecture,
399 properties = dict(arch_id = self.tbl_architecture.c.id))
401 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
402 mapper(Archive, self.tbl_archive,
403 properties = dict(archive_id = self.tbl_archive.c.id,
404 archive_name = self.tbl_archive.c.name))
406 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
407 mapper(BinAssociation, self.tbl_bin_associations,
408 properties = dict(ba_id = self.tbl_bin_associations.c.id,
409 suite_id = self.tbl_bin_associations.c.suite,
410 bin_id = self.tbl_bin_associations.c.bin))
412 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
413 mapper(Binary, self.tbl_binaries,
414 properties = dict(binary_id = self.tbl_binaries.c.id,
415 file_id = self.tbl_binaries.c.file,
416 filetype = self.tbl_binaries.c.type,
417 maintainer_id = self.tbl_binaries.c.maintainer,
418 source_id = self.tbl_binaries.c.source,
419 arch_id = self.tbl_binaries.c.architecture,
420 fingerprint_id = self.tbl_binaries.c.sig_fpr))
422 self.tbl_component = Table('component', self.db_meta, autoload=True)
423 mapper(Component, self.tbl_component,
424 properties = dict(component_id = self.tbl_component.c.id,
425 component_name = self.tbl_component.c.name))
427 self.tbl_config = Table('config', self.db_meta, autoload=True)
428 mapper(DBConfig, self.tbl_config,
429 properties = dict(config_id = self.tbl_config.c.id))
431 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
432 mapper(ContentAssociations, self.tbl_content_associations,
433 properties = dict(ca_id = self.tbl_content_associations.c.id,
434 filename_id = self.tbl_content_associations.c.filename,
435 filepath_id = self.tbl_content_associations.c.filepath,
436 binary_id = self.tbl_content_associations.c.binary_pkg))
438 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
439 mapper(ContentFilename, self.tbl_content_file_names,
440 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
441 filename = self.tbl_content_file_names.c.file))
443 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
444 mapper(ContentFilepath, self.tbl_content_file_paths,
445 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
446 filepath = self.tbl_content_file_paths.c.path))
448 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
449 mapper(DSCFile, self.tbl_dsc_files,
450 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
451 source_id = self.tbl_dsc_files.c.source,
452 file_id = self.tbl_dsc_files.c.file))
454 self.tbl_files = Table('files', self.db_meta, autoload=True)
455 mapper(PoolFile, self.tbl_files,
456 properties = dict(file_id = self.tbl_files.c.id,
457 filesize = self.tbl_files.c.size,
458 location_id = self.tbl_files.c.location))
460 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
461 mapper(Fingerprint, self.tbl_fingerprint,
462 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
463 uid_id = self.tbl_fingerprint.c.uid,
464 keyring_id = self.tbl_fingerprint.c.keyring))
466 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
467 mapper(Keyring, self.tbl_keyrings,
468 properties = dict(keyring_name = self.tbl_keyrings.c.name,
469 keyring_id = self.tbl_keyrings.c.id))
471 self.tbl_location = Table('location', self.db_meta, autoload=True)
472 mapper(Location, self.tbl_location,
473 properties = dict(location_id = self.tbl_location.c.id,
474 component_id = self.tbl_location.c.component,
475 archive_id = self.tbl_location.c.archive,
476 archive_type = self.tbl_location.c.type))
478 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
479 mapper(Maintainer, self.tbl_maintainer,
480 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
482 self.tbl_override = Table('override', self.db_meta, autoload=True)
483 mapper(Override, self.tbl_override,
484 properties = dict(suite_id = self.tbl_override.c.suite,
485 component_id = self.tbl_override.c.component,
486 priority_id = self.tbl_override.c.priority,
487 section_id = self.tbl_override.c.section,
488 overridetype_id = self.tbl_override.c.type))
490 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
491 mapper(OverrideType, self.tbl_override_type,
492 properties = dict(overridetype = self.tbl_override_type.c.type,
493 overridetype_id = self.tbl_override_type.c.id))
495 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
496 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
497 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
498 filepath_id = self.tbl_pending_content_associations.c.filepath,
499 filename_id = self.tbl_pending_content_associations.c.filename))
501 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
502 mapper(Priority, self.tbl_priority,
503 properties = dict(priority_id = self.tbl_priority.c.id))
505 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
506 mapper(Queue, self.tbl_queue,
507 properties = dict(queue_id = self.tbl_queue.c.id))
509 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
510 mapper(QueueBuild, self.tbl_queue_build,
511 properties = dict(suite_id = self.tbl_queue_build.c.suite,
512 queue_id = self.tbl_queue_build.c.queue))
514 self.tbl_section = Table('section', self.db_meta, autoload=True)
515 mapper(Section, self.tbl_section,
516 properties = dict(section_id = self.tbl_section.c.id))
518 self.tbl_source = Table('source', self.db_meta, autoload=True)
519 mapper(Source, self.tbl_source,
520 properties = dict(source_id = self.tbl_source.c.id,
521 maintainer_id = self.tbl_source.c.maintainer,
522 file_id = self.tbl_source.c.file,
523 fingerprint_id = self.tbl_source.c.sig_fpr,
524 changedby_id = self.tbl_source.c.changedby))
526 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
527 mapper(SrcAssociation, self.tbl_src_associations,
528 properties = dict(sa_id = self.tbl_src_associations.c.id))
530 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
531 mapper(SrcUploader, self.tbl_src_uploaders,
532 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
533 source_id = self.tbl_src_uploaders.c.source,
534 maintainer_id = self.tbl_src_uploaders.c.maintainer))
536 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
537 mapper(Suite, self.tbl_suite,
538 properties = dict(suite_id = self.tbl_suite.c.id))
540 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
541 mapper(SuiteArchitecture, self.tbl_suite_architectures,
542 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
543 arch_id = self.tbl_suite_architectures.c.architecture))
545 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
546 mapper(Uid, self.tbl_uid,
547 properties = dict(uid_id = self.tbl_uid.c.id))
549 ## Connection functions
550 def __createconn(self):
554 connstr = "postgres://%s" % cnf["DB::Host"]
555 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
556 connstr += ":%s" % cnf["DB::Port"]
557 connstr += "/%s" % cnf["DB::Name"]
560 connstr = "postgres:///%s" % cnf["DB::Name"]
561 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
562 connstr += "?port=%s" % cnf["DB::Port"]
564 self.db_pg = create_engine(connstr)
565 self.db_meta = MetaData()
566 self.db_meta.bind = self.db_pg
567 self.db_smaker = sessionmaker(bind=self.db_pg,
574 return self.db_smaker()
577 def __init_caches(self):
578 self.caches = {'suite': Cache(),
581 'override_type': Cache(),
582 'architecture': Cache(),
584 'component': Cache(),
585 'content_path_names': Cache(),
586 'content_file_names': Cache(),
587 'location': Cache(lambda x: '%s_%s_%s' % (x['location'], x['component'], x['location'])),
588 'maintainer': {}, # TODO
589 'keyring': {}, # TODO
590 'source': Cache(lambda x: '%s_%s_' % (x['source'], x['version'])),
591 'files': Cache(lambda x: '%s_%s_' % (x['filename'], x['location'])),
592 'maintainer': {}, # TODO
593 'fingerprint': {}, # TODO
596 'suite_version': Cache(lambda x: '%s_%s' % (x['source'], x['suite'])),
599 self.prepared_statements = {}
601 def prepare(self,name,statement):
602 if not self.prepared_statements.has_key(name):
603 pgc.execute(statement)
604 self.prepared_statements[name] = statement
606 def clear_caches(self):
610 def __get_id(self, retfield, selectobj, cachekey, cachename=None):
611 # This is a bit of a hack but it's an internal function only
612 if cachename is not None:
613 res = self.caches[cachename].GetValue(cachekey)
617 c = selectobj.execute()
624 if retfield not in res.keys():
629 if cachename is not None:
630 self.caches[cachename].SetValue(cachekey, res)
634 def get_suite_id(self, suite):
636 Returns database id for given C{suite}.
637 Results are kept in a cache during runtime to minimize database queries.
640 @param suite: The name of the suite
643 @return: the database id for the given suite
646 return int(self.__get_id('id',
647 self.tbl_suite.select(self.tbl_suite.columns.suite_name == suite),
651 def get_section_id(self, section):
653 Returns database id for given C{section}.
654 Results are kept in a cache during runtime to minimize database queries.
656 @type section: string
657 @param section: The name of the section
660 @return: the database id for the given section
663 return self.__get_id('id',
664 self.tbl_section.select(self.tbl_section.columns.section == section),
668 def get_priority_id(self, priority):
670 Returns database id for given C{priority}.
671 Results are kept in a cache during runtime to minimize database queries.
673 @type priority: string
674 @param priority: The name of the priority
677 @return: the database id for the given priority
680 return self.__get_id('id',
681 self.tbl_priority.select(self.tbl_priority.columns.priority == priority),
685 def get_override_type_id(self, override_type):
687 Returns database id for given override C{type}.
688 Results are kept in a cache during runtime to minimize database queries.
690 @type override_type: string
691 @param override_type: The name of the override type
694 @return: the database id for the given override type
697 return self.__get_id('id',
698 self.tbl_override_type.select(self.tbl_override_type.columns.type == override_type),
702 def get_architecture_id(self, architecture):
704 Returns database id for given C{architecture}.
705 Results are kept in a cache during runtime to minimize database queries.
707 @type architecture: string
708 @param architecture: The name of the override type
711 @return: the database id for the given architecture
714 return self.__get_id('id',
715 self.tbl_architecture.select(self.tbl_architecture.columns.arch_string == architecture),
719 def get_archive_id(self, archive):
721 returns database id for given c{archive}.
722 results are kept in a cache during runtime to minimize database queries.
724 @type archive: string
725 @param archive: the name of the override type
728 @return: the database id for the given archive
731 archive = archive.lower()
732 return self.__get_id('id',
733 self.tbl_archive.select(self.tbl_archive.columns.name == archive),
737 def get_component_id(self, component):
739 Returns database id for given C{component}.
740 Results are kept in a cache during runtime to minimize database queries.
742 @type component: string
743 @param component: The name of the override type
746 @return: the database id for the given component
749 component = component.lower()
750 return self.__get_id('id',
751 self.tbl_component.select(self.tbl_component.columns.name == component),
755 def get_location_id(self, location, component, archive):
757 Returns database id for the location behind the given combination of
758 - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
759 - B{component} - the id of the component as returned by L{get_component_id}
760 - B{archive} - the id of the archive as returned by L{get_archive_id}
761 Results are kept in a cache during runtime to minimize database queries.
763 @type location: string
764 @param location: the path of the location
766 @type component: string
767 @param component: the name of the component
769 @type archive: string
770 @param archive: the name of the archive
773 @return: the database id for the location
777 archive = archive.lower()
778 component = component.lower()
780 values = {'archive': archive, 'location': location, 'component': component}
782 s = self.tbl_location.join(self.tbl_archive).join(self.tbl_component)
784 s = s.select(self.tbl_location.columns.path == location)
785 s = s.where(self.tbl_archive.columns.name == archive)
786 s = s.where(self.tbl_component.columns.name == component)
788 return self.__get_id('location.id', s, values, 'location')
790 def get_source_id(self, source, version):
792 Returns database id for the combination of C{source} and C{version}
793 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
795 Results are kept in a cache during runtime to minimize database queries.
798 @param source: source package name
800 @type version: string
801 @param version: the source version
804 @return: the database id for the source
807 s = self.tbl_source.select()
808 s = s.where(self.tbl_source.columns.source == source)
809 s = s.where(self.tbl_source.columns.version == version)
811 return self.__get_id('id', s, {'source': source, 'version': version}, 'source')
813 def get_suite(self, suite):
814 if isinstance(suite, str):
815 suite_id = self.get_suite_id(suite.lower())
816 elif type(suite) == int:
819 s = self.tbl_suite.select(self.tbl_suite.columns.id == suite_id)
826 def get_suite_version(self, source, suite):
828 Returns database id for a combination of C{source} and C{suite}.
830 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
831 - B{suite} - a suite name, eg. I{unstable}
833 Results are kept in a cache during runtime to minimize database queries.
836 @param source: source package name
839 @param suite: the suite name
842 @return: the version for I{source} in I{suite}
845 s = select([self.tbl_source.columns.source, self.tbl_source.columns.version])
846 # s = self.tbl_source.join(self.tbl_src_associations).join(self.tbl_suite)
848 s = s.select(self.tbl_suite.columns.suite_name == suite, use_labels=True)
849 s = s.select(self.tbl_source.columns.source == source)
851 return self.__get_id('source.version', s, {'suite': suite, 'source': source}, 'suite_version')
854 def get_files_id (self, filename, size, md5sum, location_id):
856 Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
859 The database is queried using the C{filename} and C{location_id}. If a file does exist
860 at that location, the existing size and md5sum are checked against the provided
861 parameters. A size or checksum mismatch returns -2. If more than one entry is
862 found within the database, a -1 is returned, no result returns None, otherwise
865 Results are kept in a cache during runtime to minimize database queries.
867 @type filename: string
868 @param filename: the filename of the file to check against the DB
871 @param size: the size of the file to check against the DB
874 @param md5sum: the md5sum of the file to check against the DB
876 @type location_id: int
877 @param location_id: the id of the location as returned by L{get_location_id}
880 @return: Various return values are possible:
881 - -2: size/checksum error
882 - -1: more than one file found in database
883 - None: no file found in database
887 values = {'filename' : filename,
888 'location' : location_id}
890 res = self.caches['files'].GetValue( values )
893 query = """SELECT id, size, md5sum
895 WHERE filename = %(filename)s AND location = %(location)s"""
897 cursor = self.db_con.cursor()
898 cursor.execute( query, values )
900 if cursor.rowcount == 0:
903 elif cursor.rowcount != 1:
907 row = cursor.fetchone()
909 if row[1] != int(size) or row[2] != md5sum:
913 self.caches['files'].SetValue(values, row[0])
919 def get_or_set_contents_file_id(self, filename):
921 Returns database id for given filename.
923 Results are kept in a cache during runtime to minimize database queries.
924 If no matching file is found, a row is inserted.
926 @type filename: string
927 @param filename: The filename
930 @return: the database id for the given component
933 values={'value': filename}
934 query = "SELECT id FROM content_file_names WHERE file = %(value)s"
935 id = self.__get_single_id(query, values, cachename='content_file_names')
937 c = self.db_con.cursor()
938 c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id",
942 self.caches['content_file_names'].SetValue(values, id)
946 traceback.print_exc()
949 def get_or_set_contents_path_id(self, path):
951 Returns database id for given path.
953 Results are kept in a cache during runtime to minimize database queries.
954 If no matching file is found, a row is inserted.
957 @param path: The filename
960 @return: the database id for the given component
963 values={'value': path}
964 query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
965 id = self.__get_single_id(query, values, cachename='content_path_names')
967 c = self.db_con.cursor()
968 c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id",
972 self.caches['content_path_names'].SetValue(values, id)
976 traceback.print_exc()
979 def get_suite_architectures(self, suite):
981 Returns list of architectures for C{suite}.
983 @type suite: string, int
984 @param suite: the suite name or the suite_id
987 @return: the list of architectures for I{suite}
991 if type(suite) == str:
992 suite_id = self.get_suite_id(suite)
993 elif type(suite) == int:
998 c = self.db_con.cursor()
999 c.execute( """SELECT a.arch_string FROM suite_architectures sa
1000 JOIN architecture a ON (a.id = sa.architecture)
1001 WHERE suite='%s'""" % suite_id )
1003 return map(lambda x: x[0], c.fetchall())
1005 def insert_content_paths(self, bin_id, fullpaths):
1007 Make sure given path is associated with given binary id
1010 @param bin_id: the id of the binary
1011 @type fullpaths: list
1012 @param fullpaths: the list of paths of the file being associated with the binary
1014 @return: True upon success
1017 c = self.db_con.cursor()
1019 c.execute("BEGIN WORK")
1022 for fullpath in fullpaths:
1023 (path, file) = os.path.split(fullpath)
1025 if path.startswith( "./" ):
1027 # Get the necessary IDs ...
1028 file_id = self.get_or_set_contents_file_id(file)
1029 path_id = self.get_or_set_contents_path_id(path)
1031 c.execute("""INSERT INTO content_associations
1032 (binary_pkg, filepath, filename)
1033 VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )
1038 traceback.print_exc()
1039 c.execute("ROLLBACK")
1042 def insert_pending_content_paths(self, package, fullpaths):
1044 Make sure given paths are temporarily associated with given
1048 @param package: the package to associate with should have been read in from the binary control file
1049 @type fullpaths: list
1050 @param fullpaths: the list of paths of the file being associated with the binary
1052 @return: True upon success
1055 c = self.db_con.cursor()
1057 c.execute("BEGIN WORK")
1059 arch_id = self.get_architecture_id(package['Architecture'])
1061 # Remove any already existing recorded files for this package
1062 c.execute("""DELETE FROM pending_content_associations
1063 WHERE package=%(Package)s
1064 AND version=%(Version)s
1065 AND architecture=%(ArchID)s""", {'Package': package['Package'],
1066 'Version': package['Version'],
1069 for fullpath in fullpaths:
1070 (path, file) = os.path.split(fullpath)
1072 if path.startswith( "./" ):
1074 # Get the necessary IDs ...
1075 file_id = self.get_or_set_contents_file_id(file)
1076 path_id = self.get_or_set_contents_path_id(path)
1078 c.execute("""INSERT INTO pending_content_associations
1079 (package, version, architecture, filepath, filename)
1080 VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
1081 % (arch_id, path_id, file_id), package )
1086 traceback.print_exc()
1087 c.execute("ROLLBACK")