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
52 from config import Config
53 from singleton import Singleton
54 from textutils import fix_maintainer
56 ################################################################################
58 __all__ = ['IntegrityError', 'SQLAlchemyError']
60 ################################################################################
62 def session_wrapper(fn):
63 def wrapped(*args, **kwargs):
64 private_transaction = False
65 session = kwargs.get('session')
67 # No session specified as last argument or in kwargs, create one.
68 if session is None and len(args) == len(getargspec(fn)[0]) - 1:
69 private_transaction = True
70 kwargs['session'] = DBConn().session()
73 return fn(*args, **kwargs)
75 if private_transaction:
76 # We created a session; close it.
77 kwargs['session'].close()
81 ################################################################################
83 class Architecture(object):
84 def __init__(self, *args, **kwargs):
87 def __eq__(self, val):
88 if isinstance(val, str):
89 return (self.arch_string== val)
90 # This signals to use the normal comparison operator
93 def __ne__(self, val):
94 if isinstance(val, str):
95 return (self.arch_string != val)
96 # This signals to use the normal comparison operator
100 return '<Architecture %s>' % self.arch_string
102 __all__.append('Architecture')
105 def get_architecture(architecture, session=None):
107 Returns database id for given C{architecture}.
109 @type architecture: string
110 @param architecture: The name of the architecture
112 @type session: Session
113 @param session: Optional SQLA session object (a temporary one will be
114 generated if not supplied)
117 @return: Architecture object for the given arch (None if not present)
120 q = session.query(Architecture).filter_by(arch_string=architecture)
124 except NoResultFound:
127 __all__.append('get_architecture')
130 def get_architecture_suites(architecture, session=None):
132 Returns list of Suite objects for given C{architecture} name
135 @param source: Architecture name to search for
137 @type session: Session
138 @param session: Optional SQL session object (a temporary one will be
139 generated if not supplied)
142 @return: list of Suite objects for the given name (may be empty)
145 q = session.query(Suite)
146 q = q.join(SuiteArchitecture)
147 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
153 __all__.append('get_architecture_suites')
155 ################################################################################
157 class Archive(object):
158 def __init__(self, *args, **kwargs):
162 return '<Archive %s>' % self.archive_name
164 __all__.append('Archive')
167 def get_archive(archive, session=None):
169 returns database id for given c{archive}.
171 @type archive: string
172 @param archive: the name of the arhive
174 @type session: Session
175 @param session: Optional SQLA session object (a temporary one will be
176 generated if not supplied)
179 @return: Archive object for the given name (None if not present)
182 archive = archive.lower()
184 q = session.query(Archive).filter_by(archive_name=archive)
188 except NoResultFound:
191 __all__.append('get_archive')
193 ################################################################################
195 class BinAssociation(object):
196 def __init__(self, *args, **kwargs):
200 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
202 __all__.append('BinAssociation')
204 ################################################################################
206 class DBBinary(object):
207 def __init__(self, *args, **kwargs):
211 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
213 __all__.append('DBBinary')
216 def get_suites_binary_in(package, session=None):
218 Returns list of Suite objects which given C{package} name is in
221 @param source: DBBinary package name to search for
224 @return: list of Suite objects for the given package
227 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
229 __all__.append('get_suites_binary_in')
232 def get_binary_from_id(id, session=None):
234 Returns DBBinary object for given C{id}
237 @param id: Id of the required binary
239 @type session: Session
240 @param session: Optional SQLA session object (a temporary one will be
241 generated if not supplied)
244 @return: DBBinary object for the given binary (None if not present)
247 q = session.query(DBBinary).filter_by(binary_id=id)
251 except NoResultFound:
254 __all__.append('get_binary_from_id')
257 def get_binaries_from_name(package, version=None, architecture=None, session=None):
259 Returns list of DBBinary objects for given C{package} name
262 @param package: DBBinary package name to search for
264 @type version: str or None
265 @param version: Version to search for (or None)
267 @type package: str, list or None
268 @param package: Architectures to limit to (or None if no limit)
270 @type session: Session
271 @param session: Optional SQL session object (a temporary one will be
272 generated if not supplied)
275 @return: list of DBBinary objects for the given name (may be empty)
278 q = session.query(DBBinary).filter_by(package=package)
280 if version is not None:
281 q = q.filter_by(version=version)
283 if architecture is not None:
284 if not isinstance(architecture, list):
285 architecture = [architecture]
286 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
292 __all__.append('get_binaries_from_name')
295 def get_binaries_from_source_id(source_id, session=None):
297 Returns list of DBBinary objects for given C{source_id}
300 @param source_id: source_id to search for
302 @type session: Session
303 @param session: Optional SQL session object (a temporary one will be
304 generated if not supplied)
307 @return: list of DBBinary objects for the given name (may be empty)
310 return session.query(DBBinary).filter_by(source_id=source_id).all()
312 __all__.append('get_binaries_from_source_id')
315 def get_binary_from_name_suite(package, suitename, session=None):
316 ### For dak examine-package
317 ### XXX: Doesn't use object API yet
319 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
320 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
321 WHERE b.package=:package
323 AND fi.location = l.id
324 AND l.component = c.id
327 AND su.suite_name=:suitename
328 ORDER BY b.version DESC"""
330 return session.execute(sql, {'package': package, 'suitename': suitename})
332 __all__.append('get_binary_from_name_suite')
335 def get_binary_components(package, suitename, arch, session=None):
336 # Check for packages that have moved from one component to another
337 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
338 WHERE b.package=:package AND s.suite_name=:suitename
339 AND (a.arch_string = :arch OR a.arch_string = 'all')
340 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
341 AND f.location = l.id
342 AND l.component = c.id
345 vals = {'package': package, 'suitename': suitename, 'arch': arch}
347 return session.execute(query, vals)
349 __all__.append('get_binary_components')
351 ################################################################################
353 class Component(object):
354 def __init__(self, *args, **kwargs):
357 def __eq__(self, val):
358 if isinstance(val, str):
359 return (self.component_name == val)
360 # This signals to use the normal comparison operator
361 return NotImplemented
363 def __ne__(self, val):
364 if isinstance(val, str):
365 return (self.component_name != val)
366 # This signals to use the normal comparison operator
367 return NotImplemented
370 return '<Component %s>' % self.component_name
373 __all__.append('Component')
376 def get_component(component, session=None):
378 Returns database id for given C{component}.
380 @type component: string
381 @param component: The name of the override type
384 @return: the database id for the given component
387 component = component.lower()
389 q = session.query(Component).filter_by(component_name=component)
393 except NoResultFound:
396 __all__.append('get_component')
398 ################################################################################
400 class DBConfig(object):
401 def __init__(self, *args, **kwargs):
405 return '<DBConfig %s>' % self.name
407 __all__.append('DBConfig')
409 ################################################################################
411 class ContentFilename(object):
412 def __init__(self, *args, **kwargs):
416 return '<ContentFilename %s>' % self.filename
418 __all__.append('ContentFilename')
420 def get_or_set_contents_file_id(filename, session=None):
422 Returns database id for given filename.
424 If no matching file is found, a row is inserted.
426 @type filename: string
427 @param filename: The filename
428 @type session: SQLAlchemy
429 @param session: Optional SQL session object (a temporary one will be
430 generated if not supplied). If not passed, a commit will be performed at
431 the end of the function, otherwise the caller is responsible for commiting.
434 @return: the database id for the given component
438 session = DBConn().session()
441 q = session.query(ContentFilename).filter_by(filename=filename)
444 ret = q.one().cafilename_id
445 except NoResultFound:
446 cf = ContentFilename()
447 cf.filename = filename
453 ret = cf.cafilename_id
460 __all__.append('get_or_set_contents_file_id')
463 def get_contents(suite, overridetype, section=None, session=None):
465 Returns contents for a suite / overridetype combination, limiting
466 to a section if not None.
469 @param suite: Suite object
471 @type overridetype: OverrideType
472 @param overridetype: OverrideType object
474 @type section: Section
475 @param section: Optional section object to limit results to
477 @type session: SQLAlchemy
478 @param session: Optional SQL session object (a temporary one will be
479 generated if not supplied)
482 @return: ResultsProxy object set up to return tuples of (filename, section,
486 # find me all of the contents for a given suite
487 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
491 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
492 JOIN content_file_names n ON (c.filename=n.id)
493 JOIN binaries b ON (b.id=c.binary_pkg)
494 JOIN override o ON (o.package=b.package)
495 JOIN section s ON (s.id=o.section)
496 WHERE o.suite = :suiteid AND o.type = :overridetypeid
497 AND b.type=:overridetypename"""
499 vals = {'suiteid': suite.suite_id,
500 'overridetypeid': overridetype.overridetype_id,
501 'overridetypename': overridetype.overridetype}
503 if section is not None:
504 contents_q += " AND s.id = :sectionid"
505 vals['sectionid'] = section.section_id
507 contents_q += " ORDER BY fn"
509 return session.execute(contents_q, vals)
511 __all__.append('get_contents')
513 ################################################################################
515 class ContentFilepath(object):
516 def __init__(self, *args, **kwargs):
520 return '<ContentFilepath %s>' % self.filepath
522 __all__.append('ContentFilepath')
524 def get_or_set_contents_path_id(filepath, session=None):
526 Returns database id for given path.
528 If no matching file is found, a row is inserted.
530 @type filename: string
531 @param filename: The filepath
532 @type session: SQLAlchemy
533 @param session: Optional SQL session object (a temporary one will be
534 generated if not supplied). If not passed, a commit will be performed at
535 the end of the function, otherwise the caller is responsible for commiting.
538 @return: the database id for the given path
542 session = DBConn().session()
545 q = session.query(ContentFilepath).filter_by(filepath=filepath)
548 ret = q.one().cafilepath_id
549 except NoResultFound:
550 cf = ContentFilepath()
551 cf.filepath = filepath
557 ret = cf.cafilepath_id
564 __all__.append('get_or_set_contents_path_id')
566 ################################################################################
568 class ContentAssociation(object):
569 def __init__(self, *args, **kwargs):
573 return '<ContentAssociation %s>' % self.ca_id
575 __all__.append('ContentAssociation')
577 def insert_content_paths(binary_id, fullpaths, session=None):
579 Make sure given path is associated with given binary id
582 @param binary_id: the id of the binary
583 @type fullpaths: list
584 @param fullpaths: the list of paths of the file being associated with the binary
585 @type session: SQLAlchemy session
586 @param session: Optional SQLAlchemy session. If this is passed, the caller
587 is responsible for ensuring a transaction has begun and committing the
588 results or rolling back based on the result code. If not passed, a commit
589 will be performed at the end of the function, otherwise the caller is
590 responsible for commiting.
592 @return: True upon success
597 session = DBConn().session()
603 for fullpath in fullpaths:
604 # Get the necessary IDs ...
605 (path, file) = os.path.split(fullpath)
607 filepath_id = get_or_set_contents_path_id(path, session)
608 filename_id = get_or_set_contents_file_id(file, session)
610 pathcache[fullpath] = (filepath_id, filename_id)
612 for fullpath, dat in pathcache.items():
613 ca = ContentAssociation()
614 ca.binary_id = binary_id
615 ca.filepath_id = dat[0]
616 ca.filename_id = dat[1]
619 # Only commit if we set up the session ourself
629 traceback.print_exc()
631 # Only rollback if we set up the session ourself
638 __all__.append('insert_content_paths')
640 ################################################################################
642 class DSCFile(object):
643 def __init__(self, *args, **kwargs):
647 return '<DSCFile %s>' % self.dscfile_id
649 __all__.append('DSCFile')
652 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
654 Returns a list of DSCFiles which may be empty
656 @type dscfile_id: int (optional)
657 @param dscfile_id: the dscfile_id of the DSCFiles to find
659 @type source_id: int (optional)
660 @param source_id: the source id related to the DSCFiles to find
662 @type poolfile_id: int (optional)
663 @param poolfile_id: the poolfile id related to the DSCFiles to find
666 @return: Possibly empty list of DSCFiles
669 q = session.query(DSCFile)
671 if dscfile_id is not None:
672 q = q.filter_by(dscfile_id=dscfile_id)
674 if source_id is not None:
675 q = q.filter_by(source_id=source_id)
677 if poolfile_id is not None:
678 q = q.filter_by(poolfile_id=poolfile_id)
682 __all__.append('get_dscfiles')
684 ################################################################################
686 class PoolFile(object):
687 def __init__(self, *args, **kwargs):
691 return '<PoolFile %s>' % self.filename
693 __all__.append('PoolFile')
696 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
699 (ValidFileFound [boolean or None], PoolFile object or None)
701 @type filename: string
702 @param filename: the filename of the file to check against the DB
705 @param filesize: the size of the file to check against the DB
708 @param md5sum: the md5sum of the file to check against the DB
710 @type location_id: int
711 @param location_id: the id of the location to look in
714 @return: Tuple of length 2.
715 If more than one file found with that name:
717 If valid pool file found: (True, PoolFile object)
718 If valid pool file not found:
719 (False, None) if no file found
720 (False, PoolFile object) if file found with size/md5sum mismatch
723 q = session.query(PoolFile).filter_by(filename=filename)
724 q = q.join(Location).filter_by(location_id=location_id)
734 if obj.md5sum != md5sum or obj.filesize != filesize:
742 __all__.append('check_poolfile')
745 def get_poolfile_by_id(file_id, session=None):
747 Returns a PoolFile objects or None for the given id
750 @param file_id: the id of the file to look for
752 @rtype: PoolFile or None
753 @return: either the PoolFile object or None
756 q = session.query(PoolFile).filter_by(file_id=file_id)
760 except NoResultFound:
763 __all__.append('get_poolfile_by_id')
767 def get_poolfile_by_name(filename, location_id=None, session=None):
769 Returns an array of PoolFile objects for the given filename and
770 (optionally) location_id
772 @type filename: string
773 @param filename: the filename of the file to check against the DB
775 @type location_id: int
776 @param location_id: the id of the location to look in (optional)
779 @return: array of PoolFile objects
782 q = session.query(PoolFile).filter_by(filename=filename)
784 if location_id is not None:
785 q = q.join(Location).filter_by(location_id=location_id)
789 __all__.append('get_poolfile_by_name')
792 def get_poolfile_like_name(filename, session=None):
794 Returns an array of PoolFile objects which are like the given name
796 @type filename: string
797 @param filename: the filename of the file to check against the DB
800 @return: array of PoolFile objects
803 # TODO: There must be a way of properly using bind parameters with %FOO%
804 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
808 __all__.append('get_poolfile_like_name')
810 ################################################################################
812 class Fingerprint(object):
813 def __init__(self, *args, **kwargs):
817 return '<Fingerprint %s>' % self.fingerprint
819 __all__.append('Fingerprint')
821 def get_or_set_fingerprint(fpr, session=None):
823 Returns Fingerprint object for given fpr.
825 If no matching fpr is found, a row is inserted.
828 @param fpr: The fpr to find / add
830 @type session: SQLAlchemy
831 @param session: Optional SQL session object (a temporary one will be
832 generated if not supplied). If not passed, a commit will be performed at
833 the end of the function, otherwise the caller is responsible for commiting.
834 A flush will be performed either way.
837 @return: the Fingerprint object for the given fpr
841 session = DBConn().session()
844 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
848 except NoResultFound:
849 fingerprint = Fingerprint()
850 fingerprint.fingerprint = fpr
851 session.add(fingerprint)
863 __all__.append('get_or_set_fingerprint')
865 ################################################################################
867 class Keyring(object):
868 def __init__(self, *args, **kwargs):
872 return '<Keyring %s>' % self.keyring_name
874 __all__.append('Keyring')
876 def get_or_set_keyring(keyring, session=None):
878 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
879 and return the new Keyring
880 If C{keyring} already has an entry, simply return the existing Keyring
882 @type keyring: string
883 @param keyring: the keyring name
886 @return: the Keyring object for this keyring
891 session = DBConn().session()
895 obj = session.query(Keyring).filter_by(keyring_name=keyring).first()
898 obj = Keyring(keyring_name=keyring)
910 __all__.append('get_or_set_keyring')
912 ################################################################################
914 class Location(object):
915 def __init__(self, *args, **kwargs):
919 return '<Location %s (%s)>' % (self.path, self.location_id)
921 __all__.append('Location')
924 def get_location(location, component=None, archive=None, session=None):
926 Returns Location object for the given combination of location, component
929 @type location: string
930 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
932 @type component: string
933 @param component: the component name (if None, no restriction applied)
935 @type archive: string
936 @param archive_id: the archive name (if None, no restriction applied)
938 @rtype: Location / None
939 @return: Either a Location object or None if one can't be found
942 q = session.query(Location).filter_by(path=location)
944 if archive is not None:
945 q = q.join(Archive).filter_by(archive_name=archive)
947 if component is not None:
948 q = q.join(Component).filter_by(component_name=component)
952 except NoResultFound:
955 __all__.append('get_location')
957 ################################################################################
959 class Maintainer(object):
960 def __init__(self, *args, **kwargs):
964 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
966 def get_split_maintainer(self):
967 if not hasattr(self, 'name') or self.name is None:
968 return ('', '', '', '')
970 return fix_maintainer(self.name.strip())
972 __all__.append('Maintainer')
974 def get_or_set_maintainer(name, session=None):
976 Returns Maintainer object for given maintainer name.
978 If no matching maintainer name is found, a row is inserted.
981 @param name: The maintainer name to add
983 @type session: SQLAlchemy
984 @param session: Optional SQL session object (a temporary one will be
985 generated if not supplied). If not passed, a commit will be performed at
986 the end of the function, otherwise the caller is responsible for commiting.
987 A flush will be performed either way.
990 @return: the Maintainer object for the given maintainer
994 session = DBConn().session()
997 q = session.query(Maintainer).filter_by(name=name)
1000 except NoResultFound:
1001 maintainer = Maintainer()
1002 maintainer.name = name
1003 session.add(maintainer)
1015 __all__.append('get_or_set_maintainer')
1017 def get_maintainer(maintainer_id, session=None):
1019 Return the name of the maintainer behind C{maintainer_id} or None if that
1020 maintainer_id is invalid.
1022 @type maintainer_id: int
1023 @param maintainer_id: the id of the maintainer
1026 @return: the Maintainer with this C{maintainer_id}
1029 privatetrans = False
1031 session = DBConn().session()
1035 return session.query(Maintainer).get(maintainer_id)
1040 __all__.append('get_maintainer')
1042 ################################################################################
1044 class NewComment(object):
1045 def __init__(self, *args, **kwargs):
1049 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1051 __all__.append('NewComment')
1054 def has_new_comment(package, version, session=None):
1056 Returns true if the given combination of C{package}, C{version} has a comment.
1058 @type package: string
1059 @param package: name of the package
1061 @type version: string
1062 @param version: package version
1064 @type session: Session
1065 @param session: Optional SQLA session object (a temporary one will be
1066 generated if not supplied)
1072 q = session.query(NewComment)
1073 q = q.filter_by(package=package)
1074 q = q.filter_by(version=version)
1076 return bool(q.count() > 0)
1078 __all__.append('has_new_comment')
1081 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1083 Returns (possibly empty) list of NewComment objects for the given
1086 @type package: string (optional)
1087 @param package: name of the package
1089 @type version: string (optional)
1090 @param version: package version
1092 @type comment_id: int (optional)
1093 @param comment_id: An id of a comment
1095 @type session: Session
1096 @param session: Optional SQLA session object (a temporary one will be
1097 generated if not supplied)
1100 @return: A (possibly empty) list of NewComment objects will be returned
1103 q = session.query(NewComment)
1104 if package is not None: q = q.filter_by(package=package)
1105 if version is not None: q = q.filter_by(version=version)
1106 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1110 __all__.append('get_new_comments')
1112 ################################################################################
1114 class Override(object):
1115 def __init__(self, *args, **kwargs):
1119 return '<Override %s (%s)>' % (self.package, self.suite_id)
1121 __all__.append('Override')
1124 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1126 Returns Override object for the given parameters
1128 @type package: string
1129 @param package: The name of the package
1131 @type suite: string, list or None
1132 @param suite: The name of the suite (or suites if a list) to limit to. If
1133 None, don't limit. Defaults to None.
1135 @type component: string, list or None
1136 @param component: The name of the component (or components if a list) to
1137 limit to. If None, don't limit. Defaults to None.
1139 @type overridetype: string, list or None
1140 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1141 limit to. If None, don't limit. Defaults to None.
1143 @type session: Session
1144 @param session: Optional SQLA session object (a temporary one will be
1145 generated if not supplied)
1148 @return: A (possibly empty) list of Override objects will be returned
1151 q = session.query(Override)
1152 q = q.filter_by(package=package)
1154 if suite is not None:
1155 if not isinstance(suite, list): suite = [suite]
1156 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1158 if component is not None:
1159 if not isinstance(component, list): component = [component]
1160 q = q.join(Component).filter(Component.component_name.in_(component))
1162 if overridetype is not None:
1163 if not isinstance(overridetype, list): overridetype = [overridetype]
1164 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1168 __all__.append('get_override')
1171 ################################################################################
1173 class OverrideType(object):
1174 def __init__(self, *args, **kwargs):
1178 return '<OverrideType %s>' % self.overridetype
1180 __all__.append('OverrideType')
1183 def get_override_type(override_type, session=None):
1185 Returns OverrideType object for given C{override type}.
1187 @type override_type: string
1188 @param override_type: The name of the override type
1190 @type session: Session
1191 @param session: Optional SQLA session object (a temporary one will be
1192 generated if not supplied)
1195 @return: the database id for the given override type
1198 q = session.query(OverrideType).filter_by(overridetype=override_type)
1202 except NoResultFound:
1205 __all__.append('get_override_type')
1207 ################################################################################
1209 class PendingContentAssociation(object):
1210 def __init__(self, *args, **kwargs):
1214 return '<PendingContentAssociation %s>' % self.pca_id
1216 __all__.append('PendingContentAssociation')
1218 def insert_pending_content_paths(package, fullpaths, session=None):
1220 Make sure given paths are temporarily associated with given
1224 @param package: the package to associate with should have been read in from the binary control file
1225 @type fullpaths: list
1226 @param fullpaths: the list of paths of the file being associated with the binary
1227 @type session: SQLAlchemy session
1228 @param session: Optional SQLAlchemy session. If this is passed, the caller
1229 is responsible for ensuring a transaction has begun and committing the
1230 results or rolling back based on the result code. If not passed, a commit
1231 will be performed at the end of the function
1233 @return: True upon success, False if there is a problem
1236 privatetrans = False
1239 session = DBConn().session()
1243 arch = get_architecture(package['Architecture'], session)
1244 arch_id = arch.arch_id
1246 # Remove any already existing recorded files for this package
1247 q = session.query(PendingContentAssociation)
1248 q = q.filter_by(package=package['Package'])
1249 q = q.filter_by(version=package['Version'])
1250 q = q.filter_by(architecture=arch_id)
1255 for fullpath in fullpaths:
1256 (path, file) = os.path.split(fullpath)
1258 if path.startswith( "./" ):
1261 filepath_id = get_or_set_contents_path_id(path, session)
1262 filename_id = get_or_set_contents_file_id(file, session)
1264 pathcache[fullpath] = (filepath_id, filename_id)
1266 for fullpath, dat in pathcache.items():
1267 pca = PendingContentAssociation()
1268 pca.package = package['Package']
1269 pca.version = package['Version']
1270 pca.filepath_id = dat[0]
1271 pca.filename_id = dat[1]
1272 pca.architecture = arch_id
1275 # Only commit if we set up the session ourself
1283 except Exception, e:
1284 traceback.print_exc()
1286 # Only rollback if we set up the session ourself
1293 __all__.append('insert_pending_content_paths')
1295 ################################################################################
1297 class Priority(object):
1298 def __init__(self, *args, **kwargs):
1301 def __eq__(self, val):
1302 if isinstance(val, str):
1303 return (self.priority == val)
1304 # This signals to use the normal comparison operator
1305 return NotImplemented
1307 def __ne__(self, val):
1308 if isinstance(val, str):
1309 return (self.priority != val)
1310 # This signals to use the normal comparison operator
1311 return NotImplemented
1314 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1316 __all__.append('Priority')
1319 def get_priority(priority, session=None):
1321 Returns Priority object for given C{priority name}.
1323 @type priority: string
1324 @param priority: The name of the priority
1326 @type session: Session
1327 @param session: Optional SQLA session object (a temporary one will be
1328 generated if not supplied)
1331 @return: Priority object for the given priority
1334 q = session.query(Priority).filter_by(priority=priority)
1338 except NoResultFound:
1341 __all__.append('get_priority')
1344 def get_priorities(session=None):
1346 Returns dictionary of priority names -> id mappings
1348 @type session: Session
1349 @param session: Optional SQL session object (a temporary one will be
1350 generated if not supplied)
1353 @return: dictionary of priority names -> id mappings
1357 q = session.query(Priority)
1359 ret[x.priority] = x.priority_id
1363 __all__.append('get_priorities')
1365 ################################################################################
1367 class Queue(object):
1368 def __init__(self, *args, **kwargs):
1372 return '<Queue %s>' % self.queue_name
1374 def autobuild_upload(self, changes, srcpath, session=None):
1376 Update queue_build database table used for incoming autobuild support.
1378 @type changes: Changes
1379 @param changes: changes object for the upload to process
1381 @type srcpath: string
1382 @param srcpath: path for the queue file entries/link destinations
1384 @type session: SQLAlchemy session
1385 @param session: Optional SQLAlchemy session. If this is passed, the
1386 caller is responsible for ensuring a transaction has begun and
1387 committing the results or rolling back based on the result code. If
1388 not passed, a commit will be performed at the end of the function,
1389 otherwise the caller is responsible for commiting.
1391 @rtype: NoneType or string
1392 @return: None if the operation failed, a string describing the error if not
1395 privatetrans = False
1397 session = DBConn().session()
1400 # TODO: Remove by moving queue config into the database
1403 for suitename in changes.changes["distribution"].keys():
1404 # TODO: Move into database as:
1405 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1406 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1407 # This also gets rid of the SecurityQueueBuild hack below
1408 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1412 s = get_suite(suitename, session)
1414 return "INTERNAL ERROR: Could not find suite %s" % suitename
1416 # TODO: Get from database as above
1417 dest_dir = conf["Dir::QueueBuild"]
1419 # TODO: Move into database as above
1420 if conf.FindB("Dinstall::SecurityQueueBuild"):
1421 dest_dir = os.path.join(dest_dir, suitename)
1423 for file_entry in changes.files.keys():
1424 src = os.path.join(srcpath, file_entry)
1425 dest = os.path.join(dest_dir, file_entry)
1427 # TODO: Move into database as above
1428 if conf.FindB("Dinstall::SecurityQueueBuild"):
1429 # Copy it since the original won't be readable by www-data
1430 utils.copy(src, dest)
1432 # Create a symlink to it
1433 os.symlink(src, dest)
1436 qb.suite_id = s.suite_id
1437 qb.queue_id = self.queue_id
1443 # If the .orig.tar.gz is in the pool, create a symlink to
1444 # it (if one doesn't already exist)
1445 if changes.orig_tar_id:
1446 # Determine the .orig.tar.gz file name
1447 for dsc_file in changes.dsc_files.keys():
1448 if dsc_file.endswith(".orig.tar.gz"):
1451 dest = os.path.join(dest_dir, filename)
1453 # If it doesn't exist, create a symlink
1454 if not os.path.exists(dest):
1455 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1456 {'id': changes.orig_tar_id})
1459 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1461 src = os.path.join(res[0], res[1])
1462 os.symlink(src, dest)
1464 # Add it to the list of packages for later processing by apt-ftparchive
1466 qb.suite_id = s.suite_id
1467 qb.queue_id = self.queue_id
1472 # If it does, update things to ensure it's not removed prematurely
1474 qb = get_queue_build(dest, s.suite_id, session)
1486 __all__.append('Queue')
1489 def get_queue(queuename, session=None):
1491 Returns Queue object for given C{queue name}.
1493 @type queuename: string
1494 @param queuename: The name of the queue
1496 @type session: Session
1497 @param session: Optional SQLA session object (a temporary one will be
1498 generated if not supplied)
1501 @return: Queue object for the given queue
1504 q = session.query(Queue).filter_by(queue_name=queuename)
1508 except NoResultFound:
1511 __all__.append('get_queue')
1513 ################################################################################
1515 class QueueBuild(object):
1516 def __init__(self, *args, **kwargs):
1520 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1522 __all__.append('QueueBuild')
1525 def get_queue_build(filename, suite, session=None):
1527 Returns QueueBuild object for given C{filename} and C{suite}.
1529 @type filename: string
1530 @param filename: The name of the file
1532 @type suiteid: int or str
1533 @param suiteid: Suite name or ID
1535 @type session: Session
1536 @param session: Optional SQLA session object (a temporary one will be
1537 generated if not supplied)
1540 @return: Queue object for the given queue
1543 if isinstance(suite, int):
1544 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1546 q = session.query(QueueBuild).filter_by(filename=filename)
1547 q = q.join(Suite).filter_by(suite_name=suite)
1551 except NoResultFound:
1554 __all__.append('get_queue_build')
1556 ################################################################################
1558 class Section(object):
1559 def __init__(self, *args, **kwargs):
1562 def __eq__(self, val):
1563 if isinstance(val, str):
1564 return (self.section == val)
1565 # This signals to use the normal comparison operator
1566 return NotImplemented
1568 def __ne__(self, val):
1569 if isinstance(val, str):
1570 return (self.section != val)
1571 # This signals to use the normal comparison operator
1572 return NotImplemented
1575 return '<Section %s>' % self.section
1577 __all__.append('Section')
1580 def get_section(section, session=None):
1582 Returns Section object for given C{section name}.
1584 @type section: string
1585 @param section: The name of the section
1587 @type session: Session
1588 @param session: Optional SQLA session object (a temporary one will be
1589 generated if not supplied)
1592 @return: Section object for the given section name
1595 q = session.query(Section).filter_by(section=section)
1599 except NoResultFound:
1602 __all__.append('get_section')
1605 def get_sections(session=None):
1607 Returns dictionary of section names -> id mappings
1609 @type session: Session
1610 @param session: Optional SQL session object (a temporary one will be
1611 generated if not supplied)
1614 @return: dictionary of section names -> id mappings
1618 q = session.query(Section)
1620 ret[x.section] = x.section_id
1624 __all__.append('get_sections')
1626 ################################################################################
1628 class DBSource(object):
1629 def __init__(self, *args, **kwargs):
1633 return '<DBSource %s (%s)>' % (self.source, self.version)
1635 __all__.append('DBSource')
1638 def source_exists(source, source_version, suites = ["any"], session=None):
1640 Ensure that source exists somewhere in the archive for the binary
1641 upload being processed.
1642 1. exact match => 1.0-3
1643 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1645 @type package: string
1646 @param package: package source name
1648 @type source_version: string
1649 @param source_version: expected source version
1652 @param suites: list of suites to check in, default I{any}
1654 @type session: Session
1655 @param session: Optional SQLA session object (a temporary one will be
1656 generated if not supplied)
1659 @return: returns 1 if a source with expected version is found, otherwise 0
1666 for suite in suites:
1667 q = session.query(DBSource).filter_by(source=source)
1669 # source must exist in suite X, or in some other suite that's
1670 # mapped to X, recursively... silent-maps are counted too,
1671 # unreleased-maps aren't.
1672 maps = cnf.ValueList("SuiteMappings")[:]
1674 maps = [ m.split() for m in maps ]
1675 maps = [ (x[1], x[2]) for x in maps
1676 if x[0] == "map" or x[0] == "silent-map" ]
1679 if x[1] in s and x[0] not in s:
1682 q = q.join(SrcAssociation).join(Suite)
1683 q = q.filter(Suite.suite_name.in_(s))
1685 # Reduce the query results to a list of version numbers
1686 ql = [ j.version for j in q.all() ]
1689 if source_version in ql:
1693 from daklib.regexes import re_bin_only_nmu
1694 orig_source_version = re_bin_only_nmu.sub('', source_version)
1695 if orig_source_version in ql:
1698 # No source found so return not ok
1703 __all__.append('source_exists')
1706 def get_suites_source_in(source, session=None):
1708 Returns list of Suite objects which given C{source} name is in
1711 @param source: DBSource package name to search for
1714 @return: list of Suite objects for the given source
1717 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1719 __all__.append('get_suites_source_in')
1722 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1724 Returns list of DBSource objects for given C{source} name and other parameters
1727 @param source: DBSource package name to search for
1729 @type source: str or None
1730 @param source: DBSource version name to search for or None if not applicable
1732 @type dm_upload_allowed: bool
1733 @param dm_upload_allowed: If None, no effect. If True or False, only
1734 return packages with that dm_upload_allowed setting
1736 @type session: Session
1737 @param session: Optional SQL session object (a temporary one will be
1738 generated if not supplied)
1741 @return: list of DBSource objects for the given name (may be empty)
1744 q = session.query(DBSource).filter_by(source=source)
1746 if version is not None:
1747 q = q.filter_by(version=version)
1749 if dm_upload_allowed is not None:
1750 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1754 __all__.append('get_sources_from_name')
1757 def get_source_in_suite(source, suite, session=None):
1759 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1761 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1762 - B{suite} - a suite name, eg. I{unstable}
1764 @type source: string
1765 @param source: source package name
1768 @param suite: the suite name
1771 @return: the version for I{source} in I{suite}
1775 q = session.query(SrcAssociation)
1776 q = q.join('source').filter_by(source=source)
1777 q = q.join('suite').filter_by(suite_name=suite)
1780 return q.one().source
1781 except NoResultFound:
1784 __all__.append('get_source_in_suite')
1786 ################################################################################
1788 class SrcAssociation(object):
1789 def __init__(self, *args, **kwargs):
1793 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1795 __all__.append('SrcAssociation')
1797 ################################################################################
1799 class SrcUploader(object):
1800 def __init__(self, *args, **kwargs):
1804 return '<SrcUploader %s>' % self.uploader_id
1806 __all__.append('SrcUploader')
1808 ################################################################################
1810 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1811 ('SuiteID', 'suite_id'),
1812 ('Version', 'version'),
1813 ('Origin', 'origin'),
1815 ('Description', 'description'),
1816 ('Untouchable', 'untouchable'),
1817 ('Announce', 'announce'),
1818 ('Codename', 'codename'),
1819 ('OverrideCodename', 'overridecodename'),
1820 ('ValidTime', 'validtime'),
1821 ('Priority', 'priority'),
1822 ('NotAutomatic', 'notautomatic'),
1823 ('CopyChanges', 'copychanges'),
1824 ('CopyDotDak', 'copydotdak'),
1825 ('CommentsDir', 'commentsdir'),
1826 ('OverrideSuite', 'overridesuite'),
1827 ('ChangelogBase', 'changelogbase')]
1830 class Suite(object):
1831 def __init__(self, *args, **kwargs):
1835 return '<Suite %s>' % self.suite_name
1837 def __eq__(self, val):
1838 if isinstance(val, str):
1839 return (self.suite_name == val)
1840 # This signals to use the normal comparison operator
1841 return NotImplemented
1843 def __ne__(self, val):
1844 if isinstance(val, str):
1845 return (self.suite_name != val)
1846 # This signals to use the normal comparison operator
1847 return NotImplemented
1851 for disp, field in SUITE_FIELDS:
1852 val = getattr(self, field, None)
1854 ret.append("%s: %s" % (disp, val))
1856 return "\n".join(ret)
1858 __all__.append('Suite')
1861 def get_suite_architecture(suite, architecture, session=None):
1863 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1867 @param suite: Suite name to search for
1869 @type architecture: str
1870 @param architecture: Architecture name to search for
1872 @type session: Session
1873 @param session: Optional SQL session object (a temporary one will be
1874 generated if not supplied)
1876 @rtype: SuiteArchitecture
1877 @return: the SuiteArchitecture object or None
1880 q = session.query(SuiteArchitecture)
1881 q = q.join(Architecture).filter_by(arch_string=architecture)
1882 q = q.join(Suite).filter_by(suite_name=suite)
1886 except NoResultFound:
1889 __all__.append('get_suite_architecture')
1892 def get_suite(suite, session=None):
1894 Returns Suite object for given C{suite name}.
1897 @param suite: The name of the suite
1899 @type session: Session
1900 @param session: Optional SQLA session object (a temporary one will be
1901 generated if not supplied)
1904 @return: Suite object for the requested suite name (None if not presenT)
1907 q = session.query(Suite).filter_by(suite_name=suite)
1911 except NoResultFound:
1914 __all__.append('get_suite')
1916 ################################################################################
1918 class SuiteArchitecture(object):
1919 def __init__(self, *args, **kwargs):
1923 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1925 __all__.append('SuiteArchitecture')
1928 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1930 Returns list of Architecture objects for given C{suite} name
1933 @param source: Suite name to search for
1935 @type skipsrc: boolean
1936 @param skipsrc: Whether to skip returning the 'source' architecture entry
1939 @type skipall: boolean
1940 @param skipall: Whether to skip returning the 'all' architecture entry
1943 @type session: Session
1944 @param session: Optional SQL session object (a temporary one will be
1945 generated if not supplied)
1948 @return: list of Architecture objects for the given name (may be empty)
1951 q = session.query(Architecture)
1952 q = q.join(SuiteArchitecture)
1953 q = q.join(Suite).filter_by(suite_name=suite)
1956 q = q.filter(Architecture.arch_string != 'source')
1959 q = q.filter(Architecture.arch_string != 'all')
1961 q = q.order_by('arch_string')
1965 __all__.append('get_suite_architectures')
1967 ################################################################################
1970 def __init__(self, *args, **kwargs):
1973 def __eq__(self, val):
1974 if isinstance(val, str):
1975 return (self.uid == val)
1976 # This signals to use the normal comparison operator
1977 return NotImplemented
1979 def __ne__(self, val):
1980 if isinstance(val, str):
1981 return (self.uid != val)
1982 # This signals to use the normal comparison operator
1983 return NotImplemented
1986 return '<Uid %s (%s)>' % (self.uid, self.name)
1988 __all__.append('Uid')
1990 def add_database_user(uidname, session=None):
1992 Adds a database user
1994 @type uidname: string
1995 @param uidname: The uid of the user to add
1997 @type session: SQLAlchemy
1998 @param session: Optional SQL session object (a temporary one will be
1999 generated if not supplied). If not passed, a commit will be performed at
2000 the end of the function, otherwise the caller is responsible for commiting.
2003 @return: the uid object for the given uidname
2006 privatetrans = False
2008 session = DBConn().session()
2011 session.execute("CREATE USER :uid", {'uid': uidname})
2017 __all__.append('add_database_user')
2019 def get_or_set_uid(uidname, session=None):
2021 Returns uid object for given uidname.
2023 If no matching uidname is found, a row is inserted.
2025 @type uidname: string
2026 @param uidname: The uid to add
2028 @type session: SQLAlchemy
2029 @param session: Optional SQL session object (a temporary one will be
2030 generated if not supplied). If not passed, a commit will be performed at
2031 the end of the function, otherwise the caller is responsible for commiting.
2034 @return: the uid object for the given uidname
2037 privatetrans = False
2039 session = DBConn().session()
2042 q = session.query(Uid).filter_by(uid=uidname)
2046 except NoResultFound:
2061 __all__.append('get_or_set_uid')
2064 def get_uid_from_fingerprint(fpr, session=None):
2065 q = session.query(Uid)
2066 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2070 except NoResultFound:
2073 __all__.append('get_uid_from_fingerprint')
2075 ################################################################################
2077 class DBConn(Singleton):
2079 database module init.
2081 def __init__(self, *args, **kwargs):
2082 super(DBConn, self).__init__(*args, **kwargs)
2084 def _startup(self, *args, **kwargs):
2086 if kwargs.has_key('debug'):
2090 def __setuptables(self):
2091 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2092 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2093 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2094 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2095 self.tbl_component = Table('component', self.db_meta, autoload=True)
2096 self.tbl_config = Table('config', self.db_meta, autoload=True)
2097 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2098 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2099 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2100 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2101 self.tbl_files = Table('files', self.db_meta, autoload=True)
2102 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2103 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2104 self.tbl_location = Table('location', self.db_meta, autoload=True)
2105 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2106 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2107 self.tbl_override = Table('override', self.db_meta, autoload=True)
2108 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2109 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2110 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2111 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2112 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2113 self.tbl_section = Table('section', self.db_meta, autoload=True)
2114 self.tbl_source = Table('source', self.db_meta, autoload=True)
2115 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2116 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2117 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2118 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2119 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2121 def __setupmappers(self):
2122 mapper(Architecture, self.tbl_architecture,
2123 properties = dict(arch_id = self.tbl_architecture.c.id))
2125 mapper(Archive, self.tbl_archive,
2126 properties = dict(archive_id = self.tbl_archive.c.id,
2127 archive_name = self.tbl_archive.c.name))
2129 mapper(BinAssociation, self.tbl_bin_associations,
2130 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2131 suite_id = self.tbl_bin_associations.c.suite,
2132 suite = relation(Suite),
2133 binary_id = self.tbl_bin_associations.c.bin,
2134 binary = relation(DBBinary)))
2136 mapper(DBBinary, self.tbl_binaries,
2137 properties = dict(binary_id = self.tbl_binaries.c.id,
2138 package = self.tbl_binaries.c.package,
2139 version = self.tbl_binaries.c.version,
2140 maintainer_id = self.tbl_binaries.c.maintainer,
2141 maintainer = relation(Maintainer),
2142 source_id = self.tbl_binaries.c.source,
2143 source = relation(DBSource),
2144 arch_id = self.tbl_binaries.c.architecture,
2145 architecture = relation(Architecture),
2146 poolfile_id = self.tbl_binaries.c.file,
2147 poolfile = relation(PoolFile),
2148 binarytype = self.tbl_binaries.c.type,
2149 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2150 fingerprint = relation(Fingerprint),
2151 install_date = self.tbl_binaries.c.install_date,
2152 binassociations = relation(BinAssociation,
2153 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2155 mapper(Component, self.tbl_component,
2156 properties = dict(component_id = self.tbl_component.c.id,
2157 component_name = self.tbl_component.c.name))
2159 mapper(DBConfig, self.tbl_config,
2160 properties = dict(config_id = self.tbl_config.c.id))
2162 mapper(ContentAssociation, self.tbl_content_associations,
2163 properties = dict(ca_id = self.tbl_content_associations.c.id,
2164 filename_id = self.tbl_content_associations.c.filename,
2165 filename = relation(ContentFilename),
2166 filepath_id = self.tbl_content_associations.c.filepath,
2167 filepath = relation(ContentFilepath),
2168 binary_id = self.tbl_content_associations.c.binary_pkg,
2169 binary = relation(DBBinary)))
2172 mapper(ContentFilename, self.tbl_content_file_names,
2173 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2174 filename = self.tbl_content_file_names.c.file))
2176 mapper(ContentFilepath, self.tbl_content_file_paths,
2177 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2178 filepath = self.tbl_content_file_paths.c.path))
2180 mapper(DSCFile, self.tbl_dsc_files,
2181 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2182 source_id = self.tbl_dsc_files.c.source,
2183 source = relation(DBSource),
2184 poolfile_id = self.tbl_dsc_files.c.file,
2185 poolfile = relation(PoolFile)))
2187 mapper(PoolFile, self.tbl_files,
2188 properties = dict(file_id = self.tbl_files.c.id,
2189 filesize = self.tbl_files.c.size,
2190 location_id = self.tbl_files.c.location,
2191 location = relation(Location)))
2193 mapper(Fingerprint, self.tbl_fingerprint,
2194 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2195 uid_id = self.tbl_fingerprint.c.uid,
2196 uid = relation(Uid),
2197 keyring_id = self.tbl_fingerprint.c.keyring,
2198 keyring = relation(Keyring)))
2200 mapper(Keyring, self.tbl_keyrings,
2201 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2202 keyring_id = self.tbl_keyrings.c.id))
2204 mapper(Location, self.tbl_location,
2205 properties = dict(location_id = self.tbl_location.c.id,
2206 component_id = self.tbl_location.c.component,
2207 component = relation(Component),
2208 archive_id = self.tbl_location.c.archive,
2209 archive = relation(Archive),
2210 archive_type = self.tbl_location.c.type))
2212 mapper(Maintainer, self.tbl_maintainer,
2213 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2215 mapper(NewComment, self.tbl_new_comments,
2216 properties = dict(comment_id = self.tbl_new_comments.c.id))
2218 mapper(Override, self.tbl_override,
2219 properties = dict(suite_id = self.tbl_override.c.suite,
2220 suite = relation(Suite),
2221 component_id = self.tbl_override.c.component,
2222 component = relation(Component),
2223 priority_id = self.tbl_override.c.priority,
2224 priority = relation(Priority),
2225 section_id = self.tbl_override.c.section,
2226 section = relation(Section),
2227 overridetype_id = self.tbl_override.c.type,
2228 overridetype = relation(OverrideType)))
2230 mapper(OverrideType, self.tbl_override_type,
2231 properties = dict(overridetype = self.tbl_override_type.c.type,
2232 overridetype_id = self.tbl_override_type.c.id))
2234 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2235 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2236 filepath_id = self.tbl_pending_content_associations.c.filepath,
2237 filepath = relation(ContentFilepath),
2238 filename_id = self.tbl_pending_content_associations.c.filename,
2239 filename = relation(ContentFilename)))
2241 mapper(Priority, self.tbl_priority,
2242 properties = dict(priority_id = self.tbl_priority.c.id))
2244 mapper(Queue, self.tbl_queue,
2245 properties = dict(queue_id = self.tbl_queue.c.id))
2247 mapper(QueueBuild, self.tbl_queue_build,
2248 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2249 queue_id = self.tbl_queue_build.c.queue,
2250 queue = relation(Queue, backref='queuebuild')))
2252 mapper(Section, self.tbl_section,
2253 properties = dict(section_id = self.tbl_section.c.id))
2255 mapper(DBSource, self.tbl_source,
2256 properties = dict(source_id = self.tbl_source.c.id,
2257 version = self.tbl_source.c.version,
2258 maintainer_id = self.tbl_source.c.maintainer,
2259 maintainer = relation(Maintainer,
2260 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2261 poolfile_id = self.tbl_source.c.file,
2262 poolfile = relation(PoolFile),
2263 fingerprint_id = self.tbl_source.c.sig_fpr,
2264 fingerprint = relation(Fingerprint),
2265 changedby_id = self.tbl_source.c.changedby,
2266 changedby = relation(Maintainer,
2267 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2268 srcfiles = relation(DSCFile,
2269 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2270 srcassociations = relation(SrcAssociation,
2271 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2273 mapper(SrcAssociation, self.tbl_src_associations,
2274 properties = dict(sa_id = self.tbl_src_associations.c.id,
2275 suite_id = self.tbl_src_associations.c.suite,
2276 suite = relation(Suite),
2277 source_id = self.tbl_src_associations.c.source,
2278 source = relation(DBSource)))
2280 mapper(SrcUploader, self.tbl_src_uploaders,
2281 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2282 source_id = self.tbl_src_uploaders.c.source,
2283 source = relation(DBSource,
2284 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2285 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2286 maintainer = relation(Maintainer,
2287 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2289 mapper(Suite, self.tbl_suite,
2290 properties = dict(suite_id = self.tbl_suite.c.id))
2292 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2293 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2294 suite = relation(Suite, backref='suitearchitectures'),
2295 arch_id = self.tbl_suite_architectures.c.architecture,
2296 architecture = relation(Architecture)))
2298 mapper(Uid, self.tbl_uid,
2299 properties = dict(uid_id = self.tbl_uid.c.id,
2300 fingerprint = relation(Fingerprint)))
2302 ## Connection functions
2303 def __createconn(self):
2304 from config import Config
2308 connstr = "postgres://%s" % cnf["DB::Host"]
2309 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2310 connstr += ":%s" % cnf["DB::Port"]
2311 connstr += "/%s" % cnf["DB::Name"]
2314 connstr = "postgres:///%s" % cnf["DB::Name"]
2315 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2316 connstr += "?port=%s" % cnf["DB::Port"]
2318 self.db_pg = create_engine(connstr, echo=self.debug)
2319 self.db_meta = MetaData()
2320 self.db_meta.bind = self.db_pg
2321 self.db_smaker = sessionmaker(bind=self.db_pg,
2325 self.__setuptables()
2326 self.__setupmappers()
2329 return self.db_smaker()
2331 __all__.append('DBConn')