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 ################################################################################
954 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
955 ('SuiteID', 'suite_id'),
956 ('Version', 'version'),
957 ('Origin', 'origin'),
959 ('Description', 'description'),
960 ('Untouchable', 'untouchable'),
961 ('Announce', 'announce'),
962 ('Codename', 'codename'),
963 ('OverrideCodename', 'overridecodename'),
964 ('ValidTime', 'validtime'),
965 ('Priority', 'priority'),
966 ('NotAutomatic', 'notautomatic'),
967 ('CopyChanges', 'copychanges'),
968 ('CopyDotDak', 'copydotdak'),
969 ('CommentsDir', 'commentsdir'),
970 ('OverrideSuite', 'overridesuite'),
971 ('ChangelogBase', 'changelogbase')]
975 def __init__(self, *args, **kwargs):
979 return '<Suite %s>' % self.suite_name
983 for disp, field in SUITE_FIELDS:
984 val = getattr(self, field, None)
986 ret.append("%s: %s" % (disp, val))
988 return "\n".join(ret)
990 __all__.append('Suite')
992 def get_suite_architecture(suite, architecture, session=None):
994 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
998 @param suite: Suite name to search for
1000 @type architecture: str
1001 @param architecture: Architecture name to search for
1003 @type session: Session
1004 @param session: Optional SQL session object (a temporary one will be
1005 generated if not supplied)
1007 @rtype: SuiteArchitecture
1008 @return: the SuiteArchitecture object or None
1012 session = DBConn().session()
1014 q = session.query(SuiteArchitecture)
1015 q = q.join(Architecture).filter_by(arch_string=architecture)
1016 q = q.join(Suite).filter_by(suite_name=suite)
1021 __all__.append('get_suite_architecture')
1023 def get_suite(suite, session=None):
1025 Returns Suite object for given C{suite name}.
1028 @param suite: The name of the suite
1030 @type session: Session
1031 @param session: Optional SQLA session object (a temporary one will be
1032 generated if not supplied)
1035 @return: Suite object for the requested suite name (None if not presenT)
1039 session = DBConn().session()
1040 q = session.query(Suite).filter_by(suite_name=suite)
1045 __all__.append('get_suite')
1047 ################################################################################
1049 class SuiteArchitecture(object):
1050 def __init__(self, *args, **kwargs):
1054 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1056 __all__.append('SuiteArchitecture')
1058 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1060 Returns list of Architecture objects for given C{suite} name
1063 @param source: Suite name to search for
1065 @type skipsrc: boolean
1066 @param skipsrc: Whether to skip returning the 'source' architecture entry
1069 @type skipall: boolean
1070 @param skipall: Whether to skip returning the 'all' architecture entry
1073 @type session: Session
1074 @param session: Optional SQL session object (a temporary one will be
1075 generated if not supplied)
1078 @return: list of Architecture objects for the given name (may be empty)
1082 session = DBConn().session()
1084 q = session.query(Architecture)
1085 q = q.join(SuiteArchitecture)
1086 q = q.join(Suite).filter_by(suite_name=suite)
1088 q = q.filter(Architecture.arch_string != 'source')
1090 q = q.filter(Architecture.arch_string != 'all')
1091 q = q.order_by('arch_string')
1094 __all__.append('get_suite_architectures')
1096 ################################################################################
1099 def __init__(self, *args, **kwargs):
1103 return '<Uid %s (%s)>' % (self.uid, self.name)
1105 __all__.append('Uid')
1107 def add_database_user(uidname, session=None):
1109 Adds a database user
1111 @type uidname: string
1112 @param uidname: The uid of the user to add
1114 @type session: SQLAlchemy
1115 @param session: Optional SQL session object (a temporary one will be
1116 generated if not supplied). If not passed, a commit will be performed at
1117 the end of the function, otherwise the caller is responsible for commiting.
1120 @return: the uid object for the given uidname
1122 privatetrans = False
1124 session = DBConn().session()
1128 session.execute("CREATE USER :uid", {'uid': uidname})
1132 traceback.print_exc()
1135 __all__.append('add_database_user')
1137 def get_or_set_uid(uidname, session=None):
1139 Returns uid object for given uidname.
1141 If no matching uidname is found, a row is inserted.
1143 @type uidname: string
1144 @param uidname: The uid to add
1146 @type session: SQLAlchemy
1147 @param session: Optional SQL session object (a temporary one will be
1148 generated if not supplied). If not passed, a commit will be performed at
1149 the end of the function, otherwise the caller is responsible for commiting.
1152 @return: the uid object for the given uidname
1154 privatetrans = False
1156 session = DBConn().session()
1160 q = session.query(Uid).filter_by(uid=uidname)
1172 traceback.print_exc()
1175 __all__.append('get_or_set_uid')
1178 def get_uid_from_fingerprint(fpr, session=None):
1180 session = DBConn().session()
1182 q = session.query(Uid)
1183 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1190 __all__.append('get_uid_from_fingerprint')
1192 ################################################################################
1194 class DBConn(Singleton):
1196 database module init.
1198 def __init__(self, *args, **kwargs):
1199 super(DBConn, self).__init__(*args, **kwargs)
1201 def _startup(self, *args, **kwargs):
1203 if kwargs.has_key('debug'):
1207 def __setuptables(self):
1208 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1209 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1210 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1211 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1212 self.tbl_component = Table('component', self.db_meta, autoload=True)
1213 self.tbl_config = Table('config', self.db_meta, autoload=True)
1214 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1215 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1216 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1217 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1218 self.tbl_files = Table('files', self.db_meta, autoload=True)
1219 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1220 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1221 self.tbl_location = Table('location', self.db_meta, autoload=True)
1222 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
1223 self.tbl_override = Table('override', self.db_meta, autoload=True)
1224 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
1225 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
1226 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
1227 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
1228 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
1229 self.tbl_section = Table('section', self.db_meta, autoload=True)
1230 self.tbl_source = Table('source', self.db_meta, autoload=True)
1231 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
1232 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
1233 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
1234 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
1235 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
1237 def __setupmappers(self):
1238 mapper(Architecture, self.tbl_architecture,
1239 properties = dict(arch_id = self.tbl_architecture.c.id))
1241 mapper(Archive, self.tbl_archive,
1242 properties = dict(archive_id = self.tbl_archive.c.id,
1243 archive_name = self.tbl_archive.c.name))
1245 mapper(BinAssociation, self.tbl_bin_associations,
1246 properties = dict(ba_id = self.tbl_bin_associations.c.id,
1247 suite_id = self.tbl_bin_associations.c.suite,
1248 suite = relation(Suite),
1249 binary_id = self.tbl_bin_associations.c.bin,
1250 binary = relation(DBBinary)))
1252 mapper(DBBinary, self.tbl_binaries,
1253 properties = dict(binary_id = self.tbl_binaries.c.id,
1254 package = self.tbl_binaries.c.package,
1255 version = self.tbl_binaries.c.version,
1256 maintainer_id = self.tbl_binaries.c.maintainer,
1257 maintainer = relation(Maintainer),
1258 source_id = self.tbl_binaries.c.source,
1259 source = relation(DBSource),
1260 arch_id = self.tbl_binaries.c.architecture,
1261 architecture = relation(Architecture),
1262 poolfile_id = self.tbl_binaries.c.file,
1263 poolfile = relation(PoolFile),
1264 binarytype = self.tbl_binaries.c.type,
1265 fingerprint_id = self.tbl_binaries.c.sig_fpr,
1266 fingerprint = relation(Fingerprint),
1267 install_date = self.tbl_binaries.c.install_date,
1268 binassociations = relation(BinAssociation,
1269 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
1271 mapper(Component, self.tbl_component,
1272 properties = dict(component_id = self.tbl_component.c.id,
1273 component_name = self.tbl_component.c.name))
1275 mapper(DBConfig, self.tbl_config,
1276 properties = dict(config_id = self.tbl_config.c.id))
1278 mapper(ContentAssociation, self.tbl_content_associations,
1279 properties = dict(ca_id = self.tbl_content_associations.c.id,
1280 filename_id = self.tbl_content_associations.c.filename,
1281 filename = relation(ContentFilename),
1282 filepath_id = self.tbl_content_associations.c.filepath,
1283 filepath = relation(ContentFilepath),
1284 binary_id = self.tbl_content_associations.c.binary_pkg,
1285 binary = relation(DBBinary)))
1288 mapper(ContentFilename, self.tbl_content_file_names,
1289 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
1290 filename = self.tbl_content_file_names.c.file))
1292 mapper(ContentFilepath, self.tbl_content_file_paths,
1293 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
1294 filepath = self.tbl_content_file_paths.c.path))
1296 mapper(DSCFile, self.tbl_dsc_files,
1297 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
1298 source_id = self.tbl_dsc_files.c.source,
1299 source = relation(DBSource),
1300 poolfile_id = self.tbl_dsc_files.c.file,
1301 poolfile = relation(PoolFile)))
1303 mapper(PoolFile, self.tbl_files,
1304 properties = dict(file_id = self.tbl_files.c.id,
1305 filesize = self.tbl_files.c.size,
1306 location_id = self.tbl_files.c.location,
1307 location = relation(Location)))
1309 mapper(Fingerprint, self.tbl_fingerprint,
1310 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
1311 uid_id = self.tbl_fingerprint.c.uid,
1312 uid = relation(Uid),
1313 keyring_id = self.tbl_fingerprint.c.keyring,
1314 keyring = relation(Keyring)))
1316 mapper(Keyring, self.tbl_keyrings,
1317 properties = dict(keyring_name = self.tbl_keyrings.c.name,
1318 keyring_id = self.tbl_keyrings.c.id))
1320 mapper(Location, self.tbl_location,
1321 properties = dict(location_id = self.tbl_location.c.id,
1322 component_id = self.tbl_location.c.component,
1323 component = relation(Component),
1324 archive_id = self.tbl_location.c.archive,
1325 archive = relation(Archive),
1326 archive_type = self.tbl_location.c.type))
1328 mapper(Maintainer, self.tbl_maintainer,
1329 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
1331 mapper(Override, self.tbl_override,
1332 properties = dict(suite_id = self.tbl_override.c.suite,
1333 suite = relation(Suite),
1334 component_id = self.tbl_override.c.component,
1335 component = relation(Component),
1336 priority_id = self.tbl_override.c.priority,
1337 priority = relation(Priority),
1338 section_id = self.tbl_override.c.section,
1339 section = relation(Section),
1340 overridetype_id = self.tbl_override.c.type,
1341 overridetype = relation(OverrideType)))
1343 mapper(OverrideType, self.tbl_override_type,
1344 properties = dict(overridetype = self.tbl_override_type.c.type,
1345 overridetype_id = self.tbl_override_type.c.id))
1347 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
1348 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
1349 filepath_id = self.tbl_pending_content_associations.c.filepath,
1350 filepath = relation(ContentFilepath),
1351 filename_id = self.tbl_pending_content_associations.c.filename,
1352 filename = relation(ContentFilename)))
1354 mapper(Priority, self.tbl_priority,
1355 properties = dict(priority_id = self.tbl_priority.c.id))
1357 mapper(Queue, self.tbl_queue,
1358 properties = dict(queue_id = self.tbl_queue.c.id))
1360 mapper(QueueBuild, self.tbl_queue_build,
1361 properties = dict(suite_id = self.tbl_queue_build.c.suite,
1362 queue_id = self.tbl_queue_build.c.queue,
1363 queue = relation(Queue)))
1365 mapper(Section, self.tbl_section,
1366 properties = dict(section_id = self.tbl_section.c.id))
1368 mapper(DBSource, self.tbl_source,
1369 properties = dict(source_id = self.tbl_source.c.id,
1370 version = self.tbl_source.c.version,
1371 maintainer_id = self.tbl_source.c.maintainer,
1372 maintainer = relation(Maintainer,
1373 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
1374 poolfile_id = self.tbl_source.c.file,
1375 poolfile = relation(PoolFile),
1376 fingerprint_id = self.tbl_source.c.sig_fpr,
1377 fingerprint = relation(Fingerprint),
1378 changedby_id = self.tbl_source.c.changedby,
1379 changedby = relation(Maintainer,
1380 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
1381 srcfiles = relation(DSCFile,
1382 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
1383 srcassociations = relation(SrcAssociation,
1384 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
1386 mapper(SrcAssociation, self.tbl_src_associations,
1387 properties = dict(sa_id = self.tbl_src_associations.c.id,
1388 suite_id = self.tbl_src_associations.c.suite,
1389 suite = relation(Suite),
1390 source_id = self.tbl_src_associations.c.source,
1391 source = relation(DBSource)))
1393 mapper(SrcUploader, self.tbl_src_uploaders,
1394 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
1395 source_id = self.tbl_src_uploaders.c.source,
1396 source = relation(DBSource,
1397 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
1398 maintainer_id = self.tbl_src_uploaders.c.maintainer,
1399 maintainer = relation(Maintainer,
1400 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
1402 mapper(Suite, self.tbl_suite,
1403 properties = dict(suite_id = self.tbl_suite.c.id))
1405 mapper(SuiteArchitecture, self.tbl_suite_architectures,
1406 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
1407 suite = relation(Suite, backref='suitearchitectures'),
1408 arch_id = self.tbl_suite_architectures.c.architecture,
1409 architecture = relation(Architecture)))
1411 mapper(Uid, self.tbl_uid,
1412 properties = dict(uid_id = self.tbl_uid.c.id,
1413 fingerprint = relation(Fingerprint)))
1415 ## Connection functions
1416 def __createconn(self):
1417 from config import Config
1421 connstr = "postgres://%s" % cnf["DB::Host"]
1422 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1423 connstr += ":%s" % cnf["DB::Port"]
1424 connstr += "/%s" % cnf["DB::Name"]
1427 connstr = "postgres:///%s" % cnf["DB::Name"]
1428 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1429 connstr += "?port=%s" % cnf["DB::Port"]
1431 self.db_pg = create_engine(connstr, echo=self.debug)
1432 self.db_meta = MetaData()
1433 self.db_meta.bind = self.db_pg
1434 self.db_smaker = sessionmaker(bind=self.db_pg,
1438 self.__setuptables()
1439 self.__setupmappers()
1442 return self.db_smaker()
1444 __all__.append('DBConn')