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 inspect import getargspec
42 from sqlalchemy import create_engine, Table, MetaData, select
43 from sqlalchemy.orm import sessionmaker, mapper, relation
45 # Don't remove this, we re-export the exceptions to scripts which import us
46 from sqlalchemy.exc import *
47 from sqlalchemy.orm.exc import NoResultFound
49 # Only import Config until Queue stuff is changed to store its config
51 from config import Config
52 from singleton import Singleton
53 from textutils import fix_maintainer
55 ################################################################################
57 __all__ = ['IntegrityError', 'SQLAlchemyError']
59 ################################################################################
61 def session_wrapper(fn):
62 def wrapped(*args, **kwargs):
63 private_transaction = False
64 session = kwargs.get('session')
66 # No session specified as last argument or in kwargs, create one.
67 if session is None and len(args) <= len(getargspec(fn)[0]) - 1:
68 private_transaction = True
69 kwargs['session'] = DBConn().session()
72 return fn(*args, **kwargs)
74 if private_transaction:
75 # We created a session; close it.
76 kwargs['session'].close()
78 wrapped.__doc__ = fn.__doc__
79 wrapped.func_name = fn.func_name
83 ################################################################################
85 class Architecture(object):
86 def __init__(self, *args, **kwargs):
89 def __eq__(self, val):
90 if isinstance(val, str):
91 return (self.arch_string== val)
92 # This signals to use the normal comparison operator
95 def __ne__(self, val):
96 if isinstance(val, str):
97 return (self.arch_string != val)
98 # This signals to use the normal comparison operator
102 return '<Architecture %s>' % self.arch_string
104 __all__.append('Architecture')
107 def get_architecture(architecture, session=None):
109 Returns database id for given C{architecture}.
111 @type architecture: string
112 @param architecture: The name of the architecture
114 @type session: Session
115 @param session: Optional SQLA session object (a temporary one will be
116 generated if not supplied)
119 @return: Architecture object for the given arch (None if not present)
122 q = session.query(Architecture).filter_by(arch_string=architecture)
126 except NoResultFound:
129 __all__.append('get_architecture')
132 def get_architecture_suites(architecture, session=None):
134 Returns list of Suite objects for given C{architecture} name
137 @param source: Architecture name to search for
139 @type session: Session
140 @param session: Optional SQL session object (a temporary one will be
141 generated if not supplied)
144 @return: list of Suite objects for the given name (may be empty)
147 q = session.query(Suite)
148 q = q.join(SuiteArchitecture)
149 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
155 __all__.append('get_architecture_suites')
157 ################################################################################
159 class Archive(object):
160 def __init__(self, *args, **kwargs):
164 return '<Archive %s>' % self.archive_name
166 __all__.append('Archive')
169 def get_archive(archive, session=None):
171 returns database id for given c{archive}.
173 @type archive: string
174 @param archive: the name of the arhive
176 @type session: Session
177 @param session: Optional SQLA session object (a temporary one will be
178 generated if not supplied)
181 @return: Archive object for the given name (None if not present)
184 archive = archive.lower()
186 q = session.query(Archive).filter_by(archive_name=archive)
190 except NoResultFound:
193 __all__.append('get_archive')
195 ################################################################################
197 class BinAssociation(object):
198 def __init__(self, *args, **kwargs):
202 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
204 __all__.append('BinAssociation')
206 ################################################################################
208 class DBBinary(object):
209 def __init__(self, *args, **kwargs):
213 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
215 __all__.append('DBBinary')
218 def get_suites_binary_in(package, session=None):
220 Returns list of Suite objects which given C{package} name is in
223 @param source: DBBinary package name to search for
226 @return: list of Suite objects for the given package
229 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
231 __all__.append('get_suites_binary_in')
234 def get_binary_from_id(id, session=None):
236 Returns DBBinary object for given C{id}
239 @param id: Id of the required binary
241 @type session: Session
242 @param session: Optional SQLA session object (a temporary one will be
243 generated if not supplied)
246 @return: DBBinary object for the given binary (None if not present)
249 q = session.query(DBBinary).filter_by(binary_id=id)
253 except NoResultFound:
256 __all__.append('get_binary_from_id')
259 def get_binaries_from_name(package, version=None, architecture=None, session=None):
261 Returns list of DBBinary objects for given C{package} name
264 @param package: DBBinary package name to search for
266 @type version: str or None
267 @param version: Version to search for (or None)
269 @type package: str, list or None
270 @param package: Architectures to limit to (or None if no limit)
272 @type session: Session
273 @param session: Optional SQL session object (a temporary one will be
274 generated if not supplied)
277 @return: list of DBBinary objects for the given name (may be empty)
280 q = session.query(DBBinary).filter_by(package=package)
282 if version is not None:
283 q = q.filter_by(version=version)
285 if architecture is not None:
286 if not isinstance(architecture, list):
287 architecture = [architecture]
288 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
294 __all__.append('get_binaries_from_name')
297 def get_binaries_from_source_id(source_id, session=None):
299 Returns list of DBBinary objects for given C{source_id}
302 @param source_id: source_id to search for
304 @type session: Session
305 @param session: Optional SQL session object (a temporary one will be
306 generated if not supplied)
309 @return: list of DBBinary objects for the given name (may be empty)
312 return session.query(DBBinary).filter_by(source_id=source_id).all()
314 __all__.append('get_binaries_from_source_id')
317 def get_binary_from_name_suite(package, suitename, session=None):
318 ### For dak examine-package
319 ### XXX: Doesn't use object API yet
321 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
322 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
323 WHERE b.package=:package
325 AND fi.location = l.id
326 AND l.component = c.id
329 AND su.suite_name=:suitename
330 ORDER BY b.version DESC"""
332 return session.execute(sql, {'package': package, 'suitename': suitename})
334 __all__.append('get_binary_from_name_suite')
337 def get_binary_components(package, suitename, arch, session=None):
338 # Check for packages that have moved from one component to another
339 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
340 WHERE b.package=:package AND s.suite_name=:suitename
341 AND (a.arch_string = :arch OR a.arch_string = 'all')
342 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
343 AND f.location = l.id
344 AND l.component = c.id
347 vals = {'package': package, 'suitename': suitename, 'arch': arch}
349 return session.execute(query, vals)
351 __all__.append('get_binary_components')
353 ################################################################################
355 class Component(object):
356 def __init__(self, *args, **kwargs):
359 def __eq__(self, val):
360 if isinstance(val, str):
361 return (self.component_name == val)
362 # This signals to use the normal comparison operator
363 return NotImplemented
365 def __ne__(self, val):
366 if isinstance(val, str):
367 return (self.component_name != val)
368 # This signals to use the normal comparison operator
369 return NotImplemented
372 return '<Component %s>' % self.component_name
375 __all__.append('Component')
378 def get_component(component, session=None):
380 Returns database id for given C{component}.
382 @type component: string
383 @param component: The name of the override type
386 @return: the database id for the given component
389 component = component.lower()
391 q = session.query(Component).filter_by(component_name=component)
395 except NoResultFound:
398 __all__.append('get_component')
400 ################################################################################
402 class DBConfig(object):
403 def __init__(self, *args, **kwargs):
407 return '<DBConfig %s>' % self.name
409 __all__.append('DBConfig')
411 ################################################################################
413 class ContentFilename(object):
414 def __init__(self, *args, **kwargs):
418 return '<ContentFilename %s>' % self.filename
420 __all__.append('ContentFilename')
422 def get_or_set_contents_file_id(filename, session=None):
424 Returns database id for given filename.
426 If no matching file is found, a row is inserted.
428 @type filename: string
429 @param filename: The filename
430 @type session: SQLAlchemy
431 @param session: Optional SQL session object (a temporary one will be
432 generated if not supplied). If not passed, a commit will be performed at
433 the end of the function, otherwise the caller is responsible for commiting.
436 @return: the database id for the given component
440 session = DBConn().session()
443 q = session.query(ContentFilename).filter_by(filename=filename)
446 ret = q.one().cafilename_id
447 except NoResultFound:
448 cf = ContentFilename()
449 cf.filename = filename
455 ret = cf.cafilename_id
462 __all__.append('get_or_set_contents_file_id')
465 def get_contents(suite, overridetype, section=None, session=None):
467 Returns contents for a suite / overridetype combination, limiting
468 to a section if not None.
471 @param suite: Suite object
473 @type overridetype: OverrideType
474 @param overridetype: OverrideType object
476 @type section: Section
477 @param section: Optional section object to limit results to
479 @type session: SQLAlchemy
480 @param session: Optional SQL session object (a temporary one will be
481 generated if not supplied)
484 @return: ResultsProxy object set up to return tuples of (filename, section,
488 # find me all of the contents for a given suite
489 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
493 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
494 JOIN content_file_names n ON (c.filename=n.id)
495 JOIN binaries b ON (b.id=c.binary_pkg)
496 JOIN override o ON (o.package=b.package)
497 JOIN section s ON (s.id=o.section)
498 WHERE o.suite = :suiteid AND o.type = :overridetypeid
499 AND b.type=:overridetypename"""
501 vals = {'suiteid': suite.suite_id,
502 'overridetypeid': overridetype.overridetype_id,
503 'overridetypename': overridetype.overridetype}
505 if section is not None:
506 contents_q += " AND s.id = :sectionid"
507 vals['sectionid'] = section.section_id
509 contents_q += " ORDER BY fn"
511 return session.execute(contents_q, vals)
513 __all__.append('get_contents')
515 ################################################################################
517 class ContentFilepath(object):
518 def __init__(self, *args, **kwargs):
522 return '<ContentFilepath %s>' % self.filepath
524 __all__.append('ContentFilepath')
526 def get_or_set_contents_path_id(filepath, session=None):
528 Returns database id for given path.
530 If no matching file is found, a row is inserted.
532 @type filename: string
533 @param filename: The filepath
534 @type session: SQLAlchemy
535 @param session: Optional SQL session object (a temporary one will be
536 generated if not supplied). If not passed, a commit will be performed at
537 the end of the function, otherwise the caller is responsible for commiting.
540 @return: the database id for the given path
544 session = DBConn().session()
547 q = session.query(ContentFilepath).filter_by(filepath=filepath)
550 ret = q.one().cafilepath_id
551 except NoResultFound:
552 cf = ContentFilepath()
553 cf.filepath = filepath
559 ret = cf.cafilepath_id
566 __all__.append('get_or_set_contents_path_id')
568 ################################################################################
570 class ContentAssociation(object):
571 def __init__(self, *args, **kwargs):
575 return '<ContentAssociation %s>' % self.ca_id
577 __all__.append('ContentAssociation')
579 def insert_content_paths(binary_id, fullpaths, session=None):
581 Make sure given path is associated with given binary id
584 @param binary_id: the id of the binary
585 @type fullpaths: list
586 @param fullpaths: the list of paths of the file being associated with the binary
587 @type session: SQLAlchemy session
588 @param session: Optional SQLAlchemy session. If this is passed, the caller
589 is responsible for ensuring a transaction has begun and committing the
590 results or rolling back based on the result code. If not passed, a commit
591 will be performed at the end of the function, otherwise the caller is
592 responsible for commiting.
594 @return: True upon success
599 session = DBConn().session()
605 for fullpath in fullpaths:
606 # Get the necessary IDs ...
607 (path, file) = os.path.split(fullpath)
609 filepath_id = get_or_set_contents_path_id(path, session)
610 filename_id = get_or_set_contents_file_id(file, session)
612 pathcache[fullpath] = (filepath_id, filename_id)
614 for fullpath, dat in pathcache.items():
615 ca = ContentAssociation()
616 ca.binary_id = binary_id
617 ca.filepath_id = dat[0]
618 ca.filename_id = dat[1]
621 # Only commit if we set up the session ourself
631 traceback.print_exc()
633 # Only rollback if we set up the session ourself
640 __all__.append('insert_content_paths')
642 ################################################################################
644 class DSCFile(object):
645 def __init__(self, *args, **kwargs):
649 return '<DSCFile %s>' % self.dscfile_id
651 __all__.append('DSCFile')
654 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
656 Returns a list of DSCFiles which may be empty
658 @type dscfile_id: int (optional)
659 @param dscfile_id: the dscfile_id of the DSCFiles to find
661 @type source_id: int (optional)
662 @param source_id: the source id related to the DSCFiles to find
664 @type poolfile_id: int (optional)
665 @param poolfile_id: the poolfile id related to the DSCFiles to find
668 @return: Possibly empty list of DSCFiles
671 q = session.query(DSCFile)
673 if dscfile_id is not None:
674 q = q.filter_by(dscfile_id=dscfile_id)
676 if source_id is not None:
677 q = q.filter_by(source_id=source_id)
679 if poolfile_id is not None:
680 q = q.filter_by(poolfile_id=poolfile_id)
684 __all__.append('get_dscfiles')
686 ################################################################################
688 class PoolFile(object):
689 def __init__(self, *args, **kwargs):
693 return '<PoolFile %s>' % self.filename
695 __all__.append('PoolFile')
698 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
701 (ValidFileFound [boolean or None], PoolFile object or None)
703 @type filename: string
704 @param filename: the filename of the file to check against the DB
707 @param filesize: the size of the file to check against the DB
710 @param md5sum: the md5sum of the file to check against the DB
712 @type location_id: int
713 @param location_id: the id of the location to look in
716 @return: Tuple of length 2.
717 If more than one file found with that name:
719 If valid pool file found: (True, PoolFile object)
720 If valid pool file not found:
721 (False, None) if no file found
722 (False, PoolFile object) if file found with size/md5sum mismatch
725 q = session.query(PoolFile).filter_by(filename=filename)
726 q = q.join(Location).filter_by(location_id=location_id)
736 if obj.md5sum != md5sum or obj.filesize != filesize:
744 __all__.append('check_poolfile')
747 def get_poolfile_by_id(file_id, session=None):
749 Returns a PoolFile objects or None for the given id
752 @param file_id: the id of the file to look for
754 @rtype: PoolFile or None
755 @return: either the PoolFile object or None
758 q = session.query(PoolFile).filter_by(file_id=file_id)
762 except NoResultFound:
765 __all__.append('get_poolfile_by_id')
769 def get_poolfile_by_name(filename, location_id=None, session=None):
771 Returns an array of PoolFile objects for the given filename and
772 (optionally) location_id
774 @type filename: string
775 @param filename: the filename of the file to check against the DB
777 @type location_id: int
778 @param location_id: the id of the location to look in (optional)
781 @return: array of PoolFile objects
784 q = session.query(PoolFile).filter_by(filename=filename)
786 if location_id is not None:
787 q = q.join(Location).filter_by(location_id=location_id)
791 __all__.append('get_poolfile_by_name')
794 def get_poolfile_like_name(filename, session=None):
796 Returns an array of PoolFile objects which are like the given name
798 @type filename: string
799 @param filename: the filename of the file to check against the DB
802 @return: array of PoolFile objects
805 # TODO: There must be a way of properly using bind parameters with %FOO%
806 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
810 __all__.append('get_poolfile_like_name')
812 ################################################################################
814 class Fingerprint(object):
815 def __init__(self, *args, **kwargs):
819 return '<Fingerprint %s>' % self.fingerprint
821 __all__.append('Fingerprint')
823 def get_or_set_fingerprint(fpr, session=None):
825 Returns Fingerprint object for given fpr.
827 If no matching fpr is found, a row is inserted.
830 @param fpr: The fpr to find / add
832 @type session: SQLAlchemy
833 @param session: Optional SQL session object (a temporary one will be
834 generated if not supplied). If not passed, a commit will be performed at
835 the end of the function, otherwise the caller is responsible for commiting.
836 A flush will be performed either way.
839 @return: the Fingerprint object for the given fpr
843 session = DBConn().session()
846 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
850 except NoResultFound:
851 fingerprint = Fingerprint()
852 fingerprint.fingerprint = fpr
853 session.add(fingerprint)
865 __all__.append('get_or_set_fingerprint')
867 ################################################################################
869 class Keyring(object):
870 def __init__(self, *args, **kwargs):
874 return '<Keyring %s>' % self.keyring_name
876 __all__.append('Keyring')
878 def get_or_set_keyring(keyring, session=None):
880 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
881 and return the new Keyring
882 If C{keyring} already has an entry, simply return the existing Keyring
884 @type keyring: string
885 @param keyring: the keyring name
888 @return: the Keyring object for this keyring
893 session = DBConn().session()
897 obj = session.query(Keyring).filter_by(keyring_name=keyring).first()
900 obj = Keyring(keyring_name=keyring)
912 __all__.append('get_or_set_keyring')
914 ################################################################################
916 class Location(object):
917 def __init__(self, *args, **kwargs):
921 return '<Location %s (%s)>' % (self.path, self.location_id)
923 __all__.append('Location')
926 def get_location(location, component=None, archive=None, session=None):
928 Returns Location object for the given combination of location, component
931 @type location: string
932 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
934 @type component: string
935 @param component: the component name (if None, no restriction applied)
937 @type archive: string
938 @param archive_id: the archive name (if None, no restriction applied)
940 @rtype: Location / None
941 @return: Either a Location object or None if one can't be found
944 q = session.query(Location).filter_by(path=location)
946 if archive is not None:
947 q = q.join(Archive).filter_by(archive_name=archive)
949 if component is not None:
950 q = q.join(Component).filter_by(component_name=component)
954 except NoResultFound:
957 __all__.append('get_location')
959 ################################################################################
961 class Maintainer(object):
962 def __init__(self, *args, **kwargs):
966 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
968 def get_split_maintainer(self):
969 if not hasattr(self, 'name') or self.name is None:
970 return ('', '', '', '')
972 return fix_maintainer(self.name.strip())
974 __all__.append('Maintainer')
976 def get_or_set_maintainer(name, session=None):
978 Returns Maintainer object for given maintainer name.
980 If no matching maintainer name is found, a row is inserted.
983 @param name: The maintainer name to add
985 @type session: SQLAlchemy
986 @param session: Optional SQL session object (a temporary one will be
987 generated if not supplied). If not passed, a commit will be performed at
988 the end of the function, otherwise the caller is responsible for commiting.
989 A flush will be performed either way.
992 @return: the Maintainer object for the given maintainer
996 session = DBConn().session()
999 q = session.query(Maintainer).filter_by(name=name)
1002 except NoResultFound:
1003 maintainer = Maintainer()
1004 maintainer.name = name
1005 session.add(maintainer)
1017 __all__.append('get_or_set_maintainer')
1019 def get_maintainer(maintainer_id, session=None):
1021 Return the name of the maintainer behind C{maintainer_id} or None if that
1022 maintainer_id is invalid.
1024 @type maintainer_id: int
1025 @param maintainer_id: the id of the maintainer
1028 @return: the Maintainer with this C{maintainer_id}
1031 privatetrans = False
1033 session = DBConn().session()
1037 return session.query(Maintainer).get(maintainer_id)
1042 __all__.append('get_maintainer')
1044 ################################################################################
1046 class NewComment(object):
1047 def __init__(self, *args, **kwargs):
1051 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1053 __all__.append('NewComment')
1056 def has_new_comment(package, version, session=None):
1058 Returns true if the given combination of C{package}, C{version} has a comment.
1060 @type package: string
1061 @param package: name of the package
1063 @type version: string
1064 @param version: package version
1066 @type session: Session
1067 @param session: Optional SQLA session object (a temporary one will be
1068 generated if not supplied)
1074 q = session.query(NewComment)
1075 q = q.filter_by(package=package)
1076 q = q.filter_by(version=version)
1078 return bool(q.count() > 0)
1080 __all__.append('has_new_comment')
1083 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1085 Returns (possibly empty) list of NewComment objects for the given
1088 @type package: string (optional)
1089 @param package: name of the package
1091 @type version: string (optional)
1092 @param version: package version
1094 @type comment_id: int (optional)
1095 @param comment_id: An id of a comment
1097 @type session: Session
1098 @param session: Optional SQLA session object (a temporary one will be
1099 generated if not supplied)
1102 @return: A (possibly empty) list of NewComment objects will be returned
1105 q = session.query(NewComment)
1106 if package is not None: q = q.filter_by(package=package)
1107 if version is not None: q = q.filter_by(version=version)
1108 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1112 __all__.append('get_new_comments')
1114 ################################################################################
1116 class Override(object):
1117 def __init__(self, *args, **kwargs):
1121 return '<Override %s (%s)>' % (self.package, self.suite_id)
1123 __all__.append('Override')
1126 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1128 Returns Override object for the given parameters
1130 @type package: string
1131 @param package: The name of the package
1133 @type suite: string, list or None
1134 @param suite: The name of the suite (or suites if a list) to limit to. If
1135 None, don't limit. Defaults to None.
1137 @type component: string, list or None
1138 @param component: The name of the component (or components if a list) to
1139 limit to. If None, don't limit. Defaults to None.
1141 @type overridetype: string, list or None
1142 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1143 limit to. If None, don't limit. Defaults to None.
1145 @type session: Session
1146 @param session: Optional SQLA session object (a temporary one will be
1147 generated if not supplied)
1150 @return: A (possibly empty) list of Override objects will be returned
1153 q = session.query(Override)
1154 q = q.filter_by(package=package)
1156 if suite is not None:
1157 if not isinstance(suite, list): suite = [suite]
1158 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1160 if component is not None:
1161 if not isinstance(component, list): component = [component]
1162 q = q.join(Component).filter(Component.component_name.in_(component))
1164 if overridetype is not None:
1165 if not isinstance(overridetype, list): overridetype = [overridetype]
1166 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1170 __all__.append('get_override')
1173 ################################################################################
1175 class OverrideType(object):
1176 def __init__(self, *args, **kwargs):
1180 return '<OverrideType %s>' % self.overridetype
1182 __all__.append('OverrideType')
1185 def get_override_type(override_type, session=None):
1187 Returns OverrideType object for given C{override type}.
1189 @type override_type: string
1190 @param override_type: The name of the override type
1192 @type session: Session
1193 @param session: Optional SQLA session object (a temporary one will be
1194 generated if not supplied)
1197 @return: the database id for the given override type
1200 q = session.query(OverrideType).filter_by(overridetype=override_type)
1204 except NoResultFound:
1207 __all__.append('get_override_type')
1209 ################################################################################
1211 class PendingContentAssociation(object):
1212 def __init__(self, *args, **kwargs):
1216 return '<PendingContentAssociation %s>' % self.pca_id
1218 __all__.append('PendingContentAssociation')
1220 def insert_pending_content_paths(package, fullpaths, session=None):
1222 Make sure given paths are temporarily associated with given
1226 @param package: the package to associate with should have been read in from the binary control file
1227 @type fullpaths: list
1228 @param fullpaths: the list of paths of the file being associated with the binary
1229 @type session: SQLAlchemy session
1230 @param session: Optional SQLAlchemy session. If this is passed, the caller
1231 is responsible for ensuring a transaction has begun and committing the
1232 results or rolling back based on the result code. If not passed, a commit
1233 will be performed at the end of the function
1235 @return: True upon success, False if there is a problem
1238 privatetrans = False
1241 session = DBConn().session()
1245 arch = get_architecture(package['Architecture'], session)
1246 arch_id = arch.arch_id
1248 # Remove any already existing recorded files for this package
1249 q = session.query(PendingContentAssociation)
1250 q = q.filter_by(package=package['Package'])
1251 q = q.filter_by(version=package['Version'])
1252 q = q.filter_by(architecture=arch_id)
1257 for fullpath in fullpaths:
1258 (path, file) = os.path.split(fullpath)
1260 if path.startswith( "./" ):
1263 filepath_id = get_or_set_contents_path_id(path, session)
1264 filename_id = get_or_set_contents_file_id(file, session)
1266 pathcache[fullpath] = (filepath_id, filename_id)
1268 for fullpath, dat in pathcache.items():
1269 pca = PendingContentAssociation()
1270 pca.package = package['Package']
1271 pca.version = package['Version']
1272 pca.filepath_id = dat[0]
1273 pca.filename_id = dat[1]
1274 pca.architecture = arch_id
1277 # Only commit if we set up the session ourself
1285 except Exception, e:
1286 traceback.print_exc()
1288 # Only rollback if we set up the session ourself
1295 __all__.append('insert_pending_content_paths')
1297 ################################################################################
1299 class Priority(object):
1300 def __init__(self, *args, **kwargs):
1303 def __eq__(self, val):
1304 if isinstance(val, str):
1305 return (self.priority == val)
1306 # This signals to use the normal comparison operator
1307 return NotImplemented
1309 def __ne__(self, val):
1310 if isinstance(val, str):
1311 return (self.priority != val)
1312 # This signals to use the normal comparison operator
1313 return NotImplemented
1316 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1318 __all__.append('Priority')
1321 def get_priority(priority, session=None):
1323 Returns Priority object for given C{priority name}.
1325 @type priority: string
1326 @param priority: The name of the priority
1328 @type session: Session
1329 @param session: Optional SQLA session object (a temporary one will be
1330 generated if not supplied)
1333 @return: Priority object for the given priority
1336 q = session.query(Priority).filter_by(priority=priority)
1340 except NoResultFound:
1343 __all__.append('get_priority')
1346 def get_priorities(session=None):
1348 Returns dictionary of priority names -> id mappings
1350 @type session: Session
1351 @param session: Optional SQL session object (a temporary one will be
1352 generated if not supplied)
1355 @return: dictionary of priority names -> id mappings
1359 q = session.query(Priority)
1361 ret[x.priority] = x.priority_id
1365 __all__.append('get_priorities')
1367 ################################################################################
1369 class Queue(object):
1370 def __init__(self, *args, **kwargs):
1374 return '<Queue %s>' % self.queue_name
1376 def autobuild_upload(self, changes, srcpath, session=None):
1378 Update queue_build database table used for incoming autobuild support.
1380 @type changes: Changes
1381 @param changes: changes object for the upload to process
1383 @type srcpath: string
1384 @param srcpath: path for the queue file entries/link destinations
1386 @type session: SQLAlchemy session
1387 @param session: Optional SQLAlchemy session. If this is passed, the
1388 caller is responsible for ensuring a transaction has begun and
1389 committing the results or rolling back based on the result code. If
1390 not passed, a commit will be performed at the end of the function,
1391 otherwise the caller is responsible for commiting.
1393 @rtype: NoneType or string
1394 @return: None if the operation failed, a string describing the error if not
1397 privatetrans = False
1399 session = DBConn().session()
1402 # TODO: Remove by moving queue config into the database
1405 for suitename in changes.changes["distribution"].keys():
1406 # TODO: Move into database as:
1407 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1408 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1409 # This also gets rid of the SecurityQueueBuild hack below
1410 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1414 s = get_suite(suitename, session)
1416 return "INTERNAL ERROR: Could not find suite %s" % suitename
1418 # TODO: Get from database as above
1419 dest_dir = conf["Dir::QueueBuild"]
1421 # TODO: Move into database as above
1422 if conf.FindB("Dinstall::SecurityQueueBuild"):
1423 dest_dir = os.path.join(dest_dir, suitename)
1425 for file_entry in changes.files.keys():
1426 src = os.path.join(srcpath, file_entry)
1427 dest = os.path.join(dest_dir, file_entry)
1429 # TODO: Move into database as above
1430 if conf.FindB("Dinstall::SecurityQueueBuild"):
1431 # Copy it since the original won't be readable by www-data
1433 utils.copy(src, dest)
1435 # Create a symlink to it
1436 os.symlink(src, dest)
1439 qb.suite_id = s.suite_id
1440 qb.queue_id = self.queue_id
1446 # If the .orig tarballs are in the pool, create a symlink to
1447 # them (if one doesn't already exist)
1448 for dsc_file in changes.dsc_files.keys():
1449 # Skip all files except orig tarballs
1450 if not re_is_orig_source.match(dsc_file):
1452 # Skip orig files not identified in the pool
1453 if not (changes.orig_files.has_key(dsc_file) and
1454 changes.orig_files[dsc_file].has_key("id")):
1456 orig_file_id = changes.orig_files[dsc_file]["id"]
1457 dest = os.path.join(dest_dir, dsc_file)
1459 # If it doesn't exist, create a symlink
1460 if not os.path.exists(dest):
1461 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1462 {'id': orig_file_id})
1465 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1467 src = os.path.join(res[0], res[1])
1468 os.symlink(src, dest)
1470 # Add it to the list of packages for later processing by apt-ftparchive
1472 qb.suite_id = s.suite_id
1473 qb.queue_id = self.queue_id
1478 # If it does, update things to ensure it's not removed prematurely
1480 qb = get_queue_build(dest, s.suite_id, session)
1492 __all__.append('Queue')
1495 def get_queue(queuename, session=None):
1497 Returns Queue object for given C{queue name}.
1499 @type queuename: string
1500 @param queuename: The name of the queue
1502 @type session: Session
1503 @param session: Optional SQLA session object (a temporary one will be
1504 generated if not supplied)
1507 @return: Queue object for the given queue
1510 q = session.query(Queue).filter_by(queue_name=queuename)
1514 except NoResultFound:
1517 __all__.append('get_queue')
1519 ################################################################################
1521 class QueueBuild(object):
1522 def __init__(self, *args, **kwargs):
1526 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1528 __all__.append('QueueBuild')
1531 def get_queue_build(filename, suite, session=None):
1533 Returns QueueBuild object for given C{filename} and C{suite}.
1535 @type filename: string
1536 @param filename: The name of the file
1538 @type suiteid: int or str
1539 @param suiteid: Suite name or ID
1541 @type session: Session
1542 @param session: Optional SQLA session object (a temporary one will be
1543 generated if not supplied)
1546 @return: Queue object for the given queue
1549 if isinstance(suite, int):
1550 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1552 q = session.query(QueueBuild).filter_by(filename=filename)
1553 q = q.join(Suite).filter_by(suite_name=suite)
1557 except NoResultFound:
1560 __all__.append('get_queue_build')
1562 ################################################################################
1564 class Section(object):
1565 def __init__(self, *args, **kwargs):
1568 def __eq__(self, val):
1569 if isinstance(val, str):
1570 return (self.section == val)
1571 # This signals to use the normal comparison operator
1572 return NotImplemented
1574 def __ne__(self, val):
1575 if isinstance(val, str):
1576 return (self.section != val)
1577 # This signals to use the normal comparison operator
1578 return NotImplemented
1581 return '<Section %s>' % self.section
1583 __all__.append('Section')
1586 def get_section(section, session=None):
1588 Returns Section object for given C{section name}.
1590 @type section: string
1591 @param section: The name of the section
1593 @type session: Session
1594 @param session: Optional SQLA session object (a temporary one will be
1595 generated if not supplied)
1598 @return: Section object for the given section name
1601 q = session.query(Section).filter_by(section=section)
1605 except NoResultFound:
1608 __all__.append('get_section')
1611 def get_sections(session=None):
1613 Returns dictionary of section names -> id mappings
1615 @type session: Session
1616 @param session: Optional SQL session object (a temporary one will be
1617 generated if not supplied)
1620 @return: dictionary of section names -> id mappings
1624 q = session.query(Section)
1626 ret[x.section] = x.section_id
1630 __all__.append('get_sections')
1632 ################################################################################
1634 class DBSource(object):
1635 def __init__(self, *args, **kwargs):
1639 return '<DBSource %s (%s)>' % (self.source, self.version)
1641 __all__.append('DBSource')
1644 def source_exists(source, source_version, suites = ["any"], session=None):
1646 Ensure that source exists somewhere in the archive for the binary
1647 upload being processed.
1648 1. exact match => 1.0-3
1649 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1651 @type package: string
1652 @param package: package source name
1654 @type source_version: string
1655 @param source_version: expected source version
1658 @param suites: list of suites to check in, default I{any}
1660 @type session: Session
1661 @param session: Optional SQLA session object (a temporary one will be
1662 generated if not supplied)
1665 @return: returns 1 if a source with expected version is found, otherwise 0
1672 for suite in suites:
1673 q = session.query(DBSource).filter_by(source=source)
1675 # source must exist in suite X, or in some other suite that's
1676 # mapped to X, recursively... silent-maps are counted too,
1677 # unreleased-maps aren't.
1678 maps = cnf.ValueList("SuiteMappings")[:]
1680 maps = [ m.split() for m in maps ]
1681 maps = [ (x[1], x[2]) for x in maps
1682 if x[0] == "map" or x[0] == "silent-map" ]
1685 if x[1] in s and x[0] not in s:
1688 q = q.join(SrcAssociation).join(Suite)
1689 q = q.filter(Suite.suite_name.in_(s))
1691 # Reduce the query results to a list of version numbers
1692 ql = [ j.version for j in q.all() ]
1695 if source_version in ql:
1699 from daklib.regexes import re_bin_only_nmu
1700 orig_source_version = re_bin_only_nmu.sub('', source_version)
1701 if orig_source_version in ql:
1704 # No source found so return not ok
1709 __all__.append('source_exists')
1712 def get_suites_source_in(source, session=None):
1714 Returns list of Suite objects which given C{source} name is in
1717 @param source: DBSource package name to search for
1720 @return: list of Suite objects for the given source
1723 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1725 __all__.append('get_suites_source_in')
1728 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1730 Returns list of DBSource objects for given C{source} name and other parameters
1733 @param source: DBSource package name to search for
1735 @type source: str or None
1736 @param source: DBSource version name to search for or None if not applicable
1738 @type dm_upload_allowed: bool
1739 @param dm_upload_allowed: If None, no effect. If True or False, only
1740 return packages with that dm_upload_allowed setting
1742 @type session: Session
1743 @param session: Optional SQL session object (a temporary one will be
1744 generated if not supplied)
1747 @return: list of DBSource objects for the given name (may be empty)
1750 q = session.query(DBSource).filter_by(source=source)
1752 if version is not None:
1753 q = q.filter_by(version=version)
1755 if dm_upload_allowed is not None:
1756 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1760 __all__.append('get_sources_from_name')
1763 def get_source_in_suite(source, suite, session=None):
1765 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1767 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1768 - B{suite} - a suite name, eg. I{unstable}
1770 @type source: string
1771 @param source: source package name
1774 @param suite: the suite name
1777 @return: the version for I{source} in I{suite}
1781 q = session.query(SrcAssociation)
1782 q = q.join('source').filter_by(source=source)
1783 q = q.join('suite').filter_by(suite_name=suite)
1786 return q.one().source
1787 except NoResultFound:
1790 __all__.append('get_source_in_suite')
1792 ################################################################################
1794 class SrcAssociation(object):
1795 def __init__(self, *args, **kwargs):
1799 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1801 __all__.append('SrcAssociation')
1803 ################################################################################
1805 class SrcFormat(object):
1806 def __init__(self, *args, **kwargs):
1810 return '<SrcFormat %s>' % (self.format_name)
1812 __all__.append('SrcFormat')
1814 ################################################################################
1816 class SrcUploader(object):
1817 def __init__(self, *args, **kwargs):
1821 return '<SrcUploader %s>' % self.uploader_id
1823 __all__.append('SrcUploader')
1825 ################################################################################
1827 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1828 ('SuiteID', 'suite_id'),
1829 ('Version', 'version'),
1830 ('Origin', 'origin'),
1832 ('Description', 'description'),
1833 ('Untouchable', 'untouchable'),
1834 ('Announce', 'announce'),
1835 ('Codename', 'codename'),
1836 ('OverrideCodename', 'overridecodename'),
1837 ('ValidTime', 'validtime'),
1838 ('Priority', 'priority'),
1839 ('NotAutomatic', 'notautomatic'),
1840 ('CopyChanges', 'copychanges'),
1841 ('CopyDotDak', 'copydotdak'),
1842 ('CommentsDir', 'commentsdir'),
1843 ('OverrideSuite', 'overridesuite'),
1844 ('ChangelogBase', 'changelogbase')]
1847 class Suite(object):
1848 def __init__(self, *args, **kwargs):
1852 return '<Suite %s>' % self.suite_name
1854 def __eq__(self, val):
1855 if isinstance(val, str):
1856 return (self.suite_name == val)
1857 # This signals to use the normal comparison operator
1858 return NotImplemented
1860 def __ne__(self, val):
1861 if isinstance(val, str):
1862 return (self.suite_name != val)
1863 # This signals to use the normal comparison operator
1864 return NotImplemented
1868 for disp, field in SUITE_FIELDS:
1869 val = getattr(self, field, None)
1871 ret.append("%s: %s" % (disp, val))
1873 return "\n".join(ret)
1875 __all__.append('Suite')
1878 def get_suite_architecture(suite, architecture, session=None):
1880 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1884 @param suite: Suite name to search for
1886 @type architecture: str
1887 @param architecture: Architecture name to search for
1889 @type session: Session
1890 @param session: Optional SQL session object (a temporary one will be
1891 generated if not supplied)
1893 @rtype: SuiteArchitecture
1894 @return: the SuiteArchitecture object or None
1897 q = session.query(SuiteArchitecture)
1898 q = q.join(Architecture).filter_by(arch_string=architecture)
1899 q = q.join(Suite).filter_by(suite_name=suite)
1903 except NoResultFound:
1906 __all__.append('get_suite_architecture')
1909 def get_suite(suite, session=None):
1911 Returns Suite object for given C{suite name}.
1914 @param suite: The name of the suite
1916 @type session: Session
1917 @param session: Optional SQLA session object (a temporary one will be
1918 generated if not supplied)
1921 @return: Suite object for the requested suite name (None if not presenT)
1924 q = session.query(Suite).filter_by(suite_name=suite)
1928 except NoResultFound:
1931 __all__.append('get_suite')
1933 ################################################################################
1935 class SuiteArchitecture(object):
1936 def __init__(self, *args, **kwargs):
1940 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1942 __all__.append('SuiteArchitecture')
1945 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1947 Returns list of Architecture objects for given C{suite} name
1950 @param source: Suite name to search for
1952 @type skipsrc: boolean
1953 @param skipsrc: Whether to skip returning the 'source' architecture entry
1956 @type skipall: boolean
1957 @param skipall: Whether to skip returning the 'all' architecture entry
1960 @type session: Session
1961 @param session: Optional SQL session object (a temporary one will be
1962 generated if not supplied)
1965 @return: list of Architecture objects for the given name (may be empty)
1968 q = session.query(Architecture)
1969 q = q.join(SuiteArchitecture)
1970 q = q.join(Suite).filter_by(suite_name=suite)
1973 q = q.filter(Architecture.arch_string != 'source')
1976 q = q.filter(Architecture.arch_string != 'all')
1978 q = q.order_by('arch_string')
1982 __all__.append('get_suite_architectures')
1984 ################################################################################
1986 class SuiteSrcFormat(object):
1987 def __init__(self, *args, **kwargs):
1991 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1993 __all__.append('SuiteSrcFormat')
1995 def get_suite_src_formats(suite, session=None):
1997 Returns list of allowed SrcFormat for C{suite}.
2000 @param suite: Suite name to search for
2002 @type session: Session
2003 @param session: Optional SQL session object (a temporary one will be
2004 generated if not supplied)
2007 @return: the list of allowed source formats for I{suite}
2010 privatetrans = False
2012 session = DBConn().session()
2015 q = session.query(SrcFormat)
2016 q = q.join(SuiteSrcFormat)
2017 q = q.join(Suite).filter_by(suite_name=suite)
2018 q = q.order_by('format_name')
2027 __all__.append('get_suite_src_formats')
2029 ################################################################################
2032 def __init__(self, *args, **kwargs):
2035 def __eq__(self, val):
2036 if isinstance(val, str):
2037 return (self.uid == val)
2038 # This signals to use the normal comparison operator
2039 return NotImplemented
2041 def __ne__(self, val):
2042 if isinstance(val, str):
2043 return (self.uid != val)
2044 # This signals to use the normal comparison operator
2045 return NotImplemented
2048 return '<Uid %s (%s)>' % (self.uid, self.name)
2050 __all__.append('Uid')
2052 def add_database_user(uidname, session=None):
2054 Adds a database user
2056 @type uidname: string
2057 @param uidname: The uid of the user to add
2059 @type session: SQLAlchemy
2060 @param session: Optional SQL session object (a temporary one will be
2061 generated if not supplied). If not passed, a commit will be performed at
2062 the end of the function, otherwise the caller is responsible for commiting.
2065 @return: the uid object for the given uidname
2068 privatetrans = False
2070 session = DBConn().session()
2073 session.execute("CREATE USER :uid", {'uid': uidname})
2079 __all__.append('add_database_user')
2081 def get_or_set_uid(uidname, session=None):
2083 Returns uid object for given uidname.
2085 If no matching uidname is found, a row is inserted.
2087 @type uidname: string
2088 @param uidname: The uid to add
2090 @type session: SQLAlchemy
2091 @param session: Optional SQL session object (a temporary one will be
2092 generated if not supplied). If not passed, a commit will be performed at
2093 the end of the function, otherwise the caller is responsible for commiting.
2096 @return: the uid object for the given uidname
2099 privatetrans = False
2101 session = DBConn().session()
2104 q = session.query(Uid).filter_by(uid=uidname)
2108 except NoResultFound:
2123 __all__.append('get_or_set_uid')
2126 def get_uid_from_fingerprint(fpr, session=None):
2127 q = session.query(Uid)
2128 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2132 except NoResultFound:
2135 __all__.append('get_uid_from_fingerprint')
2137 ################################################################################
2139 class DBConn(Singleton):
2141 database module init.
2143 def __init__(self, *args, **kwargs):
2144 super(DBConn, self).__init__(*args, **kwargs)
2146 def _startup(self, *args, **kwargs):
2148 if kwargs.has_key('debug'):
2152 def __setuptables(self):
2153 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2154 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2155 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2156 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2157 self.tbl_component = Table('component', self.db_meta, autoload=True)
2158 self.tbl_config = Table('config', self.db_meta, autoload=True)
2159 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2160 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2161 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2162 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2163 self.tbl_files = Table('files', self.db_meta, autoload=True)
2164 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2165 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2166 self.tbl_location = Table('location', self.db_meta, autoload=True)
2167 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2168 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2169 self.tbl_override = Table('override', self.db_meta, autoload=True)
2170 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2171 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2172 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2173 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2174 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2175 self.tbl_section = Table('section', self.db_meta, autoload=True)
2176 self.tbl_source = Table('source', self.db_meta, autoload=True)
2177 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2178 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2179 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2180 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2181 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2182 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2183 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2185 def __setupmappers(self):
2186 mapper(Architecture, self.tbl_architecture,
2187 properties = dict(arch_id = self.tbl_architecture.c.id))
2189 mapper(Archive, self.tbl_archive,
2190 properties = dict(archive_id = self.tbl_archive.c.id,
2191 archive_name = self.tbl_archive.c.name))
2193 mapper(BinAssociation, self.tbl_bin_associations,
2194 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2195 suite_id = self.tbl_bin_associations.c.suite,
2196 suite = relation(Suite),
2197 binary_id = self.tbl_bin_associations.c.bin,
2198 binary = relation(DBBinary)))
2200 mapper(DBBinary, self.tbl_binaries,
2201 properties = dict(binary_id = self.tbl_binaries.c.id,
2202 package = self.tbl_binaries.c.package,
2203 version = self.tbl_binaries.c.version,
2204 maintainer_id = self.tbl_binaries.c.maintainer,
2205 maintainer = relation(Maintainer),
2206 source_id = self.tbl_binaries.c.source,
2207 source = relation(DBSource),
2208 arch_id = self.tbl_binaries.c.architecture,
2209 architecture = relation(Architecture),
2210 poolfile_id = self.tbl_binaries.c.file,
2211 poolfile = relation(PoolFile),
2212 binarytype = self.tbl_binaries.c.type,
2213 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2214 fingerprint = relation(Fingerprint),
2215 install_date = self.tbl_binaries.c.install_date,
2216 binassociations = relation(BinAssociation,
2217 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2219 mapper(Component, self.tbl_component,
2220 properties = dict(component_id = self.tbl_component.c.id,
2221 component_name = self.tbl_component.c.name))
2223 mapper(DBConfig, self.tbl_config,
2224 properties = dict(config_id = self.tbl_config.c.id))
2226 mapper(ContentAssociation, self.tbl_content_associations,
2227 properties = dict(ca_id = self.tbl_content_associations.c.id,
2228 filename_id = self.tbl_content_associations.c.filename,
2229 filename = relation(ContentFilename),
2230 filepath_id = self.tbl_content_associations.c.filepath,
2231 filepath = relation(ContentFilepath),
2232 binary_id = self.tbl_content_associations.c.binary_pkg,
2233 binary = relation(DBBinary)))
2236 mapper(ContentFilename, self.tbl_content_file_names,
2237 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2238 filename = self.tbl_content_file_names.c.file))
2240 mapper(ContentFilepath, self.tbl_content_file_paths,
2241 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2242 filepath = self.tbl_content_file_paths.c.path))
2244 mapper(DSCFile, self.tbl_dsc_files,
2245 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2246 source_id = self.tbl_dsc_files.c.source,
2247 source = relation(DBSource),
2248 poolfile_id = self.tbl_dsc_files.c.file,
2249 poolfile = relation(PoolFile)))
2251 mapper(PoolFile, self.tbl_files,
2252 properties = dict(file_id = self.tbl_files.c.id,
2253 filesize = self.tbl_files.c.size,
2254 location_id = self.tbl_files.c.location,
2255 location = relation(Location)))
2257 mapper(Fingerprint, self.tbl_fingerprint,
2258 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2259 uid_id = self.tbl_fingerprint.c.uid,
2260 uid = relation(Uid),
2261 keyring_id = self.tbl_fingerprint.c.keyring,
2262 keyring = relation(Keyring)))
2264 mapper(Keyring, self.tbl_keyrings,
2265 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2266 keyring_id = self.tbl_keyrings.c.id))
2268 mapper(Location, self.tbl_location,
2269 properties = dict(location_id = self.tbl_location.c.id,
2270 component_id = self.tbl_location.c.component,
2271 component = relation(Component),
2272 archive_id = self.tbl_location.c.archive,
2273 archive = relation(Archive),
2274 archive_type = self.tbl_location.c.type))
2276 mapper(Maintainer, self.tbl_maintainer,
2277 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2279 mapper(NewComment, self.tbl_new_comments,
2280 properties = dict(comment_id = self.tbl_new_comments.c.id))
2282 mapper(Override, self.tbl_override,
2283 properties = dict(suite_id = self.tbl_override.c.suite,
2284 suite = relation(Suite),
2285 component_id = self.tbl_override.c.component,
2286 component = relation(Component),
2287 priority_id = self.tbl_override.c.priority,
2288 priority = relation(Priority),
2289 section_id = self.tbl_override.c.section,
2290 section = relation(Section),
2291 overridetype_id = self.tbl_override.c.type,
2292 overridetype = relation(OverrideType)))
2294 mapper(OverrideType, self.tbl_override_type,
2295 properties = dict(overridetype = self.tbl_override_type.c.type,
2296 overridetype_id = self.tbl_override_type.c.id))
2298 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2299 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2300 filepath_id = self.tbl_pending_content_associations.c.filepath,
2301 filepath = relation(ContentFilepath),
2302 filename_id = self.tbl_pending_content_associations.c.filename,
2303 filename = relation(ContentFilename)))
2305 mapper(Priority, self.tbl_priority,
2306 properties = dict(priority_id = self.tbl_priority.c.id))
2308 mapper(Queue, self.tbl_queue,
2309 properties = dict(queue_id = self.tbl_queue.c.id))
2311 mapper(QueueBuild, self.tbl_queue_build,
2312 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2313 queue_id = self.tbl_queue_build.c.queue,
2314 queue = relation(Queue, backref='queuebuild')))
2316 mapper(Section, self.tbl_section,
2317 properties = dict(section_id = self.tbl_section.c.id))
2319 mapper(DBSource, self.tbl_source,
2320 properties = dict(source_id = self.tbl_source.c.id,
2321 version = self.tbl_source.c.version,
2322 maintainer_id = self.tbl_source.c.maintainer,
2323 maintainer = relation(Maintainer,
2324 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2325 poolfile_id = self.tbl_source.c.file,
2326 poolfile = relation(PoolFile),
2327 fingerprint_id = self.tbl_source.c.sig_fpr,
2328 fingerprint = relation(Fingerprint),
2329 changedby_id = self.tbl_source.c.changedby,
2330 changedby = relation(Maintainer,
2331 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2332 srcfiles = relation(DSCFile,
2333 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2334 srcassociations = relation(SrcAssociation,
2335 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2337 mapper(SrcAssociation, self.tbl_src_associations,
2338 properties = dict(sa_id = self.tbl_src_associations.c.id,
2339 suite_id = self.tbl_src_associations.c.suite,
2340 suite = relation(Suite),
2341 source_id = self.tbl_src_associations.c.source,
2342 source = relation(DBSource)))
2344 mapper(SrcFormat, self.tbl_src_format,
2345 properties = dict(src_format_id = self.tbl_src_format.c.id,
2346 format_name = self.tbl_src_format.c.format_name))
2348 mapper(SrcUploader, self.tbl_src_uploaders,
2349 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2350 source_id = self.tbl_src_uploaders.c.source,
2351 source = relation(DBSource,
2352 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2353 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2354 maintainer = relation(Maintainer,
2355 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2357 mapper(Suite, self.tbl_suite,
2358 properties = dict(suite_id = self.tbl_suite.c.id))
2360 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2361 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2362 suite = relation(Suite, backref='suitearchitectures'),
2363 arch_id = self.tbl_suite_architectures.c.architecture,
2364 architecture = relation(Architecture)))
2366 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2367 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2368 suite = relation(Suite, backref='suitesrcformats'),
2369 src_format_id = self.tbl_suite_src_formats.c.src_format,
2370 src_format = relation(SrcFormat)))
2372 mapper(Uid, self.tbl_uid,
2373 properties = dict(uid_id = self.tbl_uid.c.id,
2374 fingerprint = relation(Fingerprint)))
2376 ## Connection functions
2377 def __createconn(self):
2378 from config import Config
2382 connstr = "postgres://%s" % cnf["DB::Host"]
2383 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2384 connstr += ":%s" % cnf["DB::Port"]
2385 connstr += "/%s" % cnf["DB::Name"]
2388 connstr = "postgres:///%s" % cnf["DB::Name"]
2389 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2390 connstr += "?port=%s" % cnf["DB::Port"]
2392 self.db_pg = create_engine(connstr, echo=self.debug)
2393 self.db_meta = MetaData()
2394 self.db_meta.bind = self.db_pg
2395 self.db_smaker = sessionmaker(bind=self.db_pg,
2399 self.__setuptables()
2400 self.__setupmappers()
2403 return self.db_smaker()
2405 __all__.append('DBConn')