5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from sqlalchemy import create_engine, Table, MetaData, select
41 from sqlalchemy.orm import sessionmaker, mapper, relation
43 # Don't remove this, we re-export the exceptions to scripts which import us
44 from sqlalchemy.exc import *
46 # Only import Config until Queue stuff is changed to store its config
48 from config import Config
49 from singleton import Singleton
50 from textutils import fix_maintainer
52 ################################################################################
54 __all__ = ['IntegrityError', 'SQLAlchemyError']
56 ################################################################################
58 class Architecture(object):
59 def __init__(self, *args, **kwargs):
62 def __eq__(self, val):
63 if isinstance(val, str):
64 return (self.arch_string== val)
65 # This signals to use the normal comparison operator
68 def __ne__(self, val):
69 if isinstance(val, str):
70 return (self.arch_string != val)
71 # This signals to use the normal comparison operator
75 return '<Architecture %s>' % self.arch_string
77 __all__.append('Architecture')
79 def get_architecture(architecture, session=None):
81 Returns database id for given C{architecture}.
83 @type architecture: string
84 @param architecture: The name of the architecture
86 @type session: Session
87 @param session: Optional SQLA session object (a temporary one will be
88 generated if not supplied)
91 @return: Architecture object for the given arch (None if not present)
97 session = DBConn().session()
100 q = session.query(Architecture).filter_by(arch_string=architecture)
112 __all__.append('get_architecture')
114 def get_architecture_suites(architecture, session=None):
116 Returns list of Suite objects for given C{architecture} name
119 @param source: Architecture name to search for
121 @type session: Session
122 @param session: Optional SQL session object (a temporary one will be
123 generated if not supplied)
126 @return: list of Suite objects for the given name (may be empty)
131 session = DBConn().session()
134 q = session.query(Suite)
135 q = q.join(SuiteArchitecture)
136 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
145 __all__.append('get_architecture_suites')
147 ################################################################################
149 class Archive(object):
150 def __init__(self, *args, **kwargs):
154 return '<Archive %s>' % self.name
156 __all__.append('Archive')
158 def get_archive(archive, session=None):
160 returns database id for given c{archive}.
162 @type archive: string
163 @param archive: the name of the arhive
165 @type session: Session
166 @param session: Optional SQLA session object (a temporary one will be
167 generated if not supplied)
170 @return: Archive object for the given name (None if not present)
173 archive = archive.lower()
177 session = DBConn().session()
180 q = session.query(Archive).filter_by(archive_name=archive)
192 __all__.append('get_archive')
194 ################################################################################
196 class BinAssociation(object):
197 def __init__(self, *args, **kwargs):
201 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
203 __all__.append('BinAssociation')
205 ################################################################################
207 class DBBinary(object):
208 def __init__(self, *args, **kwargs):
212 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
214 __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
229 session = DBConn().session()
232 ret = session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
238 __all__.append('get_suites_binary_in')
240 def get_binary_from_id(id, session=None):
242 Returns DBBinary object for given C{id}
245 @param id: Id of the required binary
247 @type session: Session
248 @param session: Optional SQLA session object (a temporary one will be
249 generated if not supplied)
252 @return: DBBinary object for the given binary (None if not present)
256 session = DBConn().session()
259 q = session.query(DBBinary).filter_by(binary_id=id)
271 __all__.append('get_binary_from_id')
273 def get_binaries_from_name(package, version=None, architecture=None, session=None):
275 Returns list of DBBinary objects for given C{package} name
278 @param package: DBBinary package name to search for
280 @type version: str or None
281 @param version: Version to search for (or None)
283 @type package: str, list or None
284 @param package: Architectures to limit to (or None if no limit)
286 @type session: Session
287 @param session: Optional SQL session object (a temporary one will be
288 generated if not supplied)
291 @return: list of DBBinary objects for the given name (may be empty)
295 session = DBConn().session()
298 q = session.query(DBBinary).filter_by(package=package)
300 if version is not None:
301 q = q.filter_by(version=version)
303 if architecture is not None:
304 if not isinstance(architecture, list):
305 architecture = [architecture]
306 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
315 __all__.append('get_binaries_from_name')
317 def get_binaries_from_source_id(source_id, session=None):
319 Returns list of DBBinary objects for given C{source_id}
322 @param source_id: source_id to search for
324 @type session: Session
325 @param session: Optional SQL session object (a temporary one will be
326 generated if not supplied)
329 @return: list of DBBinary objects for the given name (may be empty)
333 session = DBConn().session()
336 ret = session.query(DBBinary).filter_by(source_id=source_id).all()
344 __all__.append('get_binaries_from_source_id')
347 def get_binary_from_name_suite(package, suitename, session=None):
348 ### For dak examine-package
349 ### XXX: Doesn't use object API yet
352 session = DBConn().session()
355 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
356 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
357 WHERE b.package=:package
359 AND fi.location = l.id
360 AND l.component = c.id
363 AND su.suite_name=:suitename
364 ORDER BY b.version DESC"""
366 ret = session.execute(sql, {'package': package, 'suitename': suitename})
373 __all__.append('get_binary_from_name_suite')
375 def get_binary_components(package, suitename, arch, session=None):
376 # Check for packages that have moved from one component to another
377 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
378 WHERE b.package=:package AND s.suite_name=:suitename
379 AND (a.arch_string = :arch OR a.arch_string = 'all')
380 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
381 AND f.location = l.id
382 AND l.component = c.id
385 vals = {'package': package, 'suitename': suitename, 'arch': arch}
389 session = DBConn().session()
392 ret = session.execute(query, vals)
399 __all__.append('get_binary_components')
401 ################################################################################
403 class Component(object):
404 def __init__(self, *args, **kwargs):
407 def __eq__(self, val):
408 if isinstance(val, str):
409 return (self.component_name == val)
410 # This signals to use the normal comparison operator
411 return NotImplemented
413 def __ne__(self, val):
414 if isinstance(val, str):
415 return (self.component_name != val)
416 # This signals to use the normal comparison operator
417 return NotImplemented
420 return '<Component %s>' % self.component_name
423 __all__.append('Component')
425 def get_component(component, session=None):
427 Returns database id for given C{component}.
429 @type component: string
430 @param component: The name of the override type
433 @return: the database id for the given component
436 component = component.lower()
440 session = DBConn().session()
443 q = session.query(Component).filter_by(component_name=component)
455 __all__.append('get_component')
457 ################################################################################
459 class DBConfig(object):
460 def __init__(self, *args, **kwargs):
464 return '<DBConfig %s>' % self.name
466 __all__.append('DBConfig')
468 ################################################################################
470 class ContentFilename(object):
471 def __init__(self, *args, **kwargs):
475 return '<ContentFilename %s>' % self.filename
477 __all__.append('ContentFilename')
479 def get_or_set_contents_file_id(filename, session=None):
481 Returns database id for given filename.
483 If no matching file is found, a row is inserted.
485 @type filename: string
486 @param filename: The filename
487 @type session: SQLAlchemy
488 @param session: Optional SQL session object (a temporary one will be
489 generated if not supplied). If not passed, a commit will be performed at
490 the end of the function, otherwise the caller is responsible for commiting.
493 @return: the database id for the given component
497 session = DBConn().session()
500 q = session.query(ContentFilename).filter_by(filename=filename)
502 cf = ContentFilename()
503 cf.filename = filename
509 ret = cf.cafilename_id
511 ret = q.one().cafilename_id
518 __all__.append('get_or_set_contents_file_id')
520 def get_contents(suite, overridetype, section=None, session=None):
522 Returns contents for a suite / overridetype combination, limiting
523 to a section if not None.
526 @param suite: Suite object
528 @type overridetype: OverrideType
529 @param overridetype: OverrideType object
531 @type section: Section
532 @param section: Optional section object to limit results to
534 @type session: SQLAlchemy
535 @param session: Optional SQL session object (a temporary one will be
536 generated if not supplied)
539 @return: ResultsProxy object set up to return tuples of (filename, section,
545 session = DBConn().session()
548 # find me all of the contents for a given suite
549 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
553 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
554 JOIN content_file_names n ON (c.filename=n.id)
555 JOIN binaries b ON (b.id=c.binary_pkg)
556 JOIN override o ON (o.package=b.package)
557 JOIN section s ON (s.id=o.section)
558 WHERE o.suite = :suiteid AND o.type = :overridetypeid
559 AND b.type=:overridetypename"""
561 vals = {'suiteid': suite.suite_id,
562 'overridetypeid': overridetype.overridetype_id,
563 'overridetypename': overridetype.overridetype}
565 if section is not None:
566 contents_q += " AND s.id = :sectionid"
567 vals['sectionid'] = section.section_id
569 contents_q += " ORDER BY fn"
571 ret = session.execute(contents_q, vals)
578 __all__.append('get_contents')
580 ################################################################################
582 class ContentFilepath(object):
583 def __init__(self, *args, **kwargs):
587 return '<ContentFilepath %s>' % self.filepath
589 __all__.append('ContentFilepath')
591 def get_or_set_contents_path_id(filepath, session=None):
593 Returns database id for given path.
595 If no matching file is found, a row is inserted.
597 @type filename: string
598 @param filename: The filepath
599 @type session: SQLAlchemy
600 @param session: Optional SQL session object (a temporary one will be
601 generated if not supplied). If not passed, a commit will be performed at
602 the end of the function, otherwise the caller is responsible for commiting.
605 @return: the database id for the given path
609 session = DBConn().session()
612 q = session.query(ContentFilepath).filter_by(filepath=filepath)
614 cf = ContentFilepath()
615 cf.filepath = filepath
621 ret = cf.cafilepath_id
623 ret = q.one().cafilepath_id
630 __all__.append('get_or_set_contents_path_id')
632 ################################################################################
634 class ContentAssociation(object):
635 def __init__(self, *args, **kwargs):
639 return '<ContentAssociation %s>' % self.ca_id
641 __all__.append('ContentAssociation')
643 def insert_content_paths(binary_id, fullpaths, session=None):
645 Make sure given path is associated with given binary id
648 @param binary_id: the id of the binary
649 @type fullpaths: list
650 @param fullpaths: the list of paths of the file being associated with the binary
651 @type session: SQLAlchemy session
652 @param session: Optional SQLAlchemy session. If this is passed, the caller
653 is responsible for ensuring a transaction has begun and committing the
654 results or rolling back based on the result code. If not passed, a commit
655 will be performed at the end of the function, otherwise the caller is
656 responsible for commiting.
658 @return: True upon success
663 session = DBConn().session()
669 for fullpath in fullpaths:
670 # Get the necessary IDs ...
671 (path, file) = os.path.split(fullpath)
673 filepath_id = get_or_set_contents_path_id(path, session)
674 filename_id = get_or_set_contents_file_id(file, session)
676 pathcache[fullpath] = (filepath_id, filename_id)
678 for fullpath, dat in pathcache.items():
679 ca = ContentAssociation()
680 ca.binary_id = binary_id
681 ca.filepath_id = dat[0]
682 ca.filename_id = dat[1]
685 # Only commit if we set up the session ourself
695 traceback.print_exc()
697 # Only rollback if we set up the session ourself
704 __all__.append('insert_content_paths')
706 ################################################################################
708 class DSCFile(object):
709 def __init__(self, *args, **kwargs):
713 return '<DSCFile %s>' % self.dscfile_id
715 __all__.append('DSCFile')
717 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
719 Returns a list of DSCFiles which may be empty
721 @type dscfile_id: int (optional)
722 @param dscfile_id: the dscfile_id of the DSCFiles to find
724 @type source_id: int (optional)
725 @param source_id: the source id related to the DSCFiles to find
727 @type poolfile_id: int (optional)
728 @param poolfile_id: the poolfile id related to the DSCFiles to find
731 @return: Possibly empty list of DSCFiles
736 session = DBConn().session()
739 q = session.query(DSCFile)
741 if dscfile_id is not None:
742 q = q.filter_by(dscfile_id=dscfile_id)
744 if source_id is not None:
745 q = q.filter_by(source_id=source_id)
747 if poolfile_id is not None:
748 q = q.filter_by(poolfile_id=poolfile_id)
757 __all__.append('get_dscfiles')
759 ################################################################################
761 class PoolFile(object):
762 def __init__(self, *args, **kwargs):
766 return '<PoolFile %s>' % self.filename
768 __all__.append('PoolFile')
770 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
773 (ValidFileFound [boolean or None], PoolFile object or None)
775 @type filename: string
776 @param filename: the filename of the file to check against the DB
779 @param filesize: the size of the file to check against the DB
782 @param md5sum: the md5sum of the file to check against the DB
784 @type location_id: int
785 @param location_id: the id of the location to look in
788 @return: Tuple of length 2.
789 If more than one file found with that name:
791 If valid pool file found: (True, PoolFile object)
792 If valid pool file not found:
793 (False, None) if no file found
794 (False, PoolFile object) if file found with size/md5sum mismatch
799 session = DBConn().session()
802 q = session.query(PoolFile).filter_by(filename=filename)
803 q = q.join(Location).filter_by(location_id=location_id)
813 if obj.md5sum != md5sum or obj.filesize != filesize:
824 __all__.append('check_poolfile')
826 def get_poolfile_by_id(file_id, session=None):
828 Returns a PoolFile objects or None for the given id
831 @param file_id: the id of the file to look for
833 @rtype: PoolFile or None
834 @return: either the PoolFile object or None
839 session = DBConn().session()
842 q = session.query(PoolFile).filter_by(file_id=file_id)
854 __all__.append('get_poolfile_by_id')
857 def get_poolfile_by_name(filename, location_id=None, session=None):
859 Returns an array of PoolFile objects for the given filename and
860 (optionally) location_id
862 @type filename: string
863 @param filename: the filename of the file to check against the DB
865 @type location_id: int
866 @param location_id: the id of the location to look in (optional)
869 @return: array of PoolFile objects
874 session = DBConn().session()
877 q = session.query(PoolFile).filter_by(filename=filename)
879 if location_id is not None:
880 q = q.join(Location).filter_by(location_id=location_id)
889 __all__.append('get_poolfile_by_name')
891 def get_poolfile_like_name(filename, session=None):
893 Returns an array of PoolFile objects which are like the given name
895 @type filename: string
896 @param filename: the filename of the file to check against the DB
899 @return: array of PoolFile objects
904 session = DBConn().session()
907 # TODO: There must be a way of properly using bind parameters with %FOO%
908 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
917 __all__.append('get_poolfile_like_name')
919 ################################################################################
921 class Fingerprint(object):
922 def __init__(self, *args, **kwargs):
926 return '<Fingerprint %s>' % self.fingerprint
928 __all__.append('Fingerprint')
930 def get_or_set_fingerprint(fpr, session=None):
932 Returns Fingerprint object for given fpr.
934 If no matching fpr is found, a row is inserted.
937 @param fpr: The fpr to find / add
939 @type session: SQLAlchemy
940 @param session: Optional SQL session object (a temporary one will be
941 generated if not supplied). If not passed, a commit will be performed at
942 the end of the function, otherwise the caller is responsible for commiting.
943 A flush will be performed either way.
946 @return: the Fingerprint object for the given fpr
950 session = DBConn().session()
953 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
955 fingerprint = Fingerprint()
956 fingerprint.fingerprint = fpr
957 session.add(fingerprint)
971 __all__.append('get_or_set_fingerprint')
973 ################################################################################
975 class Keyring(object):
976 def __init__(self, *args, **kwargs):
980 return '<Keyring %s>' % self.keyring_name
982 __all__.append('Keyring')
984 def get_or_set_keyring(keyring, session=None):
986 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
987 and return the new Keyring
988 If C{keyring} already has an entry, simply return the existing Keyring
990 @type keyring: string
991 @param keyring: the keyring name
994 @return: the Keyring object for this keyring
999 session = DBConn().session()
1003 obj = session.query(Keyring).filter_by(keyring_name=keyring).first()
1006 obj = Keyring(keyring_name=keyring)
1018 __all__.append('get_or_set_keyring')
1020 ################################################################################
1022 class Location(object):
1023 def __init__(self, *args, **kwargs):
1027 return '<Location %s (%s)>' % (self.path, self.location_id)
1029 __all__.append('Location')
1031 def get_location(location, component=None, archive=None, session=None):
1033 Returns Location object for the given combination of location, component
1036 @type location: string
1037 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1039 @type component: string
1040 @param component: the component name (if None, no restriction applied)
1042 @type archive: string
1043 @param archive_id: the archive name (if None, no restriction applied)
1045 @rtype: Location / None
1046 @return: Either a Location object or None if one can't be found
1049 privatetrans = False
1051 session = DBConn().session()
1054 q = session.query(Location).filter_by(path=location)
1056 if archive is not None:
1057 q = q.join(Archive).filter_by(archive_name=archive)
1059 if component is not None:
1060 q = q.join(Component).filter_by(component_name=component)
1072 __all__.append('get_location')
1074 ################################################################################
1076 class Maintainer(object):
1077 def __init__(self, *args, **kwargs):
1081 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1083 def get_split_maintainer(self):
1084 if not hasattr(self, 'name') or self.name is None:
1085 return ('', '', '', '')
1087 return fix_maintainer(self.name.strip())
1089 __all__.append('Maintainer')
1091 def get_or_set_maintainer(name, session=None):
1093 Returns Maintainer object for given maintainer name.
1095 If no matching maintainer name is found, a row is inserted.
1098 @param name: The maintainer name to add
1100 @type session: SQLAlchemy
1101 @param session: Optional SQL session object (a temporary one will be
1102 generated if not supplied). If not passed, a commit will be performed at
1103 the end of the function, otherwise the caller is responsible for commiting.
1104 A flush will be performed either way.
1107 @return: the Maintainer object for the given maintainer
1109 privatetrans = False
1111 session = DBConn().session()
1114 q = session.query(Maintainer).filter_by(name=name)
1116 maintainer = Maintainer()
1117 maintainer.name = name
1118 session.add(maintainer)
1132 __all__.append('get_or_set_maintainer')
1134 def get_maintainer(maintainer_id, session=None):
1136 Return the name of the maintainer behind C{maintainer_id} or None if that
1137 maintainer_id is invalid.
1139 @type maintainer_id: int
1140 @param maintainer_id: the id of the maintainer
1143 @return: the Maintainer with this C{maintainer_id}
1146 privatetrans = False
1148 session = DBConn().session()
1152 return session.query(Maintainer).get(maintainer_id)
1157 __all__.append('get_maintainer')
1159 ################################################################################
1161 class NewComment(object):
1162 def __init__(self, *args, **kwargs):
1166 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1168 __all__.append('NewComment')
1170 def has_new_comment(package, version, session=None):
1172 Returns true if the given combination of C{package}, C{version} has a comment.
1174 @type package: string
1175 @param package: name of the package
1177 @type version: string
1178 @param version: package version
1180 @type session: Session
1181 @param session: Optional SQLA session object (a temporary one will be
1182 generated if not supplied)
1188 privatetrans = False
1190 session = DBConn().session()
1193 q = session.query(NewComment)
1194 q = q.filter_by(package=package)
1195 q = q.filter_by(version=version)
1204 __all__.append('has_new_comment')
1206 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1208 Returns (possibly empty) list of NewComment objects for the given
1211 @type package: string (optional)
1212 @param package: name of the package
1214 @type version: string (optional)
1215 @param version: package version
1217 @type comment_id: int (optional)
1218 @param comment_id: An id of a comment
1220 @type session: Session
1221 @param session: Optional SQLA session object (a temporary one will be
1222 generated if not supplied)
1225 @return: A (possibly empty) list of NewComment objects will be returned
1229 privatetrans = False
1231 session = DBConn().session()
1234 q = session.query(NewComment)
1235 if package is not None: q = q.filter_by(package=package)
1236 if version is not None: q = q.filter_by(version=version)
1237 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1246 __all__.append('get_new_comments')
1248 ################################################################################
1250 class Override(object):
1251 def __init__(self, *args, **kwargs):
1255 return '<Override %s (%s)>' % (self.package, self.suite_id)
1257 __all__.append('Override')
1259 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1261 Returns Override object for the given parameters
1263 @type package: string
1264 @param package: The name of the package
1266 @type suite: string, list or None
1267 @param suite: The name of the suite (or suites if a list) to limit to. If
1268 None, don't limit. Defaults to None.
1270 @type component: string, list or None
1271 @param component: The name of the component (or components if a list) to
1272 limit to. If None, don't limit. Defaults to None.
1274 @type overridetype: string, list or None
1275 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1276 limit to. If None, don't limit. Defaults to None.
1278 @type session: Session
1279 @param session: Optional SQLA session object (a temporary one will be
1280 generated if not supplied)
1283 @return: A (possibly empty) list of Override objects will be returned
1286 privatetrans = False
1288 session = DBConn().session()
1291 q = session.query(Override)
1292 q = q.filter_by(package=package)
1294 if suite is not None:
1295 if not isinstance(suite, list): suite = [suite]
1296 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1298 if component is not None:
1299 if not isinstance(component, list): component = [component]
1300 q = q.join(Component).filter(Component.component_name.in_(component))
1302 if overridetype is not None:
1303 if not isinstance(overridetype, list): overridetype = [overridetype]
1304 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1313 __all__.append('get_override')
1316 ################################################################################
1318 class OverrideType(object):
1319 def __init__(self, *args, **kwargs):
1323 return '<OverrideType %s>' % self.overridetype
1325 __all__.append('OverrideType')
1327 def get_override_type(override_type, session=None):
1329 Returns OverrideType object for given C{override type}.
1331 @type override_type: string
1332 @param override_type: The name of the override type
1334 @type session: Session
1335 @param session: Optional SQLA session object (a temporary one will be
1336 generated if not supplied)
1339 @return: the database id for the given override type
1342 privatetrans = False
1344 session = DBConn().session()
1347 q = session.query(OverrideType).filter_by(overridetype=override_type)
1359 __all__.append('get_override_type')
1361 ################################################################################
1363 class PendingContentAssociation(object):
1364 def __init__(self, *args, **kwargs):
1368 return '<PendingContentAssociation %s>' % self.pca_id
1370 __all__.append('PendingContentAssociation')
1372 def insert_pending_content_paths(package, fullpaths, session=None):
1374 Make sure given paths are temporarily associated with given
1378 @param package: the package to associate with should have been read in from the binary control file
1379 @type fullpaths: list
1380 @param fullpaths: the list of paths of the file being associated with the binary
1381 @type session: SQLAlchemy session
1382 @param session: Optional SQLAlchemy session. If this is passed, the caller
1383 is responsible for ensuring a transaction has begun and committing the
1384 results or rolling back based on the result code. If not passed, a commit
1385 will be performed at the end of the function
1387 @return: True upon success, False if there is a problem
1390 privatetrans = False
1393 session = DBConn().session()
1397 arch = get_architecture(package['Architecture'], session)
1398 arch_id = arch.arch_id
1400 # Remove any already existing recorded files for this package
1401 q = session.query(PendingContentAssociation)
1402 q = q.filter_by(package=package['Package'])
1403 q = q.filter_by(version=package['Version'])
1404 q = q.filter_by(architecture=arch_id)
1409 for fullpath in fullpaths:
1410 (path, file) = os.path.split(fullpath)
1412 if path.startswith( "./" ):
1415 filepath_id = get_or_set_contents_path_id(path, session)
1416 filename_id = get_or_set_contents_file_id(file, session)
1418 pathcache[fullpath] = (filepath_id, filename_id)
1420 for fullpath, dat in pathcache.items():
1421 pca = PendingContentAssociation()
1422 pca.package = package['Package']
1423 pca.version = package['Version']
1424 pca.filepath_id = dat[0]
1425 pca.filename_id = dat[1]
1426 pca.architecture = arch_id
1429 # Only commit if we set up the session ourself
1437 except Exception, e:
1438 traceback.print_exc()
1440 # Only rollback if we set up the session ourself
1447 __all__.append('insert_pending_content_paths')
1449 ################################################################################
1451 class Priority(object):
1452 def __init__(self, *args, **kwargs):
1455 def __eq__(self, val):
1456 if isinstance(val, str):
1457 return (self.priority == val)
1458 # This signals to use the normal comparison operator
1459 return NotImplemented
1461 def __ne__(self, val):
1462 if isinstance(val, str):
1463 return (self.priority != val)
1464 # This signals to use the normal comparison operator
1465 return NotImplemented
1468 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1470 __all__.append('Priority')
1472 def get_priority(priority, session=None):
1474 Returns Priority object for given C{priority name}.
1476 @type priority: string
1477 @param priority: The name of the priority
1479 @type session: Session
1480 @param session: Optional SQLA session object (a temporary one will be
1481 generated if not supplied)
1484 @return: Priority object for the given priority
1487 privatetrans = False
1489 session = DBConn().session()
1492 q = session.query(Priority).filter_by(priority=priority)
1504 __all__.append('get_priority')
1506 def get_priorities(session=None):
1508 Returns dictionary of priority names -> id mappings
1510 @type session: Session
1511 @param session: Optional SQL session object (a temporary one will be
1512 generated if not supplied)
1515 @return: dictionary of priority names -> id mappings
1517 privatetrans = False
1519 session = DBConn().session()
1523 q = session.query(Priority)
1525 ret[x.priority] = x.priority_id
1532 __all__.append('get_priorities')
1534 ################################################################################
1536 class Queue(object):
1537 def __init__(self, *args, **kwargs):
1541 return '<Queue %s>' % self.queue_name
1543 def autobuild_upload(self, changes, srcpath, session=None):
1545 Update queue_build database table used for incoming autobuild support.
1547 @type changes: Changes
1548 @param changes: changes object for the upload to process
1550 @type srcpath: string
1551 @param srcpath: path for the queue file entries/link destinations
1553 @type session: SQLAlchemy session
1554 @param session: Optional SQLAlchemy session. If this is passed, the
1555 caller is responsible for ensuring a transaction has begun and
1556 committing the results or rolling back based on the result code. If
1557 not passed, a commit will be performed at the end of the function,
1558 otherwise the caller is responsible for commiting.
1560 @rtype: NoneType or string
1561 @return: None if the operation failed, a string describing the error if not
1564 privatetrans = False
1566 session = DBConn().session()
1569 # TODO: Remove by moving queue config into the database
1572 for suitename in changes.changes["distribution"].keys():
1573 # TODO: Move into database as:
1574 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1575 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1576 # This also gets rid of the SecurityQueueBuild hack below
1577 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1581 s = get_suite(suitename, session)
1583 return "INTERNAL ERROR: Could not find suite %s" % suitename
1585 # TODO: Get from database as above
1586 dest_dir = conf["Dir::QueueBuild"]
1588 # TODO: Move into database as above
1589 if conf.FindB("Dinstall::SecurityQueueBuild"):
1590 dest_dir = os.path.join(dest_dir, suitename)
1592 for file_entry in changes.files.keys():
1593 src = os.path.join(srcpath, file_entry)
1594 dest = os.path.join(dest_dir, file_entry)
1596 # TODO: Move into database as above
1597 if conf.FindB("Dinstall::SecurityQueueBuild"):
1598 # Copy it since the original won't be readable by www-data
1599 utils.copy(src, dest)
1601 # Create a symlink to it
1602 os.symlink(src, dest)
1605 qb.suite_id = s.suite_id
1606 qb.queue_id = self.queue_id
1612 # If the .orig tarballs are in the pool, create a symlink to
1613 # them (if one doesn't already exist)
1614 for dsc_file in changes.dsc_files.keys():
1615 # Skip all files except orig tarballs
1616 if not re_is_orig_source.match(dsc_file):
1618 # Skip orig files not identified in the pool
1619 if not (changes.orig_files.has_key(dsc_file) and
1620 changes.orig_files[dsc_file].has_key("id")):
1622 orig_file_id = changes.orig_files[dsc_file]["id"]
1623 dest = os.path.join(dest_dir, dsc_file)
1625 # If it doesn't exist, create a symlink
1626 if not os.path.exists(dest):
1627 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1628 {'id': orig_file_id})
1631 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1633 src = os.path.join(res[0], res[1])
1634 os.symlink(src, dest)
1636 # Add it to the list of packages for later processing by apt-ftparchive
1638 qb.suite_id = s.suite_id
1639 qb.queue_id = self.queue_id
1644 # If it does, update things to ensure it's not removed prematurely
1646 qb = get_queue_build(dest, s.suite_id, session)
1658 __all__.append('Queue')
1660 def get_queue(queuename, session=None):
1662 Returns Queue object for given C{queue name}.
1664 @type queuename: string
1665 @param queuename: The name of the queue
1667 @type session: Session
1668 @param session: Optional SQLA session object (a temporary one will be
1669 generated if not supplied)
1672 @return: Queue object for the given queue
1675 privatetrans = False
1677 session = DBConn().session()
1680 q = session.query(Queue).filter_by(queue_name=queuename)
1691 __all__.append('get_queue')
1693 ################################################################################
1695 class QueueBuild(object):
1696 def __init__(self, *args, **kwargs):
1700 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1702 __all__.append('QueueBuild')
1704 def get_queue_build(filename, suite, session=None):
1706 Returns QueueBuild object for given C{filename} and C{suite}.
1708 @type filename: string
1709 @param filename: The name of the file
1711 @type suiteid: int or str
1712 @param suiteid: Suite name or ID
1714 @type session: Session
1715 @param session: Optional SQLA session object (a temporary one will be
1716 generated if not supplied)
1719 @return: Queue object for the given queue
1722 privatetrans = False
1724 session = DBConn().session()
1727 if isinstance(suite, int):
1728 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1730 q = session.query(QueueBuild).filter_by(filename=filename)
1731 q = q.join(Suite).filter_by(suite_name=suite)
1743 __all__.append('get_queue_build')
1745 ################################################################################
1747 class Section(object):
1748 def __init__(self, *args, **kwargs):
1751 def __eq__(self, val):
1752 if isinstance(val, str):
1753 return (self.section == val)
1754 # This signals to use the normal comparison operator
1755 return NotImplemented
1757 def __ne__(self, val):
1758 if isinstance(val, str):
1759 return (self.section != val)
1760 # This signals to use the normal comparison operator
1761 return NotImplemented
1764 return '<Section %s>' % self.section
1766 __all__.append('Section')
1768 def get_section(section, session=None):
1770 Returns Section object for given C{section name}.
1772 @type section: string
1773 @param section: The name of the section
1775 @type session: Session
1776 @param session: Optional SQLA session object (a temporary one will be
1777 generated if not supplied)
1780 @return: Section object for the given section name
1783 privatetrans = False
1785 session = DBConn().session()
1788 q = session.query(Section).filter_by(section=section)
1799 __all__.append('get_section')
1801 def get_sections(session=None):
1803 Returns dictionary of section names -> id mappings
1805 @type session: Session
1806 @param session: Optional SQL session object (a temporary one will be
1807 generated if not supplied)
1810 @return: dictionary of section names -> id mappings
1812 privatetrans = False
1814 session = DBConn().session()
1818 q = session.query(Section)
1820 ret[x.section] = x.section_id
1827 __all__.append('get_sections')
1829 ################################################################################
1831 class DBSource(object):
1832 def __init__(self, *args, **kwargs):
1836 return '<DBSource %s (%s)>' % (self.source, self.version)
1838 __all__.append('DBSource')
1840 def source_exists(source, source_version, suites = ["any"], session=None):
1842 Ensure that source exists somewhere in the archive for the binary
1843 upload being processed.
1844 1. exact match => 1.0-3
1845 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1847 @type package: string
1848 @param package: package source name
1850 @type source_version: string
1851 @param source_version: expected source version
1854 @param suites: list of suites to check in, default I{any}
1856 @type session: Session
1857 @param session: Optional SQLA session object (a temporary one will be
1858 generated if not supplied)
1861 @return: returns 1 if a source with expected version is found, otherwise 0
1865 privatetrans = False
1867 session = DBConn().session()
1873 for suite in suites:
1874 q = session.query(DBSource).filter_by(source=source)
1876 # source must exist in suite X, or in some other suite that's
1877 # mapped to X, recursively... silent-maps are counted too,
1878 # unreleased-maps aren't.
1879 maps = cnf.ValueList("SuiteMappings")[:]
1881 maps = [ m.split() for m in maps ]
1882 maps = [ (x[1], x[2]) for x in maps
1883 if x[0] == "map" or x[0] == "silent-map" ]
1886 if x[1] in s and x[0] not in s:
1889 q = q.join(SrcAssociation).join(Suite)
1890 q = q.filter(Suite.suite_name.in_(s))
1892 # Reduce the query results to a list of version numbers
1893 ql = [ j.version for j in q.all() ]
1896 if source_version in ql:
1900 from daklib.regexes import re_bin_only_nmu
1901 orig_source_version = re_bin_only_nmu.sub('', source_version)
1902 if orig_source_version in ql:
1905 # No source found so return not ok
1913 __all__.append('source_exists')
1915 def get_suites_source_in(source, session=None):
1917 Returns list of Suite objects which given C{source} name is in
1920 @param source: DBSource package name to search for
1923 @return: list of Suite objects for the given source
1926 privatetrans = False
1928 session = DBConn().session()
1931 ret = session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1938 __all__.append('get_suites_source_in')
1940 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1942 Returns list of DBSource objects for given C{source} name and other parameters
1945 @param source: DBSource package name to search for
1947 @type source: str or None
1948 @param source: DBSource version name to search for or None if not applicable
1950 @type dm_upload_allowed: bool
1951 @param dm_upload_allowed: If None, no effect. If True or False, only
1952 return packages with that dm_upload_allowed setting
1954 @type session: Session
1955 @param session: Optional SQL session object (a temporary one will be
1956 generated if not supplied)
1959 @return: list of DBSource objects for the given name (may be empty)
1961 privatetrans = False
1963 session = DBConn().session()
1966 q = session.query(DBSource).filter_by(source=source)
1968 if version is not None:
1969 q = q.filter_by(version=version)
1971 if dm_upload_allowed is not None:
1972 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1981 __all__.append('get_sources_from_name')
1983 def get_source_in_suite(source, suite, session=None):
1985 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1987 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1988 - B{suite} - a suite name, eg. I{unstable}
1990 @type source: string
1991 @param source: source package name
1994 @param suite: the suite name
1997 @return: the version for I{source} in I{suite}
2000 privatetrans = False
2002 session = DBConn().session()
2005 q = session.query(SrcAssociation)
2006 q = q.join('source').filter_by(source=source)
2007 q = q.join('suite').filter_by(suite_name=suite)
2012 # ???: Maybe we should just return the SrcAssociation object instead
2013 ret = q.one().source
2020 __all__.append('get_source_in_suite')
2022 ################################################################################
2024 class SrcAssociation(object):
2025 def __init__(self, *args, **kwargs):
2029 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2031 __all__.append('SrcAssociation')
2033 ################################################################################
2035 class SrcFormat(object):
2036 def __init__(self, *args, **kwargs):
2040 return '<SrcFormat %s>' % (self.format_name)
2042 __all__.append('SrcFormat')
2044 ################################################################################
2046 class SrcUploader(object):
2047 def __init__(self, *args, **kwargs):
2051 return '<SrcUploader %s>' % self.uploader_id
2053 __all__.append('SrcUploader')
2055 ################################################################################
2057 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2058 ('SuiteID', 'suite_id'),
2059 ('Version', 'version'),
2060 ('Origin', 'origin'),
2062 ('Description', 'description'),
2063 ('Untouchable', 'untouchable'),
2064 ('Announce', 'announce'),
2065 ('Codename', 'codename'),
2066 ('OverrideCodename', 'overridecodename'),
2067 ('ValidTime', 'validtime'),
2068 ('Priority', 'priority'),
2069 ('NotAutomatic', 'notautomatic'),
2070 ('CopyChanges', 'copychanges'),
2071 ('CopyDotDak', 'copydotdak'),
2072 ('CommentsDir', 'commentsdir'),
2073 ('OverrideSuite', 'overridesuite'),
2074 ('ChangelogBase', 'changelogbase')]
2077 class Suite(object):
2078 def __init__(self, *args, **kwargs):
2082 return '<Suite %s>' % self.suite_name
2084 def __eq__(self, val):
2085 if isinstance(val, str):
2086 return (self.suite_name == val)
2087 # This signals to use the normal comparison operator
2088 return NotImplemented
2090 def __ne__(self, val):
2091 if isinstance(val, str):
2092 return (self.suite_name != val)
2093 # This signals to use the normal comparison operator
2094 return NotImplemented
2098 for disp, field in SUITE_FIELDS:
2099 val = getattr(self, field, None)
2101 ret.append("%s: %s" % (disp, val))
2103 return "\n".join(ret)
2105 __all__.append('Suite')
2107 def get_suite_architecture(suite, architecture, session=None):
2109 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2113 @param suite: Suite name to search for
2115 @type architecture: str
2116 @param architecture: Architecture name to search for
2118 @type session: Session
2119 @param session: Optional SQL session object (a temporary one will be
2120 generated if not supplied)
2122 @rtype: SuiteArchitecture
2123 @return: the SuiteArchitecture object or None
2126 privatetrans = False
2128 session = DBConn().session()
2131 q = session.query(SuiteArchitecture)
2132 q = q.join(Architecture).filter_by(arch_string=architecture)
2133 q = q.join(Suite).filter_by(suite_name=suite)
2145 __all__.append('get_suite_architecture')
2147 def get_suite(suite, session=None):
2149 Returns Suite object for given C{suite name}.
2152 @param suite: The name of the suite
2154 @type session: Session
2155 @param session: Optional SQLA session object (a temporary one will be
2156 generated if not supplied)
2159 @return: Suite object for the requested suite name (None if not presenT)
2162 privatetrans = False
2164 session = DBConn().session()
2167 q = session.query(Suite).filter_by(suite_name=suite)
2179 __all__.append('get_suite')
2181 ################################################################################
2183 class SuiteArchitecture(object):
2184 def __init__(self, *args, **kwargs):
2188 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2190 __all__.append('SuiteArchitecture')
2192 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2194 Returns list of Architecture objects for given C{suite} name
2197 @param source: Suite name to search for
2199 @type skipsrc: boolean
2200 @param skipsrc: Whether to skip returning the 'source' architecture entry
2203 @type skipall: boolean
2204 @param skipall: Whether to skip returning the 'all' architecture entry
2207 @type session: Session
2208 @param session: Optional SQL session object (a temporary one will be
2209 generated if not supplied)
2212 @return: list of Architecture objects for the given name (may be empty)
2215 privatetrans = False
2217 session = DBConn().session()
2220 q = session.query(Architecture)
2221 q = q.join(SuiteArchitecture)
2222 q = q.join(Suite).filter_by(suite_name=suite)
2225 q = q.filter(Architecture.arch_string != 'source')
2228 q = q.filter(Architecture.arch_string != 'all')
2230 q = q.order_by('arch_string')
2239 __all__.append('get_suite_architectures')
2241 ################################################################################
2243 class SuiteSrcFormat(object):
2244 def __init__(self, *args, **kwargs):
2248 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2250 __all__.append('SuiteSrcFormat')
2252 def get_suite_src_formats(suite, session=None):
2254 Returns list of allowed SrcFormat for C{suite}.
2257 @param suite: Suite name to search for
2259 @type session: Session
2260 @param session: Optional SQL session object (a temporary one will be
2261 generated if not supplied)
2264 @return: the list of allowed source formats for I{suite}
2267 privatetrans = False
2269 session = DBConn().session()
2272 q = session.query(SrcFormat)
2273 q = q.join(SuiteSrcFormat)
2274 q = q.join(Suite).filter_by(suite_name=suite)
2275 q = q.order_by('format_name')
2284 __all__.append('get_suite_src_formats')
2286 ################################################################################
2289 def __init__(self, *args, **kwargs):
2292 def __eq__(self, val):
2293 if isinstance(val, str):
2294 return (self.uid == val)
2295 # This signals to use the normal comparison operator
2296 return NotImplemented
2298 def __ne__(self, val):
2299 if isinstance(val, str):
2300 return (self.uid != val)
2301 # This signals to use the normal comparison operator
2302 return NotImplemented
2305 return '<Uid %s (%s)>' % (self.uid, self.name)
2307 __all__.append('Uid')
2309 def add_database_user(uidname, session=None):
2311 Adds a database user
2313 @type uidname: string
2314 @param uidname: The uid of the user to add
2316 @type session: SQLAlchemy
2317 @param session: Optional SQL session object (a temporary one will be
2318 generated if not supplied). If not passed, a commit will be performed at
2319 the end of the function, otherwise the caller is responsible for commiting.
2322 @return: the uid object for the given uidname
2325 privatetrans = False
2327 session = DBConn().session()
2330 session.execute("CREATE USER :uid", {'uid': uidname})
2336 __all__.append('add_database_user')
2338 def get_or_set_uid(uidname, session=None):
2340 Returns uid object for given uidname.
2342 If no matching uidname is found, a row is inserted.
2344 @type uidname: string
2345 @param uidname: The uid to add
2347 @type session: SQLAlchemy
2348 @param session: Optional SQL session object (a temporary one will be
2349 generated if not supplied). If not passed, a commit will be performed at
2350 the end of the function, otherwise the caller is responsible for commiting.
2353 @return: the uid object for the given uidname
2356 privatetrans = False
2358 session = DBConn().session()
2361 q = session.query(Uid).filter_by(uid=uidname)
2380 __all__.append('get_or_set_uid')
2383 def get_uid_from_fingerprint(fpr, session=None):
2384 privatetrans = False
2386 session = DBConn().session()
2389 q = session.query(Uid)
2390 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2402 __all__.append('get_uid_from_fingerprint')
2404 ################################################################################
2406 class DBConn(Singleton):
2408 database module init.
2410 def __init__(self, *args, **kwargs):
2411 super(DBConn, self).__init__(*args, **kwargs)
2413 def _startup(self, *args, **kwargs):
2415 if kwargs.has_key('debug'):
2419 def __setuptables(self):
2420 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2421 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2422 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2423 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2424 self.tbl_component = Table('component', self.db_meta, autoload=True)
2425 self.tbl_config = Table('config', self.db_meta, autoload=True)
2426 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2427 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2428 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2429 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2430 self.tbl_files = Table('files', self.db_meta, autoload=True)
2431 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2432 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2433 self.tbl_location = Table('location', self.db_meta, autoload=True)
2434 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2435 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2436 self.tbl_override = Table('override', self.db_meta, autoload=True)
2437 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2438 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2439 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2440 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2441 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2442 self.tbl_section = Table('section', self.db_meta, autoload=True)
2443 self.tbl_source = Table('source', self.db_meta, autoload=True)
2444 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2445 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2446 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2447 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2448 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2449 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2450 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2452 def __setupmappers(self):
2453 mapper(Architecture, self.tbl_architecture,
2454 properties = dict(arch_id = self.tbl_architecture.c.id))
2456 mapper(Archive, self.tbl_archive,
2457 properties = dict(archive_id = self.tbl_archive.c.id,
2458 archive_name = self.tbl_archive.c.name))
2460 mapper(BinAssociation, self.tbl_bin_associations,
2461 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2462 suite_id = self.tbl_bin_associations.c.suite,
2463 suite = relation(Suite),
2464 binary_id = self.tbl_bin_associations.c.bin,
2465 binary = relation(DBBinary)))
2467 mapper(DBBinary, self.tbl_binaries,
2468 properties = dict(binary_id = self.tbl_binaries.c.id,
2469 package = self.tbl_binaries.c.package,
2470 version = self.tbl_binaries.c.version,
2471 maintainer_id = self.tbl_binaries.c.maintainer,
2472 maintainer = relation(Maintainer),
2473 source_id = self.tbl_binaries.c.source,
2474 source = relation(DBSource),
2475 arch_id = self.tbl_binaries.c.architecture,
2476 architecture = relation(Architecture),
2477 poolfile_id = self.tbl_binaries.c.file,
2478 poolfile = relation(PoolFile),
2479 binarytype = self.tbl_binaries.c.type,
2480 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2481 fingerprint = relation(Fingerprint),
2482 install_date = self.tbl_binaries.c.install_date,
2483 binassociations = relation(BinAssociation,
2484 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2486 mapper(Component, self.tbl_component,
2487 properties = dict(component_id = self.tbl_component.c.id,
2488 component_name = self.tbl_component.c.name))
2490 mapper(DBConfig, self.tbl_config,
2491 properties = dict(config_id = self.tbl_config.c.id))
2493 mapper(ContentAssociation, self.tbl_content_associations,
2494 properties = dict(ca_id = self.tbl_content_associations.c.id,
2495 filename_id = self.tbl_content_associations.c.filename,
2496 filename = relation(ContentFilename),
2497 filepath_id = self.tbl_content_associations.c.filepath,
2498 filepath = relation(ContentFilepath),
2499 binary_id = self.tbl_content_associations.c.binary_pkg,
2500 binary = relation(DBBinary)))
2503 mapper(ContentFilename, self.tbl_content_file_names,
2504 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2505 filename = self.tbl_content_file_names.c.file))
2507 mapper(ContentFilepath, self.tbl_content_file_paths,
2508 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2509 filepath = self.tbl_content_file_paths.c.path))
2511 mapper(DSCFile, self.tbl_dsc_files,
2512 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2513 source_id = self.tbl_dsc_files.c.source,
2514 source = relation(DBSource),
2515 poolfile_id = self.tbl_dsc_files.c.file,
2516 poolfile = relation(PoolFile)))
2518 mapper(PoolFile, self.tbl_files,
2519 properties = dict(file_id = self.tbl_files.c.id,
2520 filesize = self.tbl_files.c.size,
2521 location_id = self.tbl_files.c.location,
2522 location = relation(Location)))
2524 mapper(Fingerprint, self.tbl_fingerprint,
2525 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2526 uid_id = self.tbl_fingerprint.c.uid,
2527 uid = relation(Uid),
2528 keyring_id = self.tbl_fingerprint.c.keyring,
2529 keyring = relation(Keyring)))
2531 mapper(Keyring, self.tbl_keyrings,
2532 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2533 keyring_id = self.tbl_keyrings.c.id))
2535 mapper(Location, self.tbl_location,
2536 properties = dict(location_id = self.tbl_location.c.id,
2537 component_id = self.tbl_location.c.component,
2538 component = relation(Component),
2539 archive_id = self.tbl_location.c.archive,
2540 archive = relation(Archive),
2541 archive_type = self.tbl_location.c.type))
2543 mapper(Maintainer, self.tbl_maintainer,
2544 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2546 mapper(NewComment, self.tbl_new_comments,
2547 properties = dict(comment_id = self.tbl_new_comments.c.id))
2549 mapper(Override, self.tbl_override,
2550 properties = dict(suite_id = self.tbl_override.c.suite,
2551 suite = relation(Suite),
2552 component_id = self.tbl_override.c.component,
2553 component = relation(Component),
2554 priority_id = self.tbl_override.c.priority,
2555 priority = relation(Priority),
2556 section_id = self.tbl_override.c.section,
2557 section = relation(Section),
2558 overridetype_id = self.tbl_override.c.type,
2559 overridetype = relation(OverrideType)))
2561 mapper(OverrideType, self.tbl_override_type,
2562 properties = dict(overridetype = self.tbl_override_type.c.type,
2563 overridetype_id = self.tbl_override_type.c.id))
2565 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2566 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2567 filepath_id = self.tbl_pending_content_associations.c.filepath,
2568 filepath = relation(ContentFilepath),
2569 filename_id = self.tbl_pending_content_associations.c.filename,
2570 filename = relation(ContentFilename)))
2572 mapper(Priority, self.tbl_priority,
2573 properties = dict(priority_id = self.tbl_priority.c.id))
2575 mapper(Queue, self.tbl_queue,
2576 properties = dict(queue_id = self.tbl_queue.c.id))
2578 mapper(QueueBuild, self.tbl_queue_build,
2579 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2580 queue_id = self.tbl_queue_build.c.queue,
2581 queue = relation(Queue, backref='queuebuild')))
2583 mapper(Section, self.tbl_section,
2584 properties = dict(section_id = self.tbl_section.c.id))
2586 mapper(DBSource, self.tbl_source,
2587 properties = dict(source_id = self.tbl_source.c.id,
2588 version = self.tbl_source.c.version,
2589 maintainer_id = self.tbl_source.c.maintainer,
2590 maintainer = relation(Maintainer,
2591 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2592 poolfile_id = self.tbl_source.c.file,
2593 poolfile = relation(PoolFile),
2594 fingerprint_id = self.tbl_source.c.sig_fpr,
2595 fingerprint = relation(Fingerprint),
2596 changedby_id = self.tbl_source.c.changedby,
2597 changedby = relation(Maintainer,
2598 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2599 srcfiles = relation(DSCFile,
2600 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2601 srcassociations = relation(SrcAssociation,
2602 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2604 mapper(SrcAssociation, self.tbl_src_associations,
2605 properties = dict(sa_id = self.tbl_src_associations.c.id,
2606 suite_id = self.tbl_src_associations.c.suite,
2607 suite = relation(Suite),
2608 source_id = self.tbl_src_associations.c.source,
2609 source = relation(DBSource)))
2611 mapper(SrcFormat, self.tbl_src_format,
2612 properties = dict(src_format_id = self.tbl_src_format.c.id,
2613 format_name = self.tbl_src_format.c.format_name))
2615 mapper(SrcUploader, self.tbl_src_uploaders,
2616 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2617 source_id = self.tbl_src_uploaders.c.source,
2618 source = relation(DBSource,
2619 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2620 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2621 maintainer = relation(Maintainer,
2622 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2624 mapper(Suite, self.tbl_suite,
2625 properties = dict(suite_id = self.tbl_suite.c.id))
2627 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2628 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2629 suite = relation(Suite, backref='suitearchitectures'),
2630 arch_id = self.tbl_suite_architectures.c.architecture,
2631 architecture = relation(Architecture)))
2633 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2634 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2635 suite = relation(Suite, backref='suitesrcformats'),
2636 src_format_id = self.tbl_suite_src_formats.c.src_format,
2637 src_format = relation(SrcFormat)))
2639 mapper(Uid, self.tbl_uid,
2640 properties = dict(uid_id = self.tbl_uid.c.id,
2641 fingerprint = relation(Fingerprint)))
2643 ## Connection functions
2644 def __createconn(self):
2645 from config import Config
2649 connstr = "postgres://%s" % cnf["DB::Host"]
2650 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2651 connstr += ":%s" % cnf["DB::Port"]
2652 connstr += "/%s" % cnf["DB::Name"]
2655 connstr = "postgres:///%s" % cnf["DB::Name"]
2656 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2657 connstr += "?port=%s" % cnf["DB::Port"]
2659 self.db_pg = create_engine(connstr, echo=self.debug)
2660 self.db_meta = MetaData()
2661 self.db_meta.bind = self.db_pg
2662 self.db_smaker = sessionmaker(bind=self.db_pg,
2666 self.__setuptables()
2667 self.__setupmappers()
2670 return self.db_smaker()
2672 __all__.append('DBConn')