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
47 from textutils import fix_maintainer
49 ################################################################################
51 __all__ = ['IntegrityError', 'SQLAlchemyError']
53 ################################################################################
55 class Architecture(object):
56 def __init__(self, *args, **kwargs):
60 return '<Architecture %s>' % self.arch_string
62 __all__.append('Architecture')
64 def get_architecture(architecture, session=None):
66 Returns database id for given C{architecture}.
68 @type architecture: string
69 @param architecture: The name of the architecture
71 @type session: Session
72 @param session: Optional SQLA session object (a temporary one will be
73 generated if not supplied)
76 @return: Architecture object for the given arch (None if not present)
80 session = DBConn().session()
81 q = session.query(Architecture).filter_by(arch_string=architecture)
86 __all__.append('get_architecture')
88 def get_architecture_suites(architecture, session=None):
90 Returns list of Suite objects for given C{architecture} name
93 @param source: Architecture name to search for
95 @type session: Session
96 @param session: Optional SQL session object (a temporary one will be
97 generated if not supplied)
100 @return: list of Suite objects for the given name (may be empty)
104 session = DBConn().session()
106 q = session.query(Suite)
107 q = q.join(SuiteArchitecture)
108 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
111 __all__.append('get_architecture_suites')
113 ################################################################################
115 class Archive(object):
116 def __init__(self, *args, **kwargs):
120 return '<Archive %s>' % self.name
122 __all__.append('Archive')
124 def get_archive(archive, session=None):
126 returns database id for given c{archive}.
128 @type archive: string
129 @param archive: the name of the arhive
131 @type session: Session
132 @param session: Optional SQLA session object (a temporary one will be
133 generated if not supplied)
136 @return: Archive object for the given name (None if not present)
139 archive = archive.lower()
141 session = DBConn().session()
142 q = session.query(Archive).filter_by(archive_name=archive)
147 __all__.append('get_archive')
149 ################################################################################
151 class BinAssociation(object):
152 def __init__(self, *args, **kwargs):
156 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
158 __all__.append('BinAssociation')
160 ################################################################################
162 class DBBinary(object):
163 def __init__(self, *args, **kwargs):
167 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
169 __all__.append('DBBinary')
171 def get_binary_from_id(id, session=None):
173 Returns DBBinary object for given C{id}
176 @param id: Id of the required binary
178 @type session: Session
179 @param session: Optional SQLA session object (a temporary one will be
180 generated if not supplied)
183 @return: DBBinary object for the given binary (None if not present)
186 session = DBConn().session()
187 q = session.query(DBBinary).filter_by(binary_id=id)
192 __all__.append('get_binary_from_id')
194 def get_binaries_from_name(package, session=None):
196 Returns list of DBBinary objects for given C{package} name
199 @param package: DBBinary package name to search for
201 @type session: Session
202 @param session: Optional SQL session object (a temporary one will be
203 generated if not supplied)
206 @return: list of DBBinary objects for the given name (may be empty)
209 session = DBConn().session()
210 return session.query(DBBinary).filter_by(package=package).all()
212 __all__.append('get_binaries_from_name')
214 def get_binary_from_name_suite(package, suitename, session=None):
215 ### For dak examine-package
216 ### XXX: Doesn't use object API yet
218 session = DBConn().session()
220 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
221 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
222 WHERE b.package=:package
224 AND fi.location = l.id
225 AND l.component = c.id
228 AND su.suite_name=:suitename
229 ORDER BY b.version DESC"""
231 return session.execute(sql, {'package': package, 'suitename': suitename})
233 __all__.append('get_binary_from_name_suite')
235 def get_binary_components(package, suitename, arch, session=None):
236 # Check for packages that have moved from one component to another
237 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
238 WHERE b.package=:package AND s.suite_name=:suitename
239 AND (a.arch_string = :arch OR a.arch_string = 'all')
240 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
241 AND f.location = l.id
242 AND l.component = c.id
245 vals = {'package': package, 'suitename': suitename, 'arch': arch}
248 session = DBConn().session()
249 return session.execute(query, vals)
251 __all__.append('get_binary_components')
252 ################################################################################
254 class Component(object):
255 def __init__(self, *args, **kwargs):
259 return '<Component %s>' % self.component_name
262 __all__.append('Component')
264 def get_component(component, session=None):
266 Returns database id for given C{component}.
268 @type component: string
269 @param component: The name of the override type
272 @return: the database id for the given component
275 component = component.lower()
277 session = DBConn().session()
278 q = session.query(Component).filter_by(component_name=component)
283 __all__.append('get_component')
285 ################################################################################
287 class DBConfig(object):
288 def __init__(self, *args, **kwargs):
292 return '<DBConfig %s>' % self.name
294 __all__.append('DBConfig')
296 ################################################################################
298 class ContentFilename(object):
299 def __init__(self, *args, **kwargs):
303 return '<ContentFilename %s>' % self.filename
305 __all__.append('ContentFilename')
307 def get_or_set_contents_file_id(filename, session=None):
309 Returns database id for given filename.
311 If no matching file is found, a row is inserted.
313 @type filename: string
314 @param filename: The filename
315 @type session: SQLAlchemy
316 @param session: Optional SQL session object (a temporary one will be
317 generated if not supplied). If not passed, a commit will be performed at
318 the end of the function, otherwise the caller is responsible for commiting.
321 @return: the database id for the given component
325 session = DBConn().session()
329 q = session.query(ContentFilename).filter_by(filename=filename)
331 cf = ContentFilename()
332 cf.filename = filename
336 return cf.cafilename_id
338 return q.one().cafilename_id
341 traceback.print_exc()
344 __all__.append('get_or_set_contents_file_id')
346 def get_contents(suite, overridetype, section=None, session=None):
348 Returns contents for a suite / overridetype combination, limiting
349 to a section if not None.
352 @param suite: Suite object
354 @type overridetype: OverrideType
355 @param overridetype: OverrideType object
357 @type section: Section
358 @param section: Optional section object to limit results to
360 @type session: SQLAlchemy
361 @param session: Optional SQL session object (a temporary one will be
362 generated if not supplied)
365 @return: ResultsProxy object set up to return tuples of (filename, section,
370 session = DBConn().session()
372 # find me all of the contents for a given suite
373 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
377 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
378 JOIN content_file_names n ON (c.filename=n.id)
379 JOIN binaries b ON (b.id=c.binary_pkg)
380 JOIN override o ON (o.package=b.package)
381 JOIN section s ON (s.id=o.section)
382 WHERE o.suite = :suiteid AND o.type = :overridetypeid
383 AND b.type=:overridetypename"""
385 vals = {'suiteid': suite.suite_id,
386 'overridetypeid': overridetype.overridetype_id,
387 'overridetypename': overridetype.overridetype}
389 if section is not None:
390 contents_q += " AND s.id = :sectionid"
391 vals['sectionid'] = section.section_id
393 contents_q += " ORDER BY fn"
395 return session.execute(contents_q, vals)
397 __all__.append('get_contents')
399 ################################################################################
401 class ContentFilepath(object):
402 def __init__(self, *args, **kwargs):
406 return '<ContentFilepath %s>' % self.filepath
408 __all__.append('ContentFilepath')
410 def get_or_set_contents_path_id(filepath, session):
412 Returns database id for given path.
414 If no matching file is found, a row is inserted.
416 @type filename: string
417 @param filename: The filepath
418 @type session: SQLAlchemy
419 @param session: Optional SQL session object (a temporary one will be
420 generated if not supplied). If not passed, a commit will be performed at
421 the end of the function, otherwise the caller is responsible for commiting.
424 @return: the database id for the given path
428 session = DBConn().session()
432 q = session.query(ContentFilepath).filter_by(filepath=filepath)
434 cf = ContentFilepath()
435 cf.filepath = filepath
439 return cf.cafilepath_id
441 return q.one().cafilepath_id
444 traceback.print_exc()
447 __all__.append('get_or_set_contents_path_id')
449 ################################################################################
451 class ContentAssociation(object):
452 def __init__(self, *args, **kwargs):
456 return '<ContentAssociation %s>' % self.ca_id
458 __all__.append('ContentAssociation')
460 def insert_content_paths(binary_id, fullpaths, session=None):
462 Make sure given path is associated with given binary id
465 @param binary_id: the id of the binary
466 @type fullpaths: list
467 @param fullpaths: the list of paths of the file being associated with the binary
468 @type session: SQLAlchemy session
469 @param session: Optional SQLAlchemy session. If this is passed, the caller
470 is responsible for ensuring a transaction has begun and committing the
471 results or rolling back based on the result code. If not passed, a commit
472 will be performed at the end of the function, otherwise the caller is
473 responsible for commiting.
475 @return: True upon success
481 session = DBConn().session()
485 for fullpath in fullpaths:
486 (path, file) = os.path.split(fullpath)
488 # Get the necessary IDs ...
489 ca = ContentAssociation()
490 ca.binary_id = binary_id
491 ca.filename_id = get_or_set_contents_file_id(file)
492 ca.filepath_id = get_or_set_contents_path_id(path)
495 # Only commit if we set up the session ourself
501 traceback.print_exc()
503 # Only rollback if we set up the session ourself
509 __all__.append('insert_content_paths')
511 ################################################################################
513 class DSCFile(object):
514 def __init__(self, *args, **kwargs):
518 return '<DSCFile %s>' % self.dscfile_id
520 __all__.append('DSCFile')
522 ################################################################################
524 class PoolFile(object):
525 def __init__(self, *args, **kwargs):
529 return '<PoolFile %s>' % self.filename
531 __all__.append('PoolFile')
533 def get_poolfile_by_name(filename, location_id=None, session=None):
535 Returns an array of PoolFile objects for the given filename and
536 (optionally) location_id
538 @type filename: string
539 @param filename: the filename of the file to check against the DB
541 @type location_id: int
542 @param location_id: the id of the location to look in (optional)
545 @return: array of PoolFile objects
548 if session is not None:
549 session = DBConn().session()
551 q = session.query(PoolFile).filter_by(filename=filename)
553 if location_id is not None:
554 q = q.join(Location).filter_by(location_id=location_id)
558 __all__.append('get_poolfile_by_name')
560 ################################################################################
562 class Fingerprint(object):
563 def __init__(self, *args, **kwargs):
567 return '<Fingerprint %s>' % self.fingerprint
569 __all__.append('Fingerprint')
571 ################################################################################
573 class Keyring(object):
574 def __init__(self, *args, **kwargs):
578 return '<Keyring %s>' % self.keyring_name
580 __all__.append('Keyring')
582 ################################################################################
584 class Location(object):
585 def __init__(self, *args, **kwargs):
589 return '<Location %s (%s)>' % (self.path, self.location_id)
591 __all__.append('Location')
593 def get_location(location, component=None, archive=None, session=None):
595 Returns Location object for the given combination of location, component
598 @type location: string
599 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
601 @type component: string
602 @param component: the component name (if None, no restriction applied)
604 @type archive: string
605 @param archive_id: the archive name (if None, no restriction applied)
607 @rtype: Location / None
608 @return: Either a Location object or None if one can't be found
612 session = DBConn().session()
614 q = session.query(Location).filter_by(path=location)
616 if archive is not None:
617 q = q.join(Archive).filter_by(archive_name=archive)
619 if component is not None:
620 q = q.join(Component).filter_by(component_name=component)
627 __all__.append('get_location')
629 ################################################################################
631 class Maintainer(object):
632 def __init__(self, *args, **kwargs):
636 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
638 def get_split_maintainer(self):
639 if not hasattr(self, 'name') or self.name is None:
640 return ('', '', '', '')
642 return fix_maintainer(self.name.strip())
644 __all__.append('Maintainer')
646 ################################################################################
648 class Override(object):
649 def __init__(self, *args, **kwargs):
653 return '<Override %s (%s)>' % (self.package, self.suite_id)
655 __all__.append('Override')
657 ################################################################################
659 class OverrideType(object):
660 def __init__(self, *args, **kwargs):
664 return '<OverrideType %s>' % self.overridetype
666 __all__.append('OverrideType')
668 def get_override_type(override_type, session=None):
670 Returns OverrideType object for given C{override type}.
672 @type override_type: string
673 @param override_type: The name of the override type
675 @type session: Session
676 @param session: Optional SQLA session object (a temporary one will be
677 generated if not supplied)
680 @return: the database id for the given override type
684 session = DBConn().session()
685 q = session.query(OverrideType).filter_by(overridetype=override_type)
690 __all__.append('get_override_type')
692 ################################################################################
694 class PendingContentAssociation(object):
695 def __init__(self, *args, **kwargs):
699 return '<PendingContentAssociation %s>' % self.pca_id
701 __all__.append('PendingContentAssociation')
703 def insert_pending_content_paths(package, fullpaths, session=None):
705 Make sure given paths are temporarily associated with given
709 @param package: the package to associate with should have been read in from the binary control file
710 @type fullpaths: list
711 @param fullpaths: the list of paths of the file being associated with the binary
712 @type session: SQLAlchemy session
713 @param session: Optional SQLAlchemy session. If this is passed, the caller
714 is responsible for ensuring a transaction has begun and committing the
715 results or rolling back based on the result code. If not passed, a commit
716 will be performed at the end of the function
718 @return: True upon success, False if there is a problem
724 session = DBConn().session()
728 arch = get_architecture(package['Architecture'], session)
729 arch_id = arch.arch_id
731 # Remove any already existing recorded files for this package
732 q = session.query(PendingContentAssociation)
733 q = q.filter_by(package=package['Package'])
734 q = q.filter_by(version=package['Version'])
735 q = q.filter_by(architecture=arch_id)
739 for fullpath in fullpaths:
740 (path, file) = os.path.split(fullpath)
742 if path.startswith( "./" ):
745 pca = PendingContentAssociation()
746 pca.package = package['Package']
747 pca.version = package['Version']
748 pca.filename_id = get_or_set_contents_file_id(file, session)
749 pca.filepath_id = get_or_set_contents_path_id(path, session)
750 pca.architecture = arch_id
753 # Only commit if we set up the session ourself
759 traceback.print_exc()
761 # Only rollback if we set up the session ourself
767 __all__.append('insert_pending_content_paths')
769 ################################################################################
771 class Priority(object):
772 def __init__(self, *args, **kwargs):
776 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
778 __all__.append('Priority')
780 def get_priority(priority, session=None):
782 Returns Priority object for given C{priority name}.
784 @type priority: string
785 @param priority: The name of the priority
787 @type session: Session
788 @param session: Optional SQLA session object (a temporary one will be
789 generated if not supplied)
792 @return: Priority object for the given priority
796 session = DBConn().session()
797 q = session.query(Priority).filter_by(priority=priority)
802 __all__.append('get_priority')
804 ################################################################################
807 def __init__(self, *args, **kwargs):
811 return '<Queue %s>' % self.queue_name
813 __all__.append('Queue')
815 ################################################################################
817 class QueueBuild(object):
818 def __init__(self, *args, **kwargs):
822 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
824 __all__.append('QueueBuild')
826 ################################################################################
828 class Section(object):
829 def __init__(self, *args, **kwargs):
833 return '<Section %s>' % self.section
835 __all__.append('Section')
837 def get_section(section, session=None):
839 Returns Section object for given C{section name}.
841 @type section: string
842 @param section: The name of the section
844 @type session: Session
845 @param session: Optional SQLA session object (a temporary one will be
846 generated if not supplied)
849 @return: Section object for the given section name
853 session = DBConn().session()
854 q = session.query(Section).filter_by(section=section)
859 __all__.append('get_section')
861 ################################################################################
863 class DBSource(object):
864 def __init__(self, *args, **kwargs):
868 return '<DBSource %s (%s)>' % (self.source, self.version)
870 __all__.append('DBSource')
872 def get_sources_from_name(source, dm_upload_allowed=None, session=None):
874 Returns list of DBSource objects for given C{source} name
877 @param source: DBSource package name to search for
879 @type dm_upload_allowed: bool
880 @param dm_upload_allowed: If None, no effect. If True or False, only
881 return packages with that dm_upload_allowed setting
883 @type session: Session
884 @param session: Optional SQL session object (a temporary one will be
885 generated if not supplied)
888 @return: list of DBSource objects for the given name (may be empty)
891 session = DBConn().session()
893 q = session.query(DBSource).filter_by(source=source)
894 if dm_upload_allowed is not None:
895 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
899 __all__.append('get_sources_from_name')
901 def get_source_in_suite(source, suite, session=None):
903 Returns list of DBSource objects for a combination of C{source} and C{suite}.
905 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
906 - B{suite} - a suite name, eg. I{unstable}
909 @param source: source package name
912 @param suite: the suite name
915 @return: the version for I{source} in I{suite}
919 session = DBConn().session()
920 q = session.query(SrcAssociation)
921 q = q.join('source').filter_by(source=source)
922 q = q.join('suite').filter_by(suite_name=suite)
925 # ???: Maybe we should just return the SrcAssociation object instead
926 return q.one().source
928 __all__.append('get_source_in_suite')
930 ################################################################################
932 class SrcAssociation(object):
933 def __init__(self, *args, **kwargs):
937 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
939 __all__.append('SrcAssociation')
941 ################################################################################
943 class SrcUploader(object):
944 def __init__(self, *args, **kwargs):
948 return '<SrcUploader %s>' % self.uploader_id
950 __all__.append('SrcUploader')
952 ################################################################################
955 def __init__(self, *args, **kwargs):
959 return '<Suite %s>' % self.suite_name
961 __all__.append('Suite')
963 def get_suite_architecture(suite, architecture, session=None):
965 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
969 @param suite: Suite name to search for
971 @type architecture: str
972 @param architecture: Architecture name to search for
974 @type session: Session
975 @param session: Optional SQL session object (a temporary one will be
976 generated if not supplied)
978 @rtype: SuiteArchitecture
979 @return: the SuiteArchitecture object or None
983 session = DBConn().session()
985 q = session.query(SuiteArchitecture)
986 q = q.join(Architecture).filter_by(arch_string=architecture)
987 q = q.join(Suite).filter_by(suite_name=suite)
992 __all__.append('get_suite_architecture')
994 def get_suite(suite, session=None):
996 Returns Suite object for given C{suite name}.
999 @param suite: The name of the suite
1001 @type session: Session
1002 @param session: Optional SQLA session object (a temporary one will be
1003 generated if not supplied)
1006 @return: Suite object for the requested suite name (None if not presenT)
1010 session = DBConn().session()
1011 q = session.query(Suite).filter_by(suite_name=suite)
1016 __all__.append('get_suite')
1018 ################################################################################
1020 class SuiteArchitecture(object):
1021 def __init__(self, *args, **kwargs):
1025 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1027 __all__.append('SuiteArchitecture')
1029 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1031 Returns list of Architecture objects for given C{suite} name
1034 @param source: Suite name to search for
1036 @type skipsrc: boolean
1037 @param skipsrc: Whether to skip returning the 'source' architecture entry
1040 @type skipall: boolean
1041 @param skipall: Whether to skip returning the 'all' architecture entry
1044 @type session: Session
1045 @param session: Optional SQL session object (a temporary one will be
1046 generated if not supplied)
1049 @return: list of Architecture objects for the given name (may be empty)
1053 session = DBConn().session()
1055 q = session.query(Architecture)
1056 q = q.join(SuiteArchitecture)
1057 q = q.join(Suite).filter_by(suite_name=suite)
1059 q = q.filter(Architecture.arch_string != 'source')
1061 q = q.filter(Architecture.arch_string != 'all')
1062 q = q.order_by('arch_string')
1065 __all__.append('get_suite_architectures')
1067 ################################################################################
1070 def __init__(self, *args, **kwargs):
1074 return '<Uid %s (%s)>' % (self.uid, self.name)
1076 __all__.append('Uid')
1078 def add_database_user(uidname, session=None):
1080 Adds a database user
1082 @type uidname: string
1083 @param uidname: The uid of the user to add
1085 @type session: SQLAlchemy
1086 @param session: Optional SQL session object (a temporary one will be
1087 generated if not supplied). If not passed, a commit will be performed at
1088 the end of the function, otherwise the caller is responsible for commiting.
1091 @return: the uid object for the given uidname
1093 privatetrans = False
1095 session = DBConn().session()
1099 session.execute("CREATE USER :uid", {'uid': uidname})
1103 traceback.print_exc()
1106 __all__.append('add_database_user')
1108 def get_or_set_uid(uidname, session=None):
1110 Returns uid object for given uidname.
1112 If no matching uidname is found, a row is inserted.
1114 @type uidname: string
1115 @param uidname: The uid to add
1117 @type session: SQLAlchemy
1118 @param session: Optional SQL session object (a temporary one will be
1119 generated if not supplied). If not passed, a commit will be performed at
1120 the end of the function, otherwise the caller is responsible for commiting.
1123 @return: the uid object for the given uidname
1125 privatetrans = False
1127 session = DBConn().session()
1131 q = session.query(Uid).filter_by(uid=uidname)
1143 traceback.print_exc()
1146 __all__.append('get_or_set_uid')
1149 def get_uid_from_fingerprint(fpr, session=None):
1151 session = DBConn().session()
1153 q = session.query(Uid)
1154 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1161 __all__.append('get_uid_from_fingerprint')
1163 ################################################################################
1165 class DBConn(Singleton):
1167 database module init.
1169 def __init__(self, *args, **kwargs):
1170 super(DBConn, self).__init__(*args, **kwargs)
1172 def _startup(self, *args, **kwargs):
1174 if kwargs.has_key('debug'):
1178 def __setuptables(self):
1179 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1180 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1181 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1182 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1183 self.tbl_component = Table('component', self.db_meta, autoload=True)
1184 self.tbl_config = Table('config', self.db_meta, autoload=True)
1185 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1186 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1187 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1188 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1189 self.tbl_files = Table('files', self.db_meta, autoload=True)
1190 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1191 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1192 self.tbl_location = Table('location', self.db_meta, autoload=True)
1193 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
1194 self.tbl_override = Table('override', self.db_meta, autoload=True)
1195 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
1196 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
1197 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
1198 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
1199 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
1200 self.tbl_section = Table('section', self.db_meta, autoload=True)
1201 self.tbl_source = Table('source', self.db_meta, autoload=True)
1202 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
1203 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
1204 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
1205 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
1206 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
1208 def __setupmappers(self):
1209 mapper(Architecture, self.tbl_architecture,
1210 properties = dict(arch_id = self.tbl_architecture.c.id))
1212 mapper(Archive, self.tbl_archive,
1213 properties = dict(archive_id = self.tbl_archive.c.id,
1214 archive_name = self.tbl_archive.c.name))
1216 mapper(BinAssociation, self.tbl_bin_associations,
1217 properties = dict(ba_id = self.tbl_bin_associations.c.id,
1218 suite_id = self.tbl_bin_associations.c.suite,
1219 suite = relation(Suite),
1220 binary_id = self.tbl_bin_associations.c.bin,
1221 binary = relation(DBBinary)))
1223 mapper(DBBinary, self.tbl_binaries,
1224 properties = dict(binary_id = self.tbl_binaries.c.id,
1225 package = self.tbl_binaries.c.package,
1226 version = self.tbl_binaries.c.version,
1227 maintainer_id = self.tbl_binaries.c.maintainer,
1228 maintainer = relation(Maintainer),
1229 source_id = self.tbl_binaries.c.source,
1230 source = relation(DBSource),
1231 arch_id = self.tbl_binaries.c.architecture,
1232 architecture = relation(Architecture),
1233 poolfile_id = self.tbl_binaries.c.file,
1234 poolfile = relation(PoolFile),
1235 binarytype = self.tbl_binaries.c.type,
1236 fingerprint_id = self.tbl_binaries.c.sig_fpr,
1237 fingerprint = relation(Fingerprint),
1238 install_date = self.tbl_binaries.c.install_date,
1239 binassociations = relation(BinAssociation,
1240 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
1242 mapper(Component, self.tbl_component,
1243 properties = dict(component_id = self.tbl_component.c.id,
1244 component_name = self.tbl_component.c.name))
1246 mapper(DBConfig, self.tbl_config,
1247 properties = dict(config_id = self.tbl_config.c.id))
1249 mapper(ContentAssociation, self.tbl_content_associations,
1250 properties = dict(ca_id = self.tbl_content_associations.c.id,
1251 filename_id = self.tbl_content_associations.c.filename,
1252 filename = relation(ContentFilename),
1253 filepath_id = self.tbl_content_associations.c.filepath,
1254 filepath = relation(ContentFilepath),
1255 binary_id = self.tbl_content_associations.c.binary_pkg,
1256 binary = relation(DBBinary)))
1259 mapper(ContentFilename, self.tbl_content_file_names,
1260 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
1261 filename = self.tbl_content_file_names.c.file))
1263 mapper(ContentFilepath, self.tbl_content_file_paths,
1264 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
1265 filepath = self.tbl_content_file_paths.c.path))
1267 mapper(DSCFile, self.tbl_dsc_files,
1268 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
1269 source_id = self.tbl_dsc_files.c.source,
1270 source = relation(DBSource),
1271 poolfile_id = self.tbl_dsc_files.c.file,
1272 poolfile = relation(PoolFile)))
1274 mapper(PoolFile, self.tbl_files,
1275 properties = dict(file_id = self.tbl_files.c.id,
1276 filesize = self.tbl_files.c.size,
1277 location_id = self.tbl_files.c.location,
1278 location = relation(Location)))
1280 mapper(Fingerprint, self.tbl_fingerprint,
1281 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
1282 uid_id = self.tbl_fingerprint.c.uid,
1283 uid = relation(Uid),
1284 keyring_id = self.tbl_fingerprint.c.keyring,
1285 keyring = relation(Keyring)))
1287 mapper(Keyring, self.tbl_keyrings,
1288 properties = dict(keyring_name = self.tbl_keyrings.c.name,
1289 keyring_id = self.tbl_keyrings.c.id))
1291 mapper(Location, self.tbl_location,
1292 properties = dict(location_id = self.tbl_location.c.id,
1293 component_id = self.tbl_location.c.component,
1294 component = relation(Component),
1295 archive_id = self.tbl_location.c.archive,
1296 archive = relation(Archive),
1297 archive_type = self.tbl_location.c.type))
1299 mapper(Maintainer, self.tbl_maintainer,
1300 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
1302 mapper(Override, self.tbl_override,
1303 properties = dict(suite_id = self.tbl_override.c.suite,
1304 suite = relation(Suite),
1305 component_id = self.tbl_override.c.component,
1306 component = relation(Component),
1307 priority_id = self.tbl_override.c.priority,
1308 priority = relation(Priority),
1309 section_id = self.tbl_override.c.section,
1310 section = relation(Section),
1311 overridetype_id = self.tbl_override.c.type,
1312 overridetype = relation(OverrideType)))
1314 mapper(OverrideType, self.tbl_override_type,
1315 properties = dict(overridetype = self.tbl_override_type.c.type,
1316 overridetype_id = self.tbl_override_type.c.id))
1318 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
1319 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
1320 filepath_id = self.tbl_pending_content_associations.c.filepath,
1321 filepath = relation(ContentFilepath),
1322 filename_id = self.tbl_pending_content_associations.c.filename,
1323 filename = relation(ContentFilename)))
1325 mapper(Priority, self.tbl_priority,
1326 properties = dict(priority_id = self.tbl_priority.c.id))
1328 mapper(Queue, self.tbl_queue,
1329 properties = dict(queue_id = self.tbl_queue.c.id))
1331 mapper(QueueBuild, self.tbl_queue_build,
1332 properties = dict(suite_id = self.tbl_queue_build.c.suite,
1333 queue_id = self.tbl_queue_build.c.queue,
1334 queue = relation(Queue)))
1336 mapper(Section, self.tbl_section,
1337 properties = dict(section_id = self.tbl_section.c.id))
1339 mapper(DBSource, self.tbl_source,
1340 properties = dict(source_id = self.tbl_source.c.id,
1341 version = self.tbl_source.c.version,
1342 maintainer_id = self.tbl_source.c.maintainer,
1343 maintainer = relation(Maintainer,
1344 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
1345 poolfile_id = self.tbl_source.c.file,
1346 poolfile = relation(PoolFile),
1347 fingerprint_id = self.tbl_source.c.sig_fpr,
1348 fingerprint = relation(Fingerprint),
1349 changedby_id = self.tbl_source.c.changedby,
1350 changedby = relation(Maintainer,
1351 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
1352 srcfiles = relation(DSCFile,
1353 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
1354 srcassociations = relation(SrcAssociation,
1355 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
1357 mapper(SrcAssociation, self.tbl_src_associations,
1358 properties = dict(sa_id = self.tbl_src_associations.c.id,
1359 suite_id = self.tbl_src_associations.c.suite,
1360 suite = relation(Suite),
1361 source_id = self.tbl_src_associations.c.source,
1362 source = relation(DBSource)))
1364 mapper(SrcUploader, self.tbl_src_uploaders,
1365 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
1366 source_id = self.tbl_src_uploaders.c.source,
1367 source = relation(DBSource,
1368 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
1369 maintainer_id = self.tbl_src_uploaders.c.maintainer,
1370 maintainer = relation(Maintainer,
1371 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
1373 mapper(Suite, self.tbl_suite,
1374 properties = dict(suite_id = self.tbl_suite.c.id))
1376 mapper(SuiteArchitecture, self.tbl_suite_architectures,
1377 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
1378 suite = relation(Suite, backref='suitearchitectures'),
1379 arch_id = self.tbl_suite_architectures.c.architecture,
1380 architecture = relation(Architecture)))
1382 mapper(Uid, self.tbl_uid,
1383 properties = dict(uid_id = self.tbl_uid.c.id,
1384 fingerprint = relation(Fingerprint)))
1386 ## Connection functions
1387 def __createconn(self):
1388 from config import Config
1392 connstr = "postgres://%s" % cnf["DB::Host"]
1393 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1394 connstr += ":%s" % cnf["DB::Port"]
1395 connstr += "/%s" % cnf["DB::Name"]
1398 connstr = "postgres:///%s" % cnf["DB::Name"]
1399 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1400 connstr += "?port=%s" % cnf["DB::Port"]
1402 self.db_pg = create_engine(connstr, echo=self.debug)
1403 self.db_meta = MetaData()
1404 self.db_meta.bind = self.db_pg
1405 self.db_smaker = sessionmaker(bind=self.db_pg,
1409 self.__setuptables()
1410 self.__setupmappers()
1413 return self.db_smaker()
1415 __all__.append('DBConn')