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.tar.gz is in the pool, create a symlink to
1613 # it (if one doesn't already exist)
1614 if changes.orig_tar_id:
1615 # Determine the .orig.tar.gz file name
1616 for dsc_file in changes.dsc_files.keys():
1617 if dsc_file.endswith(".orig.tar.gz"):
1620 dest = os.path.join(dest_dir, filename)
1622 # If it doesn't exist, create a symlink
1623 if not os.path.exists(dest):
1624 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1625 {'id': changes.orig_tar_id})
1628 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1630 src = os.path.join(res[0], res[1])
1631 os.symlink(src, dest)
1633 # Add it to the list of packages for later processing by apt-ftparchive
1635 qb.suite_id = s.suite_id
1636 qb.queue_id = self.queue_id
1641 # If it does, update things to ensure it's not removed prematurely
1643 qb = get_queue_build(dest, s.suite_id, session)
1655 __all__.append('Queue')
1657 def get_queue(queuename, session=None):
1659 Returns Queue object for given C{queue name}.
1661 @type queuename: string
1662 @param queuename: The name of the queue
1664 @type session: Session
1665 @param session: Optional SQLA session object (a temporary one will be
1666 generated if not supplied)
1669 @return: Queue object for the given queue
1672 privatetrans = False
1674 session = DBConn().session()
1677 q = session.query(Queue).filter_by(queue_name=queuename)
1688 __all__.append('get_queue')
1690 ################################################################################
1692 class QueueBuild(object):
1693 def __init__(self, *args, **kwargs):
1697 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1699 __all__.append('QueueBuild')
1701 def get_queue_build(filename, suite, session=None):
1703 Returns QueueBuild object for given C{filename} and C{suite}.
1705 @type filename: string
1706 @param filename: The name of the file
1708 @type suiteid: int or str
1709 @param suiteid: Suite name or ID
1711 @type session: Session
1712 @param session: Optional SQLA session object (a temporary one will be
1713 generated if not supplied)
1716 @return: Queue object for the given queue
1719 privatetrans = False
1721 session = DBConn().session()
1724 if isinstance(suite, int):
1725 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1727 q = session.query(QueueBuild).filter_by(filename=filename)
1728 q = q.join(Suite).filter_by(suite_name=suite)
1740 __all__.append('get_queue_build')
1742 ################################################################################
1744 class Section(object):
1745 def __init__(self, *args, **kwargs):
1748 def __eq__(self, val):
1749 if isinstance(val, str):
1750 return (self.section == val)
1751 # This signals to use the normal comparison operator
1752 return NotImplemented
1754 def __ne__(self, val):
1755 if isinstance(val, str):
1756 return (self.section != val)
1757 # This signals to use the normal comparison operator
1758 return NotImplemented
1761 return '<Section %s>' % self.section
1763 __all__.append('Section')
1765 def get_section(section, session=None):
1767 Returns Section object for given C{section name}.
1769 @type section: string
1770 @param section: The name of the section
1772 @type session: Session
1773 @param session: Optional SQLA session object (a temporary one will be
1774 generated if not supplied)
1777 @return: Section object for the given section name
1780 privatetrans = False
1782 session = DBConn().session()
1785 q = session.query(Section).filter_by(section=section)
1796 __all__.append('get_section')
1798 def get_sections(session=None):
1800 Returns dictionary of section names -> id mappings
1802 @type session: Session
1803 @param session: Optional SQL session object (a temporary one will be
1804 generated if not supplied)
1807 @return: dictionary of section names -> id mappings
1809 privatetrans = False
1811 session = DBConn().session()
1815 q = session.query(Section)
1817 ret[x.section] = x.section_id
1824 __all__.append('get_sections')
1826 ################################################################################
1828 class DBSource(object):
1829 def __init__(self, *args, **kwargs):
1833 return '<DBSource %s (%s)>' % (self.source, self.version)
1835 __all__.append('DBSource')
1837 def source_exists(source, source_version, suites = ["any"], session=None):
1839 Ensure that source exists somewhere in the archive for the binary
1840 upload being processed.
1841 1. exact match => 1.0-3
1842 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1844 @type package: string
1845 @param package: package source name
1847 @type source_version: string
1848 @param source_version: expected source version
1851 @param suites: list of suites to check in, default I{any}
1853 @type session: Session
1854 @param session: Optional SQLA session object (a temporary one will be
1855 generated if not supplied)
1858 @return: returns 1 if a source with expected version is found, otherwise 0
1862 privatetrans = False
1864 session = DBConn().session()
1870 for suite in suites:
1871 q = session.query(DBSource).filter_by(source=source)
1873 # source must exist in suite X, or in some other suite that's
1874 # mapped to X, recursively... silent-maps are counted too,
1875 # unreleased-maps aren't.
1876 maps = cnf.ValueList("SuiteMappings")[:]
1878 maps = [ m.split() for m in maps ]
1879 maps = [ (x[1], x[2]) for x in maps
1880 if x[0] == "map" or x[0] == "silent-map" ]
1883 if x[1] in s and x[0] not in s:
1886 q = q.join(SrcAssociation).join(Suite)
1887 q = q.filter(Suite.suite_name.in_(s))
1889 # Reduce the query results to a list of version numbers
1890 ql = [ j.version for j in q.all() ]
1893 if source_version in ql:
1897 from daklib.regexes import re_bin_only_nmu
1898 orig_source_version = re_bin_only_nmu.sub('', source_version)
1899 if orig_source_version in ql:
1902 # No source found so return not ok
1910 __all__.append('source_exists')
1912 def get_suites_source_in(source, session=None):
1914 Returns list of Suite objects which given C{source} name is in
1917 @param source: DBSource package name to search for
1920 @return: list of Suite objects for the given source
1923 privatetrans = False
1925 session = DBConn().session()
1928 ret = session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1935 __all__.append('get_suites_source_in')
1937 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1939 Returns list of DBSource objects for given C{source} name and other parameters
1942 @param source: DBSource package name to search for
1944 @type source: str or None
1945 @param source: DBSource version name to search for or None if not applicable
1947 @type dm_upload_allowed: bool
1948 @param dm_upload_allowed: If None, no effect. If True or False, only
1949 return packages with that dm_upload_allowed setting
1951 @type session: Session
1952 @param session: Optional SQL session object (a temporary one will be
1953 generated if not supplied)
1956 @return: list of DBSource objects for the given name (may be empty)
1958 privatetrans = False
1960 session = DBConn().session()
1963 q = session.query(DBSource).filter_by(source=source)
1965 if version is not None:
1966 q = q.filter_by(version=version)
1968 if dm_upload_allowed is not None:
1969 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1978 __all__.append('get_sources_from_name')
1980 def get_source_in_suite(source, suite, session=None):
1982 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1984 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1985 - B{suite} - a suite name, eg. I{unstable}
1987 @type source: string
1988 @param source: source package name
1991 @param suite: the suite name
1994 @return: the version for I{source} in I{suite}
1997 privatetrans = False
1999 session = DBConn().session()
2002 q = session.query(SrcAssociation)
2003 q = q.join('source').filter_by(source=source)
2004 q = q.join('suite').filter_by(suite_name=suite)
2009 # ???: Maybe we should just return the SrcAssociation object instead
2010 ret = q.one().source
2017 __all__.append('get_source_in_suite')
2019 ################################################################################
2021 class SrcAssociation(object):
2022 def __init__(self, *args, **kwargs):
2026 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
2028 __all__.append('SrcAssociation')
2030 ################################################################################
2032 class SrcUploader(object):
2033 def __init__(self, *args, **kwargs):
2037 return '<SrcUploader %s>' % self.uploader_id
2039 __all__.append('SrcUploader')
2041 ################################################################################
2043 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2044 ('SuiteID', 'suite_id'),
2045 ('Version', 'version'),
2046 ('Origin', 'origin'),
2048 ('Description', 'description'),
2049 ('Untouchable', 'untouchable'),
2050 ('Announce', 'announce'),
2051 ('Codename', 'codename'),
2052 ('OverrideCodename', 'overridecodename'),
2053 ('ValidTime', 'validtime'),
2054 ('Priority', 'priority'),
2055 ('NotAutomatic', 'notautomatic'),
2056 ('CopyChanges', 'copychanges'),
2057 ('CopyDotDak', 'copydotdak'),
2058 ('CommentsDir', 'commentsdir'),
2059 ('OverrideSuite', 'overridesuite'),
2060 ('ChangelogBase', 'changelogbase')]
2063 class Suite(object):
2064 def __init__(self, *args, **kwargs):
2068 return '<Suite %s>' % self.suite_name
2070 def __eq__(self, val):
2071 if isinstance(val, str):
2072 return (self.suite_name == val)
2073 # This signals to use the normal comparison operator
2074 return NotImplemented
2076 def __ne__(self, val):
2077 if isinstance(val, str):
2078 return (self.suite_name != val)
2079 # This signals to use the normal comparison operator
2080 return NotImplemented
2084 for disp, field in SUITE_FIELDS:
2085 val = getattr(self, field, None)
2087 ret.append("%s: %s" % (disp, val))
2089 return "\n".join(ret)
2091 __all__.append('Suite')
2093 def get_suite_architecture(suite, architecture, session=None):
2095 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2099 @param suite: Suite name to search for
2101 @type architecture: str
2102 @param architecture: Architecture name to search for
2104 @type session: Session
2105 @param session: Optional SQL session object (a temporary one will be
2106 generated if not supplied)
2108 @rtype: SuiteArchitecture
2109 @return: the SuiteArchitecture object or None
2112 privatetrans = False
2114 session = DBConn().session()
2117 q = session.query(SuiteArchitecture)
2118 q = q.join(Architecture).filter_by(arch_string=architecture)
2119 q = q.join(Suite).filter_by(suite_name=suite)
2131 __all__.append('get_suite_architecture')
2133 def get_suite(suite, session=None):
2135 Returns Suite object for given C{suite name}.
2138 @param suite: The name of the suite
2140 @type session: Session
2141 @param session: Optional SQLA session object (a temporary one will be
2142 generated if not supplied)
2145 @return: Suite object for the requested suite name (None if not presenT)
2148 privatetrans = False
2150 session = DBConn().session()
2153 q = session.query(Suite).filter_by(suite_name=suite)
2165 __all__.append('get_suite')
2167 ################################################################################
2169 class SuiteArchitecture(object):
2170 def __init__(self, *args, **kwargs):
2174 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2176 __all__.append('SuiteArchitecture')
2178 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2180 Returns list of Architecture objects for given C{suite} name
2183 @param source: Suite name to search for
2185 @type skipsrc: boolean
2186 @param skipsrc: Whether to skip returning the 'source' architecture entry
2189 @type skipall: boolean
2190 @param skipall: Whether to skip returning the 'all' architecture entry
2193 @type session: Session
2194 @param session: Optional SQL session object (a temporary one will be
2195 generated if not supplied)
2198 @return: list of Architecture objects for the given name (may be empty)
2201 privatetrans = False
2203 session = DBConn().session()
2206 q = session.query(Architecture)
2207 q = q.join(SuiteArchitecture)
2208 q = q.join(Suite).filter_by(suite_name=suite)
2211 q = q.filter(Architecture.arch_string != 'source')
2214 q = q.filter(Architecture.arch_string != 'all')
2216 q = q.order_by('arch_string')
2225 __all__.append('get_suite_architectures')
2227 ################################################################################
2230 def __init__(self, *args, **kwargs):
2233 def __eq__(self, val):
2234 if isinstance(val, str):
2235 return (self.uid == val)
2236 # This signals to use the normal comparison operator
2237 return NotImplemented
2239 def __ne__(self, val):
2240 if isinstance(val, str):
2241 return (self.uid != val)
2242 # This signals to use the normal comparison operator
2243 return NotImplemented
2246 return '<Uid %s (%s)>' % (self.uid, self.name)
2248 __all__.append('Uid')
2250 def add_database_user(uidname, session=None):
2252 Adds a database user
2254 @type uidname: string
2255 @param uidname: The uid of the user to add
2257 @type session: SQLAlchemy
2258 @param session: Optional SQL session object (a temporary one will be
2259 generated if not supplied). If not passed, a commit will be performed at
2260 the end of the function, otherwise the caller is responsible for commiting.
2263 @return: the uid object for the given uidname
2266 privatetrans = False
2268 session = DBConn().session()
2271 session.execute("CREATE USER :uid", {'uid': uidname})
2277 __all__.append('add_database_user')
2279 def get_or_set_uid(uidname, session=None):
2281 Returns uid object for given uidname.
2283 If no matching uidname is found, a row is inserted.
2285 @type uidname: string
2286 @param uidname: The uid to add
2288 @type session: SQLAlchemy
2289 @param session: Optional SQL session object (a temporary one will be
2290 generated if not supplied). If not passed, a commit will be performed at
2291 the end of the function, otherwise the caller is responsible for commiting.
2294 @return: the uid object for the given uidname
2297 privatetrans = False
2299 session = DBConn().session()
2302 q = session.query(Uid).filter_by(uid=uidname)
2321 __all__.append('get_or_set_uid')
2324 def get_uid_from_fingerprint(fpr, session=None):
2325 privatetrans = False
2327 session = DBConn().session()
2330 q = session.query(Uid)
2331 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2343 __all__.append('get_uid_from_fingerprint')
2345 ################################################################################
2347 class DBConn(Singleton):
2349 database module init.
2351 def __init__(self, *args, **kwargs):
2352 super(DBConn, self).__init__(*args, **kwargs)
2354 def _startup(self, *args, **kwargs):
2356 if kwargs.has_key('debug'):
2360 def __setuptables(self):
2361 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2362 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2363 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2364 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2365 self.tbl_component = Table('component', self.db_meta, autoload=True)
2366 self.tbl_config = Table('config', self.db_meta, autoload=True)
2367 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2368 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2369 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2370 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2371 self.tbl_files = Table('files', self.db_meta, autoload=True)
2372 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2373 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2374 self.tbl_location = Table('location', self.db_meta, autoload=True)
2375 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2376 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2377 self.tbl_override = Table('override', self.db_meta, autoload=True)
2378 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2379 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2380 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2381 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2382 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2383 self.tbl_section = Table('section', self.db_meta, autoload=True)
2384 self.tbl_source = Table('source', self.db_meta, autoload=True)
2385 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2386 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2387 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2388 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2389 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2391 def __setupmappers(self):
2392 mapper(Architecture, self.tbl_architecture,
2393 properties = dict(arch_id = self.tbl_architecture.c.id))
2395 mapper(Archive, self.tbl_archive,
2396 properties = dict(archive_id = self.tbl_archive.c.id,
2397 archive_name = self.tbl_archive.c.name))
2399 mapper(BinAssociation, self.tbl_bin_associations,
2400 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2401 suite_id = self.tbl_bin_associations.c.suite,
2402 suite = relation(Suite),
2403 binary_id = self.tbl_bin_associations.c.bin,
2404 binary = relation(DBBinary)))
2406 mapper(DBBinary, self.tbl_binaries,
2407 properties = dict(binary_id = self.tbl_binaries.c.id,
2408 package = self.tbl_binaries.c.package,
2409 version = self.tbl_binaries.c.version,
2410 maintainer_id = self.tbl_binaries.c.maintainer,
2411 maintainer = relation(Maintainer),
2412 source_id = self.tbl_binaries.c.source,
2413 source = relation(DBSource),
2414 arch_id = self.tbl_binaries.c.architecture,
2415 architecture = relation(Architecture),
2416 poolfile_id = self.tbl_binaries.c.file,
2417 poolfile = relation(PoolFile),
2418 binarytype = self.tbl_binaries.c.type,
2419 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2420 fingerprint = relation(Fingerprint),
2421 install_date = self.tbl_binaries.c.install_date,
2422 binassociations = relation(BinAssociation,
2423 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2425 mapper(Component, self.tbl_component,
2426 properties = dict(component_id = self.tbl_component.c.id,
2427 component_name = self.tbl_component.c.name))
2429 mapper(DBConfig, self.tbl_config,
2430 properties = dict(config_id = self.tbl_config.c.id))
2432 mapper(ContentAssociation, self.tbl_content_associations,
2433 properties = dict(ca_id = self.tbl_content_associations.c.id,
2434 filename_id = self.tbl_content_associations.c.filename,
2435 filename = relation(ContentFilename),
2436 filepath_id = self.tbl_content_associations.c.filepath,
2437 filepath = relation(ContentFilepath),
2438 binary_id = self.tbl_content_associations.c.binary_pkg,
2439 binary = relation(DBBinary)))
2442 mapper(ContentFilename, self.tbl_content_file_names,
2443 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2444 filename = self.tbl_content_file_names.c.file))
2446 mapper(ContentFilepath, self.tbl_content_file_paths,
2447 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2448 filepath = self.tbl_content_file_paths.c.path))
2450 mapper(DSCFile, self.tbl_dsc_files,
2451 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2452 source_id = self.tbl_dsc_files.c.source,
2453 source = relation(DBSource),
2454 poolfile_id = self.tbl_dsc_files.c.file,
2455 poolfile = relation(PoolFile)))
2457 mapper(PoolFile, self.tbl_files,
2458 properties = dict(file_id = self.tbl_files.c.id,
2459 filesize = self.tbl_files.c.size,
2460 location_id = self.tbl_files.c.location,
2461 location = relation(Location)))
2463 mapper(Fingerprint, self.tbl_fingerprint,
2464 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2465 uid_id = self.tbl_fingerprint.c.uid,
2466 uid = relation(Uid),
2467 keyring_id = self.tbl_fingerprint.c.keyring,
2468 keyring = relation(Keyring)))
2470 mapper(Keyring, self.tbl_keyrings,
2471 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2472 keyring_id = self.tbl_keyrings.c.id))
2474 mapper(Location, self.tbl_location,
2475 properties = dict(location_id = self.tbl_location.c.id,
2476 component_id = self.tbl_location.c.component,
2477 component = relation(Component),
2478 archive_id = self.tbl_location.c.archive,
2479 archive = relation(Archive),
2480 archive_type = self.tbl_location.c.type))
2482 mapper(Maintainer, self.tbl_maintainer,
2483 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2485 mapper(NewComment, self.tbl_new_comments,
2486 properties = dict(comment_id = self.tbl_new_comments.c.id))
2488 mapper(Override, self.tbl_override,
2489 properties = dict(suite_id = self.tbl_override.c.suite,
2490 suite = relation(Suite),
2491 component_id = self.tbl_override.c.component,
2492 component = relation(Component),
2493 priority_id = self.tbl_override.c.priority,
2494 priority = relation(Priority),
2495 section_id = self.tbl_override.c.section,
2496 section = relation(Section),
2497 overridetype_id = self.tbl_override.c.type,
2498 overridetype = relation(OverrideType)))
2500 mapper(OverrideType, self.tbl_override_type,
2501 properties = dict(overridetype = self.tbl_override_type.c.type,
2502 overridetype_id = self.tbl_override_type.c.id))
2504 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2505 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2506 filepath_id = self.tbl_pending_content_associations.c.filepath,
2507 filepath = relation(ContentFilepath),
2508 filename_id = self.tbl_pending_content_associations.c.filename,
2509 filename = relation(ContentFilename)))
2511 mapper(Priority, self.tbl_priority,
2512 properties = dict(priority_id = self.tbl_priority.c.id))
2514 mapper(Queue, self.tbl_queue,
2515 properties = dict(queue_id = self.tbl_queue.c.id))
2517 mapper(QueueBuild, self.tbl_queue_build,
2518 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2519 queue_id = self.tbl_queue_build.c.queue,
2520 queue = relation(Queue, backref='queuebuild')))
2522 mapper(Section, self.tbl_section,
2523 properties = dict(section_id = self.tbl_section.c.id))
2525 mapper(DBSource, self.tbl_source,
2526 properties = dict(source_id = self.tbl_source.c.id,
2527 version = self.tbl_source.c.version,
2528 maintainer_id = self.tbl_source.c.maintainer,
2529 maintainer = relation(Maintainer,
2530 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2531 poolfile_id = self.tbl_source.c.file,
2532 poolfile = relation(PoolFile),
2533 fingerprint_id = self.tbl_source.c.sig_fpr,
2534 fingerprint = relation(Fingerprint),
2535 changedby_id = self.tbl_source.c.changedby,
2536 changedby = relation(Maintainer,
2537 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2538 srcfiles = relation(DSCFile,
2539 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2540 srcassociations = relation(SrcAssociation,
2541 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2543 mapper(SrcAssociation, self.tbl_src_associations,
2544 properties = dict(sa_id = self.tbl_src_associations.c.id,
2545 suite_id = self.tbl_src_associations.c.suite,
2546 suite = relation(Suite),
2547 source_id = self.tbl_src_associations.c.source,
2548 source = relation(DBSource)))
2550 mapper(SrcUploader, self.tbl_src_uploaders,
2551 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2552 source_id = self.tbl_src_uploaders.c.source,
2553 source = relation(DBSource,
2554 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2555 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2556 maintainer = relation(Maintainer,
2557 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2559 mapper(Suite, self.tbl_suite,
2560 properties = dict(suite_id = self.tbl_suite.c.id))
2562 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2563 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2564 suite = relation(Suite, backref='suitearchitectures'),
2565 arch_id = self.tbl_suite_architectures.c.architecture,
2566 architecture = relation(Architecture)))
2568 mapper(Uid, self.tbl_uid,
2569 properties = dict(uid_id = self.tbl_uid.c.id,
2570 fingerprint = relation(Fingerprint)))
2572 ## Connection functions
2573 def __createconn(self):
2574 from config import Config
2578 connstr = "postgres://%s" % cnf["DB::Host"]
2579 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2580 connstr += ":%s" % cnf["DB::Port"]
2581 connstr += "/%s" % cnf["DB::Name"]
2584 connstr = "postgres:///%s" % cnf["DB::Name"]
2585 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2586 connstr += "?port=%s" % cnf["DB::Port"]
2588 self.db_pg = create_engine(connstr, echo=self.debug)
2589 self.db_meta = MetaData()
2590 self.db_meta.bind = self.db_pg
2591 self.db_smaker = sessionmaker(bind=self.db_pg,
2595 self.__setuptables()
2596 self.__setupmappers()
2599 return self.db_smaker()
2601 __all__.append('DBConn')