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, relation
43 # Don't remove this, we re-export the exceptions to scripts which import us
44 from sqlalchemy.exc import *
46 from singleton import Singleton
48 ################################################################################
52 ################################################################################
54 class Architecture(object):
55 def __init__(self, *args, **kwargs):
59 return '<Architecture %s>' % self.arch_string
61 __all__.append('Architecture')
63 def get_architecture(architecture, session=None):
65 Returns database id for given C{architecture}.
67 @type architecture: string
68 @param architecture: The name of the architecture
70 @type session: Session
71 @param session: Optional SQLA session object (a temporary one will be
72 generated if not supplied)
75 @return: Architecture object for the given arch (None if not present)
79 session = DBConn().session()
80 q = session.query(Architecture).filter_by(arch_string=architecture)
85 __all__.append('get_architecture')
87 def get_architecture_suites(architecture, session=None):
89 Returns list of Suite objects for given C{architecture} name
92 @param source: Architecture name to search for
94 @type session: Session
95 @param session: Optional SQL session object (a temporary one will be
96 generated if not supplied)
99 @return: list of Suite objects for the given name (may be empty)
103 session = DBConn().session()
105 q = session.query(Suite)
106 q = q.join(SuiteArchitecture)
107 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
110 __all__.append('get_architecture_suites')
112 class Archive(object):
113 def __init__(self, *args, **kwargs):
117 return '<Archive %s>' % self.name
119 __all__.append('Archive')
121 def get_archive(archive, session=None):
123 returns database id for given c{archive}.
125 @type archive: string
126 @param archive: the name of the arhive
128 @type session: Session
129 @param session: Optional SQLA session object (a temporary one will be
130 generated if not supplied)
133 @return: Archive object for the given name (None if not present)
136 archive = archive.lower()
138 session = DBConn().session()
139 q = session.query(Archive).filter_by(archive_name=archive)
144 __all__.append('get_archive')
146 class BinAssociation(object):
147 def __init__(self, *args, **kwargs):
151 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
153 __all__.append('BinAssociation')
155 class Binary(object):
156 def __init__(self, *args, **kwargs):
160 return '<Binary %s (%s, %s)>' % (self.package, self.version, self.architecture)
162 __all__.append('Binary')
164 def get_binary_from_id(id, session=None):
166 Returns Binary object for given C{id}
169 @param id: Id of the required binary
171 @type session: Session
172 @param session: Optional SQLA session object (a temporary one will be
173 generated if not supplied)
176 @return: Binary object for the given binary (None if not present)
179 session = DBConn().session()
180 q = session.query(Binary).filter_by(binary_id=id)
185 __all__.append('get_binary_from_id')
187 def get_binaries_from_name(package, session=None):
189 Returns list of Binary objects for given C{package} name
192 @param package: Binary package name to search for
194 @type session: Session
195 @param session: Optional SQL session object (a temporary one will be
196 generated if not supplied)
199 @return: list of Binary objects for the given name (may be empty)
202 session = DBConn().session()
203 return session.query(Binary).filter_by(package=package).all()
205 __all__.append('get_binaries_from_name')
207 class Component(object):
208 def __init__(self, *args, **kwargs):
212 return '<Component %s>' % self.component_name
215 __all__.append('Component')
217 def get_component(component, session=None):
219 Returns database id for given C{component}.
221 @type component: string
222 @param component: The name of the override type
225 @return: the database id for the given component
228 component = component.lower()
230 session = DBConn().session()
231 q = session.query(Component).filter_by(component_name=component)
236 __all__.append('get_component')
238 class DBConfig(object):
239 def __init__(self, *args, **kwargs):
243 return '<DBConfig %s>' % self.name
245 __all__.append('DBConfig')
247 class ContentFilename(object):
248 def __init__(self, *args, **kwargs):
252 return '<ContentFilename %s>' % self.filename
254 __all__.append('ContentFilename')
256 class ContentFilepath(object):
257 def __init__(self, *args, **kwargs):
261 return '<ContentFilepath %s>' % self.filepath
263 __all__.append('ContentFilepath')
265 class ContentAssociation(object):
266 def __init__(self, *args, **kwargs):
270 return '<ContentAssociation %s>' % self.ca_id
272 __all__.append('ContentAssociation')
274 class DSCFile(object):
275 def __init__(self, *args, **kwargs):
279 return '<DSCFile %s>' % self.dscfile_id
281 __all__.append('DSCFile')
283 class PoolFile(object):
284 def __init__(self, *args, **kwargs):
288 return '<PoolFile %s>' % self.filename
290 __all__.append('PoolFile')
292 class Fingerprint(object):
293 def __init__(self, *args, **kwargs):
297 return '<Fingerprint %s>' % self.fingerprint
299 __all__.append('Fingerprint')
301 class Keyring(object):
302 def __init__(self, *args, **kwargs):
306 return '<Keyring %s>' % self.keyring_name
308 __all__.append('Keyring')
310 class Location(object):
311 def __init__(self, *args, **kwargs):
315 return '<Location %s (%s)>' % (self.path, self.location_id)
317 __all__.append('Location')
319 class Maintainer(object):
320 def __init__(self, *args, **kwargs):
324 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
326 __all__.append('Maintainer')
328 class Override(object):
329 def __init__(self, *args, **kwargs):
333 return '<Override %s (%s)>' % (self.package, self.suite_id)
335 __all__.append('Override')
337 class OverrideType(object):
338 def __init__(self, *args, **kwargs):
342 return '<OverrideType %s>' % self.overridetype
344 __all__.append('OverrideType')
346 def get_override_type(override_type, session=None):
348 Returns OverrideType object for given C{override type}.
350 @type override_type: string
351 @param override_type: The name of the override type
353 @type session: Session
354 @param session: Optional SQLA session object (a temporary one will be
355 generated if not supplied)
358 @return: the database id for the given override type
362 session = DBConn().session()
363 q = session.query(Priority).filter_by(priority=priority)
368 __all__.append('get_override_type')
370 class PendingContentAssociation(object):
371 def __init__(self, *args, **kwargs):
375 return '<PendingContentAssociation %s>' % self.pca_id
377 __all__.append('PendingContentAssociation')
379 class Priority(object):
380 def __init__(self, *args, **kwargs):
384 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
386 __all__.append('Priority')
388 def get_priority(priority, session=None):
390 Returns Priority object for given C{priority name}.
392 @type priority: string
393 @param priority: The name of the priority
395 @type session: Session
396 @param session: Optional SQLA session object (a temporary one will be
397 generated if not supplied)
400 @return: Priority object for the given priority
404 session = DBConn().session()
405 q = session.query(Priority).filter_by(priority=priority)
410 __all__.append('get_priority')
413 def __init__(self, *args, **kwargs):
417 return '<Queue %s>' % self.queue_name
419 __all__.append('Queue')
421 class QueueBuild(object):
422 def __init__(self, *args, **kwargs):
426 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
428 __all__.append('QueueBuild')
430 class Section(object):
431 def __init__(self, *args, **kwargs):
435 return '<Section %s>' % self.section
437 __all__.append('Section')
439 def get_section(section, session=None):
441 Returns Section object for given C{section name}.
443 @type section: string
444 @param section: The name of the section
446 @type session: Session
447 @param session: Optional SQLA session object (a temporary one will be
448 generated if not supplied)
451 @return: Section object for the given section name
455 session = DBConn().session()
456 q = session.query(Section).filter_by(section=section)
461 __all__.append('get_section')
463 class Source(object):
464 def __init__(self, *args, **kwargs):
468 return '<Source %s (%s)>' % (self.source, self.version)
470 __all__.append('Source')
472 def get_sources_from_name(source, session=None):
474 Returns list of Source objects for given C{source} name
477 @param source: Source package name to search for
479 @type session: Session
480 @param session: Optional SQL session object (a temporary one will be
481 generated if not supplied)
484 @return: list of Source objects for the given name (may be empty)
487 session = DBConn().session()
488 return session.query(Source).filter_by(source=source).all()
490 __all__.append('get_sources_from_name')
492 def get_source_in_suite(source, suite, session=None):
494 Returns list of Source objects for a combination of C{source} and C{suite}.
496 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
497 - B{suite} - a suite name, eg. I{unstable}
500 @param source: source package name
503 @param suite: the suite name
506 @return: the version for I{source} in I{suite}
510 session = DBConn().session()
511 q = session.query(SrcAssociation)
512 q = q.join('source').filter_by(source=source)
513 q = q.join('suite').filter_by(suite_name=suite)
516 # ???: Maybe we should just return the SrcAssociation object instead
517 return q.one().source
519 __all__.append('get_source_in_suite')
521 class SrcAssociation(object):
522 def __init__(self, *args, **kwargs):
526 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
528 __all__.append('SrcAssociation')
530 class SrcUploader(object):
531 def __init__(self, *args, **kwargs):
535 return '<SrcUploader %s>' % self.uploader_id
537 __all__.append('SrcUploader')
540 def __init__(self, *args, **kwargs):
544 return '<Suite %s>' % self.suite_name
546 __all__.append('Suite')
548 def get_suite_architecture(suite, architecture, session=None):
550 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
554 @param suite: Suite name to search for
556 @type architecture: str
557 @param architecture: Architecture name to search for
559 @type session: Session
560 @param session: Optional SQL session object (a temporary one will be
561 generated if not supplied)
563 @rtype: SuiteArchitecture
564 @return: the SuiteArchitecture object or None
568 session = DBConn().session()
570 q = session.query(SuiteArchitecture)
571 q = q.join(Architecture).filter_by(arch_string=architecture)
572 q = q.join(Suite).filter_by(suite_name=suite)
577 __all__.append('get_suite_architecture')
579 def get_suite(suite, session=None):
581 Returns Suite object for given C{suite name}.
584 @param suite: The name of the suite
586 @type session: Session
587 @param session: Optional SQLA session object (a temporary one will be
588 generated if not supplied)
591 @return: Suite object for the requested suite name (None if not presenT)
595 session = DBConn().session()
596 q = session.query(Suite).filter_by(suite_name=suite)
601 __all__.append('get_suite')
603 class SuiteArchitecture(object):
604 def __init__(self, *args, **kwargs):
608 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
610 __all__.append('SuiteArchitecture')
612 def get_suite_architectures(suite, session=None):
614 Returns list of Architecture objects for given C{suite} name
617 @param source: Suite name to search for
619 @type session: Session
620 @param session: Optional SQL session object (a temporary one will be
621 generated if not supplied)
624 @return: list of Architecture objects for the given name (may be empty)
628 session = DBConn().session()
630 q = session.query(Architecture)
631 q = q.join(SuiteArchitecture)
632 q = q.join(Suite).filter_by(suite_name=suite).order_by('arch_string')
635 __all__.append('get_suite_architectures')
638 def __init__(self, *args, **kwargs):
642 return '<Uid %s (%s)>' % (self.uid, self.name)
644 __all__.append('Uid')
646 ################################################################################
648 class DBConn(Singleton):
650 database module init.
652 def __init__(self, *args, **kwargs):
653 super(DBConn, self).__init__(*args, **kwargs)
655 def _startup(self, *args, **kwargs):
657 if kwargs.has_key('debug'):
661 def __setuptables(self):
662 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
663 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
664 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
665 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
666 self.tbl_component = Table('component', self.db_meta, autoload=True)
667 self.tbl_config = Table('config', self.db_meta, autoload=True)
668 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
669 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
670 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
671 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
672 self.tbl_files = Table('files', self.db_meta, autoload=True)
673 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
674 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
675 self.tbl_location = Table('location', self.db_meta, autoload=True)
676 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
677 self.tbl_override = Table('override', self.db_meta, autoload=True)
678 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
679 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
680 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
681 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
682 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
683 self.tbl_section = Table('section', self.db_meta, autoload=True)
684 self.tbl_source = Table('source', self.db_meta, autoload=True)
685 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
686 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
687 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
688 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
689 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
691 def __setupmappers(self):
692 mapper(Architecture, self.tbl_architecture,
693 properties = dict(arch_id = self.tbl_architecture.c.id))
695 mapper(Archive, self.tbl_archive,
696 properties = dict(archive_id = self.tbl_archive.c.id,
697 archive_name = self.tbl_archive.c.name))
699 mapper(BinAssociation, self.tbl_bin_associations,
700 properties = dict(ba_id = self.tbl_bin_associations.c.id,
701 suite_id = self.tbl_bin_associations.c.suite,
702 suite = relation(Suite),
703 binary_id = self.tbl_bin_associations.c.bin,
704 binary = relation(Binary)))
706 mapper(Binary, self.tbl_binaries,
707 properties = dict(binary_id = self.tbl_binaries.c.id,
708 package = self.tbl_binaries.c.package,
709 version = self.tbl_binaries.c.version,
710 maintainer_id = self.tbl_binaries.c.maintainer,
711 maintainer = relation(Maintainer),
712 source_id = self.tbl_binaries.c.source,
713 source = relation(Source),
714 arch_id = self.tbl_binaries.c.architecture,
715 architecture = relation(Architecture),
716 poolfile_id = self.tbl_binaries.c.file,
717 poolfile = relation(PoolFile),
718 binarytype = self.tbl_binaries.c.type,
719 fingerprint_id = self.tbl_binaries.c.sig_fpr,
720 fingerprint = relation(Fingerprint),
721 install_date = self.tbl_binaries.c.install_date,
722 binassociations = relation(BinAssociation,
723 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
725 mapper(Component, self.tbl_component,
726 properties = dict(component_id = self.tbl_component.c.id,
727 component_name = self.tbl_component.c.name))
729 mapper(DBConfig, self.tbl_config,
730 properties = dict(config_id = self.tbl_config.c.id))
732 mapper(ContentAssociation, self.tbl_content_associations,
733 properties = dict(ca_id = self.tbl_content_associations.c.id,
734 filename_id = self.tbl_content_associations.c.filename,
735 filename = relation(ContentFilename),
736 filepath_id = self.tbl_content_associations.c.filepath,
737 filepath = relation(ContentFilepath),
738 binary_id = self.tbl_content_associations.c.binary_pkg,
739 binary = relation(Binary)))
742 mapper(ContentFilename, self.tbl_content_file_names,
743 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
744 filename = self.tbl_content_file_names.c.file))
746 mapper(ContentFilepath, self.tbl_content_file_paths,
747 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
748 filepath = self.tbl_content_file_paths.c.path))
750 mapper(DSCFile, self.tbl_dsc_files,
751 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
752 source_id = self.tbl_dsc_files.c.source,
753 source = relation(Source),
754 poolfile_id = self.tbl_dsc_files.c.file,
755 poolfile = relation(PoolFile)))
757 mapper(PoolFile, self.tbl_files,
758 properties = dict(file_id = self.tbl_files.c.id,
759 filesize = self.tbl_files.c.size,
760 location_id = self.tbl_files.c.location,
761 location = relation(Location)))
763 mapper(Fingerprint, self.tbl_fingerprint,
764 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
765 uid_id = self.tbl_fingerprint.c.uid,
767 keyring_id = self.tbl_fingerprint.c.keyring,
768 keyring = relation(Keyring)))
770 mapper(Keyring, self.tbl_keyrings,
771 properties = dict(keyring_name = self.tbl_keyrings.c.name,
772 keyring_id = self.tbl_keyrings.c.id))
774 mapper(Location, self.tbl_location,
775 properties = dict(location_id = self.tbl_location.c.id,
776 component_id = self.tbl_location.c.component,
777 component = relation(Component),
778 archive_id = self.tbl_location.c.archive,
779 archive = relation(Archive),
780 archive_type = self.tbl_location.c.type))
782 mapper(Maintainer, self.tbl_maintainer,
783 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
785 mapper(Override, self.tbl_override,
786 properties = dict(suite_id = self.tbl_override.c.suite,
787 suite = relation(Suite),
788 component_id = self.tbl_override.c.component,
789 component = relation(Component),
790 priority_id = self.tbl_override.c.priority,
791 priority = relation(Priority),
792 section_id = self.tbl_override.c.section,
793 section = relation(Section),
794 overridetype_id = self.tbl_override.c.type,
795 overridetype = relation(OverrideType)))
797 mapper(OverrideType, self.tbl_override_type,
798 properties = dict(overridetype = self.tbl_override_type.c.type,
799 overridetype_id = self.tbl_override_type.c.id))
801 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
802 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
803 filepath_id = self.tbl_pending_content_associations.c.filepath,
804 filepath = relation(ContentFilepath),
805 filename_id = self.tbl_pending_content_associations.c.filename,
806 filename = relation(ContentFilename)))
808 mapper(Priority, self.tbl_priority,
809 properties = dict(priority_id = self.tbl_priority.c.id))
811 mapper(Queue, self.tbl_queue,
812 properties = dict(queue_id = self.tbl_queue.c.id))
814 mapper(QueueBuild, self.tbl_queue_build,
815 properties = dict(suite_id = self.tbl_queue_build.c.suite,
816 queue_id = self.tbl_queue_build.c.queue,
817 queue = relation(Queue)))
819 mapper(Section, self.tbl_section,
820 properties = dict(section_id = self.tbl_section.c.id))
822 mapper(Source, self.tbl_source,
823 properties = dict(source_id = self.tbl_source.c.id,
824 version = self.tbl_source.c.version,
825 maintainer_id = self.tbl_source.c.maintainer,
826 maintainer = relation(Maintainer,
827 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
828 poolfile_id = self.tbl_source.c.file,
829 poolfile = relation(PoolFile),
830 fingerprint_id = self.tbl_source.c.sig_fpr,
831 fingerprint = relation(Fingerprint),
832 changedby_id = self.tbl_source.c.changedby,
833 changedby = relation(Maintainer,
834 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
835 srcfiles = relation(DSCFile,
836 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
837 srcassociations = relation(SrcAssociation,
838 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
840 mapper(SrcAssociation, self.tbl_src_associations,
841 properties = dict(sa_id = self.tbl_src_associations.c.id,
842 suite_id = self.tbl_src_associations.c.suite,
843 suite = relation(Suite),
844 source_id = self.tbl_src_associations.c.source,
845 source = relation(Source)))
847 mapper(SrcUploader, self.tbl_src_uploaders,
848 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
849 source_id = self.tbl_src_uploaders.c.source,
850 source = relation(Source,
851 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
852 maintainer_id = self.tbl_src_uploaders.c.maintainer,
853 maintainer = relation(Maintainer,
854 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
856 mapper(Suite, self.tbl_suite,
857 properties = dict(suite_id = self.tbl_suite.c.id))
859 mapper(SuiteArchitecture, self.tbl_suite_architectures,
860 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
861 suite = relation(Suite),
862 arch_id = self.tbl_suite_architectures.c.architecture,
863 architecture = relation(Architecture)))
865 mapper(Uid, self.tbl_uid,
866 properties = dict(uid_id = self.tbl_uid.c.id))
868 ## Connection functions
869 def __createconn(self):
870 from config import Config
874 connstr = "postgres://%s" % cnf["DB::Host"]
875 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
876 connstr += ":%s" % cnf["DB::Port"]
877 connstr += "/%s" % cnf["DB::Name"]
880 connstr = "postgres:///%s" % cnf["DB::Name"]
881 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
882 connstr += "?port=%s" % cnf["DB::Port"]
884 self.db_pg = create_engine(connstr, echo=self.debug)
885 self.db_meta = MetaData()
886 self.db_meta.bind = self.db_pg
887 self.db_smaker = sessionmaker(bind=self.db_pg,
892 self.__setupmappers()
895 return self.db_smaker()
897 def prepare(self,name,statement):
898 if not self.prepared_statements.has_key(name):
899 pgc.execute(statement)
900 self.prepared_statements[name] = statement
903 def get_location_id(self, location, component, archive):
905 Returns database id for the location behind the given combination of
906 - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
907 - B{component} - the id of the component as returned by L{get_component_id}
908 - B{archive} - the id of the archive as returned by L{get_archive_id}
909 Results are kept in a cache during runtime to minimize database queries.
911 @type location: string
912 @param location: the path of the location
915 @param component: the id of the component
918 @param archive: the id of the archive
921 @return: the database id for the location
925 archive_id = self.get_archive_id(archive)
933 component_id = self.get_component_id(component)
935 res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND component=%(component)s AND archive=%(archive)s",
936 {'location': location,
937 'archive': int(archive_id),
938 'component': component_id}, cachename='location')
940 res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND archive=%(archive)d",
941 {'location': location, 'archive': archive_id, 'component': ''}, cachename='location')
947 def get_files_id (self, filename, size, md5sum, location_id):
949 Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
952 The database is queried using the C{filename} and C{location_id}. If a file does exist
953 at that location, the existing size and md5sum are checked against the provided
954 parameters. A size or checksum mismatch returns -2. If more than one entry is
955 found within the database, a -1 is returned, no result returns None, otherwise
958 @type filename: string
959 @param filename: the filename of the file to check against the DB
962 @param size: the size of the file to check against the DB
965 @param md5sum: the md5sum of the file to check against the DB
967 @type location_id: int
968 @param location_id: the id of the location as returned by L{get_location_id}
971 @return: Various return values are possible:
972 - -2: size/checksum error
973 - -1: more than one file found in database
974 - None: no file found in database
978 values = {'filename' : filename,
979 'location' : location_id}
982 query = """SELECT id, size, md5sum
984 WHERE filename = %(filename)s AND location = %(location)s"""
986 cursor = self.db_con.cursor()
987 cursor.execute( query, values )
989 if cursor.rowcount == 0:
992 elif cursor.rowcount != 1:
996 row = cursor.fetchone()
998 if row[1] != int(size) or row[2] != md5sum:
1007 def get_or_set_contents_file_id(self, filename):
1009 Returns database id for given filename.
1011 If no matching file is found, a row is inserted.
1013 @type filename: string
1014 @param filename: The filename
1017 @return: the database id for the given component
1020 values={'value': filename}
1021 query = "SELECT id FROM content_file_names WHERE file = %(value)s"
1023 c = self.db_con.cursor()
1024 c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id",
1027 id = c.fetchone()[0]
1031 traceback.print_exc()
1034 def get_or_set_contents_path_id(self, path):
1036 Returns database id for given path.
1038 If no matching file is found, a row is inserted.
1041 @param path: The filename
1044 @return: the database id for the given component
1047 values={'value': path}
1048 query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
1050 c = self.db_con.cursor()
1051 c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id",
1054 id = c.fetchone()[0]
1058 traceback.print_exc()
1062 def insert_content_paths(self, bin_id, fullpaths):
1064 Make sure given path is associated with given binary id
1067 @param bin_id: the id of the binary
1068 @type fullpaths: list
1069 @param fullpaths: the list of paths of the file being associated with the binary
1071 @return: True upon success
1074 c = self.db_con.cursor()
1076 c.execute("BEGIN WORK")
1079 for fullpath in fullpaths:
1080 (path, file) = os.path.split(fullpath)
1082 # Get the necessary IDs ...
1083 file_id = self.get_or_set_contents_file_id(file)
1084 path_id = self.get_or_set_contents_path_id(path)
1086 c.execute("""INSERT INTO content_associations
1087 (binary_pkg, filepath, filename)
1088 VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )
1093 traceback.print_exc()
1094 c.execute("ROLLBACK")
1097 def insert_pending_content_paths(self, package, fullpaths):
1099 Make sure given paths are temporarily associated with given
1103 @param package: the package to associate with should have been read in from the binary control file
1104 @type fullpaths: list
1105 @param fullpaths: the list of paths of the file being associated with the binary
1107 @return: True upon success
1110 c = self.db_con.cursor()
1112 c.execute("BEGIN WORK")
1114 arch_id = self.get_architecture_id(package['Architecture'])
1116 # Remove any already existing recorded files for this package
1117 c.execute("""DELETE FROM pending_content_associations
1118 WHERE package=%(Package)s
1119 AND version=%(Version)s
1120 AND architecture=%(ArchID)s""", {'Package': package['Package'],
1121 'Version': package['Version'],
1124 for fullpath in fullpaths:
1125 (path, file) = os.path.split(fullpath)
1127 if path.startswith( "./" ):
1129 # Get the necessary IDs ...
1130 file_id = self.get_or_set_contents_file_id(file)
1131 path_id = self.get_or_set_contents_path_id(path)
1133 c.execute("""INSERT INTO pending_content_associations
1134 (package, version, architecture, filepath, filename)
1135 VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
1136 % (arch_id, path_id, file_id), package )
1141 traceback.print_exc()
1142 c.execute("ROLLBACK")