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 *
48 # Only import Config until Queue stuff is changed to store its config
50 from config import Config
51 from singleton import Singleton
52 from textutils import fix_maintainer
54 ################################################################################
56 __all__ = ['IntegrityError', 'SQLAlchemyError']
58 ################################################################################
60 def session_wrapper(fn):
61 def wrapped(*args, **kwargs):
62 private_transaction = False
63 session = kwargs.get('session')
65 # No session specified as last argument or in kwargs, create one.
66 if session is None and len(args) == len(getargspec(fn)[0]) - 1:
67 private_transaction = True
68 kwargs['session'] = DBConn().session()
71 return fn(*args, **kwargs)
73 if private_transaction:
74 # We created a session; close it.
75 kwargs['session'].close()
79 ################################################################################
81 class Architecture(object):
82 def __init__(self, *args, **kwargs):
85 def __eq__(self, val):
86 if isinstance(val, str):
87 return (self.arch_string== val)
88 # This signals to use the normal comparison operator
91 def __ne__(self, val):
92 if isinstance(val, str):
93 return (self.arch_string != val)
94 # This signals to use the normal comparison operator
98 return '<Architecture %s>' % self.arch_string
100 __all__.append('Architecture')
103 def get_architecture(architecture, session=None):
105 Returns database id for given C{architecture}.
107 @type architecture: string
108 @param architecture: The name of the architecture
110 @type session: Session
111 @param session: Optional SQLA session object (a temporary one will be
112 generated if not supplied)
115 @return: Architecture object for the given arch (None if not present)
118 q = session.query(Architecture).filter_by(arch_string=architecture)
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)
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 ret = session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
233 __all__.append('get_suites_binary_in')
236 def get_binary_from_id(id, session=None):
238 Returns DBBinary object for given C{id}
241 @param id: Id of the required binary
243 @type session: Session
244 @param session: Optional SQLA session object (a temporary one will be
245 generated if not supplied)
248 @return: DBBinary object for the given binary (None if not present)
251 q = session.query(DBBinary).filter_by(binary_id=id)
260 __all__.append('get_binary_from_id')
263 def get_binaries_from_name(package, version=None, architecture=None, session=None):
265 Returns list of DBBinary objects for given C{package} name
268 @param package: DBBinary package name to search for
270 @type version: str or None
271 @param version: Version to search for (or None)
273 @type package: str, list or None
274 @param package: Architectures to limit to (or None if no limit)
276 @type session: Session
277 @param session: Optional SQL session object (a temporary one will be
278 generated if not supplied)
281 @return: list of DBBinary objects for the given name (may be empty)
284 q = session.query(DBBinary).filter_by(package=package)
286 if version is not None:
287 q = q.filter_by(version=version)
289 if architecture is not None:
290 if not isinstance(architecture, list):
291 architecture = [architecture]
292 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
298 __all__.append('get_binaries_from_name')
301 def get_binaries_from_source_id(source_id, session=None):
303 Returns list of DBBinary objects for given C{source_id}
306 @param source_id: source_id to search for
308 @type session: Session
309 @param session: Optional SQL session object (a temporary one will be
310 generated if not supplied)
313 @return: list of DBBinary objects for the given name (may be empty)
316 ret = session.query(DBBinary).filter_by(source_id=source_id).all()
320 __all__.append('get_binaries_from_source_id')
323 def get_binary_from_name_suite(package, suitename, session=None):
324 ### For dak examine-package
325 ### XXX: Doesn't use object API yet
327 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
328 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
329 WHERE b.package=:package
331 AND fi.location = l.id
332 AND l.component = c.id
335 AND su.suite_name=:suitename
336 ORDER BY b.version DESC"""
338 ret = session.execute(sql, {'package': package, 'suitename': suitename})
342 __all__.append('get_binary_from_name_suite')
345 def get_binary_components(package, suitename, arch, session=None):
346 # Check for packages that have moved from one component to another
347 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
348 WHERE b.package=:package AND s.suite_name=:suitename
349 AND (a.arch_string = :arch OR a.arch_string = 'all')
350 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
351 AND f.location = l.id
352 AND l.component = c.id
355 vals = {'package': package, 'suitename': suitename, 'arch': arch}
357 ret = session.execute(query, vals)
361 __all__.append('get_binary_components')
363 ################################################################################
365 class Component(object):
366 def __init__(self, *args, **kwargs):
369 def __eq__(self, val):
370 if isinstance(val, str):
371 return (self.component_name == val)
372 # This signals to use the normal comparison operator
373 return NotImplemented
375 def __ne__(self, val):
376 if isinstance(val, str):
377 return (self.component_name != val)
378 # This signals to use the normal comparison operator
379 return NotImplemented
382 return '<Component %s>' % self.component_name
385 __all__.append('Component')
388 def get_component(component, session=None):
390 Returns database id for given C{component}.
392 @type component: string
393 @param component: The name of the override type
396 @return: the database id for the given component
399 component = component.lower()
401 q = session.query(Component).filter_by(component_name=component)
410 __all__.append('get_component')
412 ################################################################################
414 class DBConfig(object):
415 def __init__(self, *args, **kwargs):
419 return '<DBConfig %s>' % self.name
421 __all__.append('DBConfig')
423 ################################################################################
425 class ContentFilename(object):
426 def __init__(self, *args, **kwargs):
430 return '<ContentFilename %s>' % self.filename
432 __all__.append('ContentFilename')
434 def get_or_set_contents_file_id(filename, session=None):
436 Returns database id for given filename.
438 If no matching file is found, a row is inserted.
440 @type filename: string
441 @param filename: The filename
442 @type session: SQLAlchemy
443 @param session: Optional SQL session object (a temporary one will be
444 generated if not supplied). If not passed, a commit will be performed at
445 the end of the function, otherwise the caller is responsible for commiting.
448 @return: the database id for the given component
452 session = DBConn().session()
455 q = session.query(ContentFilename).filter_by(filename=filename)
457 cf = ContentFilename()
458 cf.filename = filename
464 ret = cf.cafilename_id
466 ret = q.one().cafilename_id
473 __all__.append('get_or_set_contents_file_id')
476 def get_contents(suite, overridetype, section=None, session=None):
478 Returns contents for a suite / overridetype combination, limiting
479 to a section if not None.
482 @param suite: Suite object
484 @type overridetype: OverrideType
485 @param overridetype: OverrideType object
487 @type section: Section
488 @param section: Optional section object to limit results to
490 @type session: SQLAlchemy
491 @param session: Optional SQL session object (a temporary one will be
492 generated if not supplied)
495 @return: ResultsProxy object set up to return tuples of (filename, section,
499 # find me all of the contents for a given suite
500 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
504 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
505 JOIN content_file_names n ON (c.filename=n.id)
506 JOIN binaries b ON (b.id=c.binary_pkg)
507 JOIN override o ON (o.package=b.package)
508 JOIN section s ON (s.id=o.section)
509 WHERE o.suite = :suiteid AND o.type = :overridetypeid
510 AND b.type=:overridetypename"""
512 vals = {'suiteid': suite.suite_id,
513 'overridetypeid': overridetype.overridetype_id,
514 'overridetypename': overridetype.overridetype}
516 if section is not None:
517 contents_q += " AND s.id = :sectionid"
518 vals['sectionid'] = section.section_id
520 contents_q += " ORDER BY fn"
522 ret = session.execute(contents_q, vals)
526 __all__.append('get_contents')
528 ################################################################################
530 class ContentFilepath(object):
531 def __init__(self, *args, **kwargs):
535 return '<ContentFilepath %s>' % self.filepath
537 __all__.append('ContentFilepath')
539 def get_or_set_contents_path_id(filepath, session=None):
541 Returns database id for given path.
543 If no matching file is found, a row is inserted.
545 @type filename: string
546 @param filename: The filepath
547 @type session: SQLAlchemy
548 @param session: Optional SQL session object (a temporary one will be
549 generated if not supplied). If not passed, a commit will be performed at
550 the end of the function, otherwise the caller is responsible for commiting.
553 @return: the database id for the given path
557 session = DBConn().session()
560 q = session.query(ContentFilepath).filter_by(filepath=filepath)
562 cf = ContentFilepath()
563 cf.filepath = filepath
569 ret = cf.cafilepath_id
571 ret = q.one().cafilepath_id
578 __all__.append('get_or_set_contents_path_id')
580 ################################################################################
582 class ContentAssociation(object):
583 def __init__(self, *args, **kwargs):
587 return '<ContentAssociation %s>' % self.ca_id
589 __all__.append('ContentAssociation')
591 def insert_content_paths(binary_id, fullpaths, session=None):
593 Make sure given path is associated with given binary id
596 @param binary_id: the id of the binary
597 @type fullpaths: list
598 @param fullpaths: the list of paths of the file being associated with the binary
599 @type session: SQLAlchemy session
600 @param session: Optional SQLAlchemy session. If this is passed, the caller
601 is responsible for ensuring a transaction has begun and committing the
602 results or rolling back based on the result code. If not passed, a commit
603 will be performed at the end of the function, otherwise the caller is
604 responsible for commiting.
606 @return: True upon success
611 session = DBConn().session()
617 for fullpath in fullpaths:
618 # Get the necessary IDs ...
619 (path, file) = os.path.split(fullpath)
621 filepath_id = get_or_set_contents_path_id(path, session)
622 filename_id = get_or_set_contents_file_id(file, session)
624 pathcache[fullpath] = (filepath_id, filename_id)
626 for fullpath, dat in pathcache.items():
627 ca = ContentAssociation()
628 ca.binary_id = binary_id
629 ca.filepath_id = dat[0]
630 ca.filename_id = dat[1]
633 # Only commit if we set up the session ourself
643 traceback.print_exc()
645 # Only rollback if we set up the session ourself
652 __all__.append('insert_content_paths')
654 ################################################################################
656 class DSCFile(object):
657 def __init__(self, *args, **kwargs):
661 return '<DSCFile %s>' % self.dscfile_id
663 __all__.append('DSCFile')
666 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
668 Returns a list of DSCFiles which may be empty
670 @type dscfile_id: int (optional)
671 @param dscfile_id: the dscfile_id of the DSCFiles to find
673 @type source_id: int (optional)
674 @param source_id: the source id related to the DSCFiles to find
676 @type poolfile_id: int (optional)
677 @param poolfile_id: the poolfile id related to the DSCFiles to find
680 @return: Possibly empty list of DSCFiles
683 q = session.query(DSCFile)
685 if dscfile_id is not None:
686 q = q.filter_by(dscfile_id=dscfile_id)
688 if source_id is not None:
689 q = q.filter_by(source_id=source_id)
691 if poolfile_id is not None:
692 q = q.filter_by(poolfile_id=poolfile_id)
698 __all__.append('get_dscfiles')
700 ################################################################################
702 class PoolFile(object):
703 def __init__(self, *args, **kwargs):
707 return '<PoolFile %s>' % self.filename
709 __all__.append('PoolFile')
712 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
715 (ValidFileFound [boolean or None], PoolFile object or None)
717 @type filename: string
718 @param filename: the filename of the file to check against the DB
721 @param filesize: the size of the file to check against the DB
724 @param md5sum: the md5sum of the file to check against the DB
726 @type location_id: int
727 @param location_id: the id of the location to look in
730 @return: Tuple of length 2.
731 If more than one file found with that name:
733 If valid pool file found: (True, PoolFile object)
734 If valid pool file not found:
735 (False, None) if no file found
736 (False, PoolFile object) if file found with size/md5sum mismatch
739 q = session.query(PoolFile).filter_by(filename=filename)
740 q = q.join(Location).filter_by(location_id=location_id)
750 if obj.md5sum != md5sum or obj.filesize != filesize:
758 __all__.append('check_poolfile')
761 def get_poolfile_by_id(file_id, session=None):
763 Returns a PoolFile objects or None for the given id
766 @param file_id: the id of the file to look for
768 @rtype: PoolFile or None
769 @return: either the PoolFile object or None
772 q = session.query(PoolFile).filter_by(file_id=file_id)
781 __all__.append('get_poolfile_by_id')
785 def get_poolfile_by_name(filename, location_id=None, session=None):
787 Returns an array of PoolFile objects for the given filename and
788 (optionally) location_id
790 @type filename: string
791 @param filename: the filename of the file to check against the DB
793 @type location_id: int
794 @param location_id: the id of the location to look in (optional)
797 @return: array of PoolFile objects
800 q = session.query(PoolFile).filter_by(filename=filename)
802 if location_id is not None:
803 q = q.join(Location).filter_by(location_id=location_id)
809 __all__.append('get_poolfile_by_name')
812 def get_poolfile_like_name(filename, session=None):
814 Returns an array of PoolFile objects which are like the given name
816 @type filename: string
817 @param filename: the filename of the file to check against the DB
820 @return: array of PoolFile objects
823 # TODO: There must be a way of properly using bind parameters with %FOO%
824 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
830 __all__.append('get_poolfile_like_name')
832 ################################################################################
834 class Fingerprint(object):
835 def __init__(self, *args, **kwargs):
839 return '<Fingerprint %s>' % self.fingerprint
841 __all__.append('Fingerprint')
843 def get_or_set_fingerprint(fpr, session=None):
845 Returns Fingerprint object for given fpr.
847 If no matching fpr is found, a row is inserted.
850 @param fpr: The fpr to find / add
852 @type session: SQLAlchemy
853 @param session: Optional SQL session object (a temporary one will be
854 generated if not supplied). If not passed, a commit will be performed at
855 the end of the function, otherwise the caller is responsible for commiting.
856 A flush will be performed either way.
859 @return: the Fingerprint object for the given fpr
863 session = DBConn().session()
866 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
868 fingerprint = Fingerprint()
869 fingerprint.fingerprint = fpr
870 session.add(fingerprint)
884 __all__.append('get_or_set_fingerprint')
886 ################################################################################
888 class Keyring(object):
889 def __init__(self, *args, **kwargs):
893 return '<Keyring %s>' % self.keyring_name
895 __all__.append('Keyring')
897 def get_or_set_keyring(keyring, session=None):
899 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
900 and return the new Keyring
901 If C{keyring} already has an entry, simply return the existing Keyring
903 @type keyring: string
904 @param keyring: the keyring name
907 @return: the Keyring object for this keyring
912 session = DBConn().session()
916 obj = session.query(Keyring).filter_by(keyring_name=keyring).first()
919 obj = Keyring(keyring_name=keyring)
931 __all__.append('get_or_set_keyring')
933 ################################################################################
935 class Location(object):
936 def __init__(self, *args, **kwargs):
940 return '<Location %s (%s)>' % (self.path, self.location_id)
942 __all__.append('Location')
945 def get_location(location, component=None, archive=None, session=None):
947 Returns Location object for the given combination of location, component
950 @type location: string
951 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
953 @type component: string
954 @param component: the component name (if None, no restriction applied)
956 @type archive: string
957 @param archive_id: the archive name (if None, no restriction applied)
959 @rtype: Location / None
960 @return: Either a Location object or None if one can't be found
963 q = session.query(Location).filter_by(path=location)
965 if archive is not None:
966 q = q.join(Archive).filter_by(archive_name=archive)
968 if component is not None:
969 q = q.join(Component).filter_by(component_name=component)
978 __all__.append('get_location')
980 ################################################################################
982 class Maintainer(object):
983 def __init__(self, *args, **kwargs):
987 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
989 def get_split_maintainer(self):
990 if not hasattr(self, 'name') or self.name is None:
991 return ('', '', '', '')
993 return fix_maintainer(self.name.strip())
995 __all__.append('Maintainer')
997 def get_or_set_maintainer(name, session=None):
999 Returns Maintainer object for given maintainer name.
1001 If no matching maintainer name is found, a row is inserted.
1004 @param name: The maintainer name to add
1006 @type session: SQLAlchemy
1007 @param session: Optional SQL session object (a temporary one will be
1008 generated if not supplied). If not passed, a commit will be performed at
1009 the end of the function, otherwise the caller is responsible for commiting.
1010 A flush will be performed either way.
1013 @return: the Maintainer object for the given maintainer
1015 privatetrans = False
1017 session = DBConn().session()
1020 q = session.query(Maintainer).filter_by(name=name)
1022 maintainer = Maintainer()
1023 maintainer.name = name
1024 session.add(maintainer)
1038 __all__.append('get_or_set_maintainer')
1040 def get_maintainer(maintainer_id, session=None):
1042 Return the name of the maintainer behind C{maintainer_id} or None if that
1043 maintainer_id is invalid.
1045 @type maintainer_id: int
1046 @param maintainer_id: the id of the maintainer
1049 @return: the Maintainer with this C{maintainer_id}
1052 privatetrans = False
1054 session = DBConn().session()
1058 return session.query(Maintainer).get(maintainer_id)
1063 __all__.append('get_maintainer')
1065 ################################################################################
1067 class NewComment(object):
1068 def __init__(self, *args, **kwargs):
1072 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1074 __all__.append('NewComment')
1077 def has_new_comment(package, version, session=None):
1079 Returns true if the given combination of C{package}, C{version} has a comment.
1081 @type package: string
1082 @param package: name of the package
1084 @type version: string
1085 @param version: package version
1087 @type session: Session
1088 @param session: Optional SQLA session object (a temporary one will be
1089 generated if not supplied)
1095 q = session.query(NewComment)
1096 q = q.filter_by(package=package)
1097 q = q.filter_by(version=version)
1103 __all__.append('has_new_comment')
1106 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1108 Returns (possibly empty) list of NewComment objects for the given
1111 @type package: string (optional)
1112 @param package: name of the package
1114 @type version: string (optional)
1115 @param version: package version
1117 @type comment_id: int (optional)
1118 @param comment_id: An id of a comment
1120 @type session: Session
1121 @param session: Optional SQLA session object (a temporary one will be
1122 generated if not supplied)
1125 @return: A (possibly empty) list of NewComment objects will be returned
1128 q = session.query(NewComment)
1129 if package is not None: q = q.filter_by(package=package)
1130 if version is not None: q = q.filter_by(version=version)
1131 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1137 __all__.append('get_new_comments')
1139 ################################################################################
1141 class Override(object):
1142 def __init__(self, *args, **kwargs):
1146 return '<Override %s (%s)>' % (self.package, self.suite_id)
1148 __all__.append('Override')
1151 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1153 Returns Override object for the given parameters
1155 @type package: string
1156 @param package: The name of the package
1158 @type suite: string, list or None
1159 @param suite: The name of the suite (or suites if a list) to limit to. If
1160 None, don't limit. Defaults to None.
1162 @type component: string, list or None
1163 @param component: The name of the component (or components if a list) to
1164 limit to. If None, don't limit. Defaults to None.
1166 @type overridetype: string, list or None
1167 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1168 limit to. If None, don't limit. Defaults to None.
1170 @type session: Session
1171 @param session: Optional SQLA session object (a temporary one will be
1172 generated if not supplied)
1175 @return: A (possibly empty) list of Override objects will be returned
1178 q = session.query(Override)
1179 q = q.filter_by(package=package)
1181 if suite is not None:
1182 if not isinstance(suite, list): suite = [suite]
1183 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1185 if component is not None:
1186 if not isinstance(component, list): component = [component]
1187 q = q.join(Component).filter(Component.component_name.in_(component))
1189 if overridetype is not None:
1190 if not isinstance(overridetype, list): overridetype = [overridetype]
1191 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1197 __all__.append('get_override')
1200 ################################################################################
1202 class OverrideType(object):
1203 def __init__(self, *args, **kwargs):
1207 return '<OverrideType %s>' % self.overridetype
1209 __all__.append('OverrideType')
1212 def get_override_type(override_type, session=None):
1214 Returns OverrideType object for given C{override type}.
1216 @type override_type: string
1217 @param override_type: The name of the override type
1219 @type session: Session
1220 @param session: Optional SQLA session object (a temporary one will be
1221 generated if not supplied)
1224 @return: the database id for the given override type
1227 q = session.query(OverrideType).filter_by(overridetype=override_type)
1236 __all__.append('get_override_type')
1238 ################################################################################
1240 class PendingContentAssociation(object):
1241 def __init__(self, *args, **kwargs):
1245 return '<PendingContentAssociation %s>' % self.pca_id
1247 __all__.append('PendingContentAssociation')
1249 def insert_pending_content_paths(package, fullpaths, session=None):
1251 Make sure given paths are temporarily associated with given
1255 @param package: the package to associate with should have been read in from the binary control file
1256 @type fullpaths: list
1257 @param fullpaths: the list of paths of the file being associated with the binary
1258 @type session: SQLAlchemy session
1259 @param session: Optional SQLAlchemy session. If this is passed, the caller
1260 is responsible for ensuring a transaction has begun and committing the
1261 results or rolling back based on the result code. If not passed, a commit
1262 will be performed at the end of the function
1264 @return: True upon success, False if there is a problem
1267 privatetrans = False
1270 session = DBConn().session()
1274 arch = get_architecture(package['Architecture'], session)
1275 arch_id = arch.arch_id
1277 # Remove any already existing recorded files for this package
1278 q = session.query(PendingContentAssociation)
1279 q = q.filter_by(package=package['Package'])
1280 q = q.filter_by(version=package['Version'])
1281 q = q.filter_by(architecture=arch_id)
1286 for fullpath in fullpaths:
1287 (path, file) = os.path.split(fullpath)
1289 if path.startswith( "./" ):
1292 filepath_id = get_or_set_contents_path_id(path, session)
1293 filename_id = get_or_set_contents_file_id(file, session)
1295 pathcache[fullpath] = (filepath_id, filename_id)
1297 for fullpath, dat in pathcache.items():
1298 pca = PendingContentAssociation()
1299 pca.package = package['Package']
1300 pca.version = package['Version']
1301 pca.filepath_id = dat[0]
1302 pca.filename_id = dat[1]
1303 pca.architecture = arch_id
1306 # Only commit if we set up the session ourself
1314 except Exception, e:
1315 traceback.print_exc()
1317 # Only rollback if we set up the session ourself
1324 __all__.append('insert_pending_content_paths')
1326 ################################################################################
1328 class Priority(object):
1329 def __init__(self, *args, **kwargs):
1332 def __eq__(self, val):
1333 if isinstance(val, str):
1334 return (self.priority == val)
1335 # This signals to use the normal comparison operator
1336 return NotImplemented
1338 def __ne__(self, val):
1339 if isinstance(val, str):
1340 return (self.priority != val)
1341 # This signals to use the normal comparison operator
1342 return NotImplemented
1345 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1347 __all__.append('Priority')
1350 def get_priority(priority, session=None):
1352 Returns Priority object for given C{priority name}.
1354 @type priority: string
1355 @param priority: The name of the priority
1357 @type session: Session
1358 @param session: Optional SQLA session object (a temporary one will be
1359 generated if not supplied)
1362 @return: Priority object for the given priority
1365 q = session.query(Priority).filter_by(priority=priority)
1374 __all__.append('get_priority')
1377 def get_priorities(session=None):
1379 Returns dictionary of priority names -> id mappings
1381 @type session: Session
1382 @param session: Optional SQL session object (a temporary one will be
1383 generated if not supplied)
1386 @return: dictionary of priority names -> id mappings
1390 q = session.query(Priority)
1392 ret[x.priority] = x.priority_id
1396 __all__.append('get_priorities')
1398 ################################################################################
1400 class Queue(object):
1401 def __init__(self, *args, **kwargs):
1405 return '<Queue %s>' % self.queue_name
1407 def autobuild_upload(self, changes, srcpath, session=None):
1409 Update queue_build database table used for incoming autobuild support.
1411 @type changes: Changes
1412 @param changes: changes object for the upload to process
1414 @type srcpath: string
1415 @param srcpath: path for the queue file entries/link destinations
1417 @type session: SQLAlchemy session
1418 @param session: Optional SQLAlchemy session. If this is passed, the
1419 caller is responsible for ensuring a transaction has begun and
1420 committing the results or rolling back based on the result code. If
1421 not passed, a commit will be performed at the end of the function,
1422 otherwise the caller is responsible for commiting.
1424 @rtype: NoneType or string
1425 @return: None if the operation failed, a string describing the error if not
1428 privatetrans = False
1430 session = DBConn().session()
1433 # TODO: Remove by moving queue config into the database
1436 for suitename in changes.changes["distribution"].keys():
1437 # TODO: Move into database as:
1438 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1439 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1440 # This also gets rid of the SecurityQueueBuild hack below
1441 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1445 s = get_suite(suitename, session)
1447 return "INTERNAL ERROR: Could not find suite %s" % suitename
1449 # TODO: Get from database as above
1450 dest_dir = conf["Dir::QueueBuild"]
1452 # TODO: Move into database as above
1453 if conf.FindB("Dinstall::SecurityQueueBuild"):
1454 dest_dir = os.path.join(dest_dir, suitename)
1456 for file_entry in changes.files.keys():
1457 src = os.path.join(srcpath, file_entry)
1458 dest = os.path.join(dest_dir, file_entry)
1460 # TODO: Move into database as above
1461 if conf.FindB("Dinstall::SecurityQueueBuild"):
1462 # Copy it since the original won't be readable by www-data
1463 utils.copy(src, dest)
1465 # Create a symlink to it
1466 os.symlink(src, dest)
1469 qb.suite_id = s.suite_id
1470 qb.queue_id = self.queue_id
1476 # If the .orig.tar.gz is in the pool, create a symlink to
1477 # it (if one doesn't already exist)
1478 if changes.orig_tar_id:
1479 # Determine the .orig.tar.gz file name
1480 for dsc_file in changes.dsc_files.keys():
1481 if dsc_file.endswith(".orig.tar.gz"):
1484 dest = os.path.join(dest_dir, filename)
1486 # If it doesn't exist, create a symlink
1487 if not os.path.exists(dest):
1488 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1489 {'id': changes.orig_tar_id})
1492 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1494 src = os.path.join(res[0], res[1])
1495 os.symlink(src, dest)
1497 # Add it to the list of packages for later processing by apt-ftparchive
1499 qb.suite_id = s.suite_id
1500 qb.queue_id = self.queue_id
1505 # If it does, update things to ensure it's not removed prematurely
1507 qb = get_queue_build(dest, s.suite_id, session)
1519 __all__.append('Queue')
1522 def get_queue(queuename, session=None):
1524 Returns Queue object for given C{queue name}.
1526 @type queuename: string
1527 @param queuename: The name of the queue
1529 @type session: Session
1530 @param session: Optional SQLA session object (a temporary one will be
1531 generated if not supplied)
1534 @return: Queue object for the given queue
1537 q = session.query(Queue).filter_by(queue_name=queuename)
1545 __all__.append('get_queue')
1547 ################################################################################
1549 class QueueBuild(object):
1550 def __init__(self, *args, **kwargs):
1554 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1556 __all__.append('QueueBuild')
1559 def get_queue_build(filename, suite, session=None):
1561 Returns QueueBuild object for given C{filename} and C{suite}.
1563 @type filename: string
1564 @param filename: The name of the file
1566 @type suiteid: int or str
1567 @param suiteid: Suite name or ID
1569 @type session: Session
1570 @param session: Optional SQLA session object (a temporary one will be
1571 generated if not supplied)
1574 @return: Queue object for the given queue
1577 if isinstance(suite, int):
1578 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1580 q = session.query(QueueBuild).filter_by(filename=filename)
1581 q = q.join(Suite).filter_by(suite_name=suite)
1590 __all__.append('get_queue_build')
1592 ################################################################################
1594 class Section(object):
1595 def __init__(self, *args, **kwargs):
1598 def __eq__(self, val):
1599 if isinstance(val, str):
1600 return (self.section == val)
1601 # This signals to use the normal comparison operator
1602 return NotImplemented
1604 def __ne__(self, val):
1605 if isinstance(val, str):
1606 return (self.section != val)
1607 # This signals to use the normal comparison operator
1608 return NotImplemented
1611 return '<Section %s>' % self.section
1613 __all__.append('Section')
1616 def get_section(section, session=None):
1618 Returns Section object for given C{section name}.
1620 @type section: string
1621 @param section: The name of the section
1623 @type session: Session
1624 @param session: Optional SQLA session object (a temporary one will be
1625 generated if not supplied)
1628 @return: Section object for the given section name
1631 q = session.query(Section).filter_by(section=section)
1639 __all__.append('get_section')
1642 def get_sections(session=None):
1644 Returns dictionary of section names -> id mappings
1646 @type session: Session
1647 @param session: Optional SQL session object (a temporary one will be
1648 generated if not supplied)
1651 @return: dictionary of section names -> id mappings
1655 q = session.query(Section)
1657 ret[x.section] = x.section_id
1661 __all__.append('get_sections')
1663 ################################################################################
1665 class DBSource(object):
1666 def __init__(self, *args, **kwargs):
1670 return '<DBSource %s (%s)>' % (self.source, self.version)
1672 __all__.append('DBSource')
1675 def source_exists(source, source_version, suites = ["any"], session=None):
1677 Ensure that source exists somewhere in the archive for the binary
1678 upload being processed.
1679 1. exact match => 1.0-3
1680 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1682 @type package: string
1683 @param package: package source name
1685 @type source_version: string
1686 @param source_version: expected source version
1689 @param suites: list of suites to check in, default I{any}
1691 @type session: Session
1692 @param session: Optional SQLA session object (a temporary one will be
1693 generated if not supplied)
1696 @return: returns 1 if a source with expected version is found, otherwise 0
1703 for suite in suites:
1704 q = session.query(DBSource).filter_by(source=source)
1706 # source must exist in suite X, or in some other suite that's
1707 # mapped to X, recursively... silent-maps are counted too,
1708 # unreleased-maps aren't.
1709 maps = cnf.ValueList("SuiteMappings")[:]
1711 maps = [ m.split() for m in maps ]
1712 maps = [ (x[1], x[2]) for x in maps
1713 if x[0] == "map" or x[0] == "silent-map" ]
1716 if x[1] in s and x[0] not in s:
1719 q = q.join(SrcAssociation).join(Suite)
1720 q = q.filter(Suite.suite_name.in_(s))
1722 # Reduce the query results to a list of version numbers
1723 ql = [ j.version for j in q.all() ]
1726 if source_version in ql:
1730 from daklib.regexes import re_bin_only_nmu
1731 orig_source_version = re_bin_only_nmu.sub('', source_version)
1732 if orig_source_version in ql:
1735 # No source found so return not ok
1740 __all__.append('source_exists')
1743 def get_suites_source_in(source, session=None):
1745 Returns list of Suite objects which given C{source} name is in
1748 @param source: DBSource package name to search for
1751 @return: list of Suite objects for the given source
1754 ret = session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1758 __all__.append('get_suites_source_in')
1761 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1763 Returns list of DBSource objects for given C{source} name and other parameters
1766 @param source: DBSource package name to search for
1768 @type source: str or None
1769 @param source: DBSource version name to search for or None if not applicable
1771 @type dm_upload_allowed: bool
1772 @param dm_upload_allowed: If None, no effect. If True or False, only
1773 return packages with that dm_upload_allowed setting
1775 @type session: Session
1776 @param session: Optional SQL session object (a temporary one will be
1777 generated if not supplied)
1780 @return: list of DBSource objects for the given name (may be empty)
1783 q = session.query(DBSource).filter_by(source=source)
1785 if version is not None:
1786 q = q.filter_by(version=version)
1788 if dm_upload_allowed is not None:
1789 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1795 __all__.append('get_sources_from_name')
1798 def get_source_in_suite(source, suite, session=None):
1800 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1802 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1803 - B{suite} - a suite name, eg. I{unstable}
1805 @type source: string
1806 @param source: source package name
1809 @param suite: the suite name
1812 @return: the version for I{source} in I{suite}
1816 q = session.query(SrcAssociation)
1817 q = q.join('source').filter_by(source=source)
1818 q = q.join('suite').filter_by(suite_name=suite)
1823 # ???: Maybe we should just return the SrcAssociation object instead
1824 ret = q.one().source
1828 __all__.append('get_source_in_suite')
1830 ################################################################################
1832 class SrcAssociation(object):
1833 def __init__(self, *args, **kwargs):
1837 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1839 __all__.append('SrcAssociation')
1841 ################################################################################
1843 class SrcUploader(object):
1844 def __init__(self, *args, **kwargs):
1848 return '<SrcUploader %s>' % self.uploader_id
1850 __all__.append('SrcUploader')
1852 ################################################################################
1854 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1855 ('SuiteID', 'suite_id'),
1856 ('Version', 'version'),
1857 ('Origin', 'origin'),
1859 ('Description', 'description'),
1860 ('Untouchable', 'untouchable'),
1861 ('Announce', 'announce'),
1862 ('Codename', 'codename'),
1863 ('OverrideCodename', 'overridecodename'),
1864 ('ValidTime', 'validtime'),
1865 ('Priority', 'priority'),
1866 ('NotAutomatic', 'notautomatic'),
1867 ('CopyChanges', 'copychanges'),
1868 ('CopyDotDak', 'copydotdak'),
1869 ('CommentsDir', 'commentsdir'),
1870 ('OverrideSuite', 'overridesuite'),
1871 ('ChangelogBase', 'changelogbase')]
1874 class Suite(object):
1875 def __init__(self, *args, **kwargs):
1879 return '<Suite %s>' % self.suite_name
1881 def __eq__(self, val):
1882 if isinstance(val, str):
1883 return (self.suite_name == val)
1884 # This signals to use the normal comparison operator
1885 return NotImplemented
1887 def __ne__(self, val):
1888 if isinstance(val, str):
1889 return (self.suite_name != val)
1890 # This signals to use the normal comparison operator
1891 return NotImplemented
1895 for disp, field in SUITE_FIELDS:
1896 val = getattr(self, field, None)
1898 ret.append("%s: %s" % (disp, val))
1900 return "\n".join(ret)
1902 __all__.append('Suite')
1905 def get_suite_architecture(suite, architecture, session=None):
1907 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1911 @param suite: Suite name to search for
1913 @type architecture: str
1914 @param architecture: Architecture name to search for
1916 @type session: Session
1917 @param session: Optional SQL session object (a temporary one will be
1918 generated if not supplied)
1920 @rtype: SuiteArchitecture
1921 @return: the SuiteArchitecture object or None
1924 q = session.query(SuiteArchitecture)
1925 q = q.join(Architecture).filter_by(arch_string=architecture)
1926 q = q.join(Suite).filter_by(suite_name=suite)
1935 __all__.append('get_suite_architecture')
1938 def get_suite(suite, session=None):
1940 Returns Suite object for given C{suite name}.
1943 @param suite: The name of the suite
1945 @type session: Session
1946 @param session: Optional SQLA session object (a temporary one will be
1947 generated if not supplied)
1950 @return: Suite object for the requested suite name (None if not presenT)
1953 q = session.query(Suite).filter_by(suite_name=suite)
1962 __all__.append('get_suite')
1964 ################################################################################
1966 class SuiteArchitecture(object):
1967 def __init__(self, *args, **kwargs):
1971 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1973 __all__.append('SuiteArchitecture')
1976 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1978 Returns list of Architecture objects for given C{suite} name
1981 @param source: Suite name to search for
1983 @type skipsrc: boolean
1984 @param skipsrc: Whether to skip returning the 'source' architecture entry
1987 @type skipall: boolean
1988 @param skipall: Whether to skip returning the 'all' architecture entry
1991 @type session: Session
1992 @param session: Optional SQL session object (a temporary one will be
1993 generated if not supplied)
1996 @return: list of Architecture objects for the given name (may be empty)
1999 q = session.query(Architecture)
2000 q = q.join(SuiteArchitecture)
2001 q = q.join(Suite).filter_by(suite_name=suite)
2004 q = q.filter(Architecture.arch_string != 'source')
2007 q = q.filter(Architecture.arch_string != 'all')
2009 q = q.order_by('arch_string')
2015 __all__.append('get_suite_architectures')
2017 ################################################################################
2020 def __init__(self, *args, **kwargs):
2023 def __eq__(self, val):
2024 if isinstance(val, str):
2025 return (self.uid == val)
2026 # This signals to use the normal comparison operator
2027 return NotImplemented
2029 def __ne__(self, val):
2030 if isinstance(val, str):
2031 return (self.uid != val)
2032 # This signals to use the normal comparison operator
2033 return NotImplemented
2036 return '<Uid %s (%s)>' % (self.uid, self.name)
2038 __all__.append('Uid')
2040 def add_database_user(uidname, session=None):
2042 Adds a database user
2044 @type uidname: string
2045 @param uidname: The uid of the user to add
2047 @type session: SQLAlchemy
2048 @param session: Optional SQL session object (a temporary one will be
2049 generated if not supplied). If not passed, a commit will be performed at
2050 the end of the function, otherwise the caller is responsible for commiting.
2053 @return: the uid object for the given uidname
2056 privatetrans = False
2058 session = DBConn().session()
2061 session.execute("CREATE USER :uid", {'uid': uidname})
2067 __all__.append('add_database_user')
2069 def get_or_set_uid(uidname, session=None):
2071 Returns uid object for given uidname.
2073 If no matching uidname is found, a row is inserted.
2075 @type uidname: string
2076 @param uidname: The uid to add
2078 @type session: SQLAlchemy
2079 @param session: Optional SQL session object (a temporary one will be
2080 generated if not supplied). If not passed, a commit will be performed at
2081 the end of the function, otherwise the caller is responsible for commiting.
2084 @return: the uid object for the given uidname
2087 privatetrans = False
2089 session = DBConn().session()
2092 q = session.query(Uid).filter_by(uid=uidname)
2111 __all__.append('get_or_set_uid')
2114 def get_uid_from_fingerprint(fpr, session=None):
2115 q = session.query(Uid)
2116 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2125 __all__.append('get_uid_from_fingerprint')
2127 ################################################################################
2129 class DBConn(Singleton):
2131 database module init.
2133 def __init__(self, *args, **kwargs):
2134 super(DBConn, self).__init__(*args, **kwargs)
2136 def _startup(self, *args, **kwargs):
2138 if kwargs.has_key('debug'):
2142 def __setuptables(self):
2143 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2144 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2145 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2146 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2147 self.tbl_component = Table('component', self.db_meta, autoload=True)
2148 self.tbl_config = Table('config', self.db_meta, autoload=True)
2149 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2150 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2151 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2152 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2153 self.tbl_files = Table('files', self.db_meta, autoload=True)
2154 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2155 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2156 self.tbl_location = Table('location', self.db_meta, autoload=True)
2157 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2158 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2159 self.tbl_override = Table('override', self.db_meta, autoload=True)
2160 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2161 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2162 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2163 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2164 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2165 self.tbl_section = Table('section', self.db_meta, autoload=True)
2166 self.tbl_source = Table('source', self.db_meta, autoload=True)
2167 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2168 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2169 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2170 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2171 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2173 def __setupmappers(self):
2174 mapper(Architecture, self.tbl_architecture,
2175 properties = dict(arch_id = self.tbl_architecture.c.id))
2177 mapper(Archive, self.tbl_archive,
2178 properties = dict(archive_id = self.tbl_archive.c.id,
2179 archive_name = self.tbl_archive.c.name))
2181 mapper(BinAssociation, self.tbl_bin_associations,
2182 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2183 suite_id = self.tbl_bin_associations.c.suite,
2184 suite = relation(Suite),
2185 binary_id = self.tbl_bin_associations.c.bin,
2186 binary = relation(DBBinary)))
2188 mapper(DBBinary, self.tbl_binaries,
2189 properties = dict(binary_id = self.tbl_binaries.c.id,
2190 package = self.tbl_binaries.c.package,
2191 version = self.tbl_binaries.c.version,
2192 maintainer_id = self.tbl_binaries.c.maintainer,
2193 maintainer = relation(Maintainer),
2194 source_id = self.tbl_binaries.c.source,
2195 source = relation(DBSource),
2196 arch_id = self.tbl_binaries.c.architecture,
2197 architecture = relation(Architecture),
2198 poolfile_id = self.tbl_binaries.c.file,
2199 poolfile = relation(PoolFile),
2200 binarytype = self.tbl_binaries.c.type,
2201 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2202 fingerprint = relation(Fingerprint),
2203 install_date = self.tbl_binaries.c.install_date,
2204 binassociations = relation(BinAssociation,
2205 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2207 mapper(Component, self.tbl_component,
2208 properties = dict(component_id = self.tbl_component.c.id,
2209 component_name = self.tbl_component.c.name))
2211 mapper(DBConfig, self.tbl_config,
2212 properties = dict(config_id = self.tbl_config.c.id))
2214 mapper(ContentAssociation, self.tbl_content_associations,
2215 properties = dict(ca_id = self.tbl_content_associations.c.id,
2216 filename_id = self.tbl_content_associations.c.filename,
2217 filename = relation(ContentFilename),
2218 filepath_id = self.tbl_content_associations.c.filepath,
2219 filepath = relation(ContentFilepath),
2220 binary_id = self.tbl_content_associations.c.binary_pkg,
2221 binary = relation(DBBinary)))
2224 mapper(ContentFilename, self.tbl_content_file_names,
2225 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2226 filename = self.tbl_content_file_names.c.file))
2228 mapper(ContentFilepath, self.tbl_content_file_paths,
2229 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2230 filepath = self.tbl_content_file_paths.c.path))
2232 mapper(DSCFile, self.tbl_dsc_files,
2233 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2234 source_id = self.tbl_dsc_files.c.source,
2235 source = relation(DBSource),
2236 poolfile_id = self.tbl_dsc_files.c.file,
2237 poolfile = relation(PoolFile)))
2239 mapper(PoolFile, self.tbl_files,
2240 properties = dict(file_id = self.tbl_files.c.id,
2241 filesize = self.tbl_files.c.size,
2242 location_id = self.tbl_files.c.location,
2243 location = relation(Location)))
2245 mapper(Fingerprint, self.tbl_fingerprint,
2246 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2247 uid_id = self.tbl_fingerprint.c.uid,
2248 uid = relation(Uid),
2249 keyring_id = self.tbl_fingerprint.c.keyring,
2250 keyring = relation(Keyring)))
2252 mapper(Keyring, self.tbl_keyrings,
2253 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2254 keyring_id = self.tbl_keyrings.c.id))
2256 mapper(Location, self.tbl_location,
2257 properties = dict(location_id = self.tbl_location.c.id,
2258 component_id = self.tbl_location.c.component,
2259 component = relation(Component),
2260 archive_id = self.tbl_location.c.archive,
2261 archive = relation(Archive),
2262 archive_type = self.tbl_location.c.type))
2264 mapper(Maintainer, self.tbl_maintainer,
2265 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2267 mapper(NewComment, self.tbl_new_comments,
2268 properties = dict(comment_id = self.tbl_new_comments.c.id))
2270 mapper(Override, self.tbl_override,
2271 properties = dict(suite_id = self.tbl_override.c.suite,
2272 suite = relation(Suite),
2273 component_id = self.tbl_override.c.component,
2274 component = relation(Component),
2275 priority_id = self.tbl_override.c.priority,
2276 priority = relation(Priority),
2277 section_id = self.tbl_override.c.section,
2278 section = relation(Section),
2279 overridetype_id = self.tbl_override.c.type,
2280 overridetype = relation(OverrideType)))
2282 mapper(OverrideType, self.tbl_override_type,
2283 properties = dict(overridetype = self.tbl_override_type.c.type,
2284 overridetype_id = self.tbl_override_type.c.id))
2286 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2287 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2288 filepath_id = self.tbl_pending_content_associations.c.filepath,
2289 filepath = relation(ContentFilepath),
2290 filename_id = self.tbl_pending_content_associations.c.filename,
2291 filename = relation(ContentFilename)))
2293 mapper(Priority, self.tbl_priority,
2294 properties = dict(priority_id = self.tbl_priority.c.id))
2296 mapper(Queue, self.tbl_queue,
2297 properties = dict(queue_id = self.tbl_queue.c.id))
2299 mapper(QueueBuild, self.tbl_queue_build,
2300 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2301 queue_id = self.tbl_queue_build.c.queue,
2302 queue = relation(Queue, backref='queuebuild')))
2304 mapper(Section, self.tbl_section,
2305 properties = dict(section_id = self.tbl_section.c.id))
2307 mapper(DBSource, self.tbl_source,
2308 properties = dict(source_id = self.tbl_source.c.id,
2309 version = self.tbl_source.c.version,
2310 maintainer_id = self.tbl_source.c.maintainer,
2311 maintainer = relation(Maintainer,
2312 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2313 poolfile_id = self.tbl_source.c.file,
2314 poolfile = relation(PoolFile),
2315 fingerprint_id = self.tbl_source.c.sig_fpr,
2316 fingerprint = relation(Fingerprint),
2317 changedby_id = self.tbl_source.c.changedby,
2318 changedby = relation(Maintainer,
2319 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2320 srcfiles = relation(DSCFile,
2321 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2322 srcassociations = relation(SrcAssociation,
2323 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2325 mapper(SrcAssociation, self.tbl_src_associations,
2326 properties = dict(sa_id = self.tbl_src_associations.c.id,
2327 suite_id = self.tbl_src_associations.c.suite,
2328 suite = relation(Suite),
2329 source_id = self.tbl_src_associations.c.source,
2330 source = relation(DBSource)))
2332 mapper(SrcUploader, self.tbl_src_uploaders,
2333 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2334 source_id = self.tbl_src_uploaders.c.source,
2335 source = relation(DBSource,
2336 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2337 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2338 maintainer = relation(Maintainer,
2339 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2341 mapper(Suite, self.tbl_suite,
2342 properties = dict(suite_id = self.tbl_suite.c.id))
2344 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2345 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2346 suite = relation(Suite, backref='suitearchitectures'),
2347 arch_id = self.tbl_suite_architectures.c.architecture,
2348 architecture = relation(Architecture)))
2350 mapper(Uid, self.tbl_uid,
2351 properties = dict(uid_id = self.tbl_uid.c.id,
2352 fingerprint = relation(Fingerprint)))
2354 ## Connection functions
2355 def __createconn(self):
2356 from config import Config
2360 connstr = "postgres://%s" % cnf["DB::Host"]
2361 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2362 connstr += ":%s" % cnf["DB::Port"]
2363 connstr += "/%s" % cnf["DB::Name"]
2366 connstr = "postgres:///%s" % cnf["DB::Name"]
2367 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2368 connstr += "?port=%s" % cnf["DB::Port"]
2370 self.db_pg = create_engine(connstr, echo=self.debug)
2371 self.db_meta = MetaData()
2372 self.db_meta.bind = self.db_pg
2373 self.db_smaker = sessionmaker(bind=self.db_pg,
2377 self.__setuptables()
2378 self.__setupmappers()
2381 return self.db_smaker()
2383 __all__.append('DBConn')