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 ################################################################################
986 class Location(object):
987 def __init__(self, *args, **kwargs):
991 return '<Location %s (%s)>' % (self.path, self.location_id)
993 __all__.append('Location')
995 def get_location(location, component=None, archive=None, session=None):
997 Returns Location object for the given combination of location, component
1000 @type location: string
1001 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
1003 @type component: string
1004 @param component: the component name (if None, no restriction applied)
1006 @type archive: string
1007 @param archive_id: the archive name (if None, no restriction applied)
1009 @rtype: Location / None
1010 @return: Either a Location object or None if one can't be found
1013 privatetrans = False
1015 session = DBConn().session()
1018 q = session.query(Location).filter_by(path=location)
1020 if archive is not None:
1021 q = q.join(Archive).filter_by(archive_name=archive)
1023 if component is not None:
1024 q = q.join(Component).filter_by(component_name=component)
1036 __all__.append('get_location')
1038 ################################################################################
1040 class Maintainer(object):
1041 def __init__(self, *args, **kwargs):
1045 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1047 def get_split_maintainer(self):
1048 if not hasattr(self, 'name') or self.name is None:
1049 return ('', '', '', '')
1051 return fix_maintainer(self.name.strip())
1053 __all__.append('Maintainer')
1055 def get_or_set_maintainer(name, session=None):
1057 Returns Maintainer object for given maintainer name.
1059 If no matching maintainer name is found, a row is inserted.
1062 @param name: The maintainer name to add
1064 @type session: SQLAlchemy
1065 @param session: Optional SQL session object (a temporary one will be
1066 generated if not supplied). If not passed, a commit will be performed at
1067 the end of the function, otherwise the caller is responsible for commiting.
1068 A flush will be performed either way.
1071 @return: the Maintainer object for the given maintainer
1073 privatetrans = False
1075 session = DBConn().session()
1078 q = session.query(Maintainer).filter_by(name=name)
1080 maintainer = Maintainer()
1081 maintainer.name = name
1082 session.add(maintainer)
1096 __all__.append('get_or_set_maintainer')
1098 ################################################################################
1100 class NewComment(object):
1101 def __init__(self, *args, **kwargs):
1105 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1107 __all__.append('NewComment')
1109 def has_new_comment(package, version, session=None):
1111 Returns true if the given combination of C{package}, C{version} has a comment.
1113 @type package: string
1114 @param package: name of the package
1116 @type version: string
1117 @param version: package version
1119 @type session: Session
1120 @param session: Optional SQLA session object (a temporary one will be
1121 generated if not supplied)
1127 privatetrans = False
1129 session = DBConn().session()
1132 q = session.query(NewComment)
1133 q = q.filter_by(package=package)
1134 q = q.filter_by(version=version)
1143 __all__.append('has_new_comment')
1145 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1147 Returns (possibly empty) list of NewComment objects for the given
1150 @type package: string (optional)
1151 @param package: name of the package
1153 @type version: string (optional)
1154 @param version: package version
1156 @type comment_id: int (optional)
1157 @param comment_id: An id of a comment
1159 @type session: Session
1160 @param session: Optional SQLA session object (a temporary one will be
1161 generated if not supplied)
1164 @return: A (possibly empty) list of NewComment objects will be returned
1168 privatetrans = False
1170 session = DBConn().session()
1173 q = session.query(NewComment)
1174 if package is not None: q = q.filter_by(package=package)
1175 if version is not None: q = q.filter_by(version=version)
1176 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1185 __all__.append('get_new_comments')
1187 ################################################################################
1189 class Override(object):
1190 def __init__(self, *args, **kwargs):
1194 return '<Override %s (%s)>' % (self.package, self.suite_id)
1196 __all__.append('Override')
1198 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1200 Returns Override object for the given parameters
1202 @type package: string
1203 @param package: The name of the package
1205 @type suite: string, list or None
1206 @param suite: The name of the suite (or suites if a list) to limit to. If
1207 None, don't limit. Defaults to None.
1209 @type component: string, list or None
1210 @param component: The name of the component (or components if a list) to
1211 limit to. If None, don't limit. Defaults to None.
1213 @type overridetype: string, list or None
1214 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1215 limit to. If None, don't limit. Defaults to None.
1217 @type session: Session
1218 @param session: Optional SQLA session object (a temporary one will be
1219 generated if not supplied)
1222 @return: A (possibly empty) list of Override objects will be returned
1225 privatetrans = False
1227 session = DBConn().session()
1230 q = session.query(Override)
1231 q = q.filter_by(package=package)
1233 if suite is not None:
1234 if not isinstance(suite, list): suite = [suite]
1235 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1237 if component is not None:
1238 if not isinstance(component, list): component = [component]
1239 q = q.join(Component).filter(Component.component_name.in_(component))
1241 if overridetype is not None:
1242 if not isinstance(overridetype, list): overridetype = [overridetype]
1243 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1252 __all__.append('get_override')
1255 ################################################################################
1257 class OverrideType(object):
1258 def __init__(self, *args, **kwargs):
1262 return '<OverrideType %s>' % self.overridetype
1264 __all__.append('OverrideType')
1266 def get_override_type(override_type, session=None):
1268 Returns OverrideType object for given C{override type}.
1270 @type override_type: string
1271 @param override_type: The name of the override type
1273 @type session: Session
1274 @param session: Optional SQLA session object (a temporary one will be
1275 generated if not supplied)
1278 @return: the database id for the given override type
1281 privatetrans = False
1283 session = DBConn().session()
1286 q = session.query(OverrideType).filter_by(overridetype=override_type)
1298 __all__.append('get_override_type')
1300 ################################################################################
1302 class PendingContentAssociation(object):
1303 def __init__(self, *args, **kwargs):
1307 return '<PendingContentAssociation %s>' % self.pca_id
1309 __all__.append('PendingContentAssociation')
1311 def insert_pending_content_paths(package, fullpaths, session=None):
1313 Make sure given paths are temporarily associated with given
1317 @param package: the package to associate with should have been read in from the binary control file
1318 @type fullpaths: list
1319 @param fullpaths: the list of paths of the file being associated with the binary
1320 @type session: SQLAlchemy session
1321 @param session: Optional SQLAlchemy session. If this is passed, the caller
1322 is responsible for ensuring a transaction has begun and committing the
1323 results or rolling back based on the result code. If not passed, a commit
1324 will be performed at the end of the function
1326 @return: True upon success, False if there is a problem
1329 privatetrans = False
1332 session = DBConn().session()
1336 arch = get_architecture(package['Architecture'], session)
1337 arch_id = arch.arch_id
1339 # Remove any already existing recorded files for this package
1340 q = session.query(PendingContentAssociation)
1341 q = q.filter_by(package=package['Package'])
1342 q = q.filter_by(version=package['Version'])
1343 q = q.filter_by(architecture=arch_id)
1348 for fullpath in fullpaths:
1349 (path, file) = os.path.split(fullpath)
1351 if path.startswith( "./" ):
1354 filepath_id = get_or_set_contents_path_id(path, session)
1355 filename_id = get_or_set_contents_file_id(file, session)
1357 pathcache[fullpath] = (filepath_id, filename_id)
1359 for fullpath, dat in pathcache.items():
1360 pca = PendingContentAssociation()
1361 pca.package = package['Package']
1362 pca.version = package['Version']
1363 pca.filepath_id = dat[0]
1364 pca.filename_id = dat[1]
1365 pca.architecture = arch_id
1368 # Only commit if we set up the session ourself
1376 except Exception, e:
1377 traceback.print_exc()
1379 # Only rollback if we set up the session ourself
1386 __all__.append('insert_pending_content_paths')
1388 ################################################################################
1390 class Priority(object):
1391 def __init__(self, *args, **kwargs):
1394 def __eq__(self, val):
1395 if isinstance(val, str):
1396 return (self.priority == val)
1397 # This signals to use the normal comparison operator
1398 return NotImplemented
1400 def __ne__(self, val):
1401 if isinstance(val, str):
1402 return (self.priority != val)
1403 # This signals to use the normal comparison operator
1404 return NotImplemented
1407 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1409 __all__.append('Priority')
1411 def get_priority(priority, session=None):
1413 Returns Priority object for given C{priority name}.
1415 @type priority: string
1416 @param priority: The name of the priority
1418 @type session: Session
1419 @param session: Optional SQLA session object (a temporary one will be
1420 generated if not supplied)
1423 @return: Priority object for the given priority
1426 privatetrans = False
1428 session = DBConn().session()
1431 q = session.query(Priority).filter_by(priority=priority)
1443 __all__.append('get_priority')
1445 def get_priorities(session=None):
1447 Returns dictionary of priority names -> id mappings
1449 @type session: Session
1450 @param session: Optional SQL session object (a temporary one will be
1451 generated if not supplied)
1454 @return: dictionary of priority names -> id mappings
1456 privatetrans = False
1458 session = DBConn().session()
1462 q = session.query(Priority)
1464 ret[x.priority] = x.priority_id
1471 __all__.append('get_priorities')
1473 ################################################################################
1475 class Queue(object):
1476 def __init__(self, *args, **kwargs):
1480 return '<Queue %s>' % self.queue_name
1482 def autobuild_upload(self, changes, srcpath, session=None):
1484 Update queue_build database table used for incoming autobuild support.
1486 @type changes: Changes
1487 @param changes: changes object for the upload to process
1489 @type srcpath: string
1490 @param srcpath: path for the queue file entries/link destinations
1492 @type session: SQLAlchemy session
1493 @param session: Optional SQLAlchemy session. If this is passed, the
1494 caller is responsible for ensuring a transaction has begun and
1495 committing the results or rolling back based on the result code. If
1496 not passed, a commit will be performed at the end of the function,
1497 otherwise the caller is responsible for commiting.
1499 @rtype: NoneType or string
1500 @return: None if the operation failed, a string describing the error if not
1503 privatetrans = False
1505 session = DBConn().session()
1508 # TODO: Remove by moving queue config into the database
1511 for suitename in changes.changes["distribution"].keys():
1512 # TODO: Move into database as:
1513 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1514 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1515 # This also gets rid of the SecurityQueueBuild hack below
1516 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1520 s = get_suite(suitename, session)
1522 return "INTERNAL ERROR: Could not find suite %s" % suitename
1524 # TODO: Get from database as above
1525 dest_dir = conf["Dir::QueueBuild"]
1527 # TODO: Move into database as above
1528 if conf.FindB("Dinstall::SecurityQueueBuild"):
1529 dest_dir = os.path.join(dest_dir, suitename)
1531 for file_entry in changes.files.keys():
1532 src = os.path.join(srcpath, file_entry)
1533 dest = os.path.join(dest_dir, file_entry)
1535 # TODO: Move into database as above
1536 if conf.FindB("Dinstall::SecurityQueueBuild"):
1537 # Copy it since the original won't be readable by www-data
1538 utils.copy(src, dest)
1540 # Create a symlink to it
1541 os.symlink(src, dest)
1544 qb.suite_id = s.suite_id
1545 qb.queue_id = self.queue_id
1551 # If the .orig.tar.gz is in the pool, create a symlink to
1552 # it (if one doesn't already exist)
1553 if changes.orig_tar_id:
1554 # Determine the .orig.tar.gz file name
1555 for dsc_file in changes.dsc_files.keys():
1556 if dsc_file.endswith(".orig.tar.gz"):
1559 dest = os.path.join(dest_dir, filename)
1561 # If it doesn't exist, create a symlink
1562 if not os.path.exists(dest):
1563 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1564 {'id': changes.orig_tar_id})
1567 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1569 src = os.path.join(res[0], res[1])
1570 os.symlink(src, dest)
1572 # Add it to the list of packages for later processing by apt-ftparchive
1574 qb.suite_id = s.suite_id
1575 qb.queue_id = self.queue_id
1580 # If it does, update things to ensure it's not removed prematurely
1582 qb = get_queue_build(dest, s.suite_id, session)
1594 __all__.append('Queue')
1596 def get_queue(queuename, session=None):
1598 Returns Queue object for given C{queue name}.
1600 @type queuename: string
1601 @param queuename: The name of the queue
1603 @type session: Session
1604 @param session: Optional SQLA session object (a temporary one will be
1605 generated if not supplied)
1608 @return: Queue object for the given queue
1611 privatetrans = False
1613 session = DBConn().session()
1616 q = session.query(Queue).filter_by(queue_name=queuename)
1627 __all__.append('get_queue')
1629 ################################################################################
1631 class QueueBuild(object):
1632 def __init__(self, *args, **kwargs):
1636 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1638 __all__.append('QueueBuild')
1640 def get_queue_build(filename, suite, session=None):
1642 Returns QueueBuild object for given C{filename} and C{suite}.
1644 @type filename: string
1645 @param filename: The name of the file
1647 @type suiteid: int or str
1648 @param suiteid: Suite name or ID
1650 @type session: Session
1651 @param session: Optional SQLA session object (a temporary one will be
1652 generated if not supplied)
1655 @return: Queue object for the given queue
1658 privatetrans = False
1660 session = DBConn().session()
1663 if isinstance(suite, int):
1664 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1666 q = session.query(QueueBuild).filter_by(filename=filename)
1667 q = q.join(Suite).filter_by(suite_name=suite)
1679 __all__.append('get_queue_build')
1681 ################################################################################
1683 class Section(object):
1684 def __init__(self, *args, **kwargs):
1687 def __eq__(self, val):
1688 if isinstance(val, str):
1689 return (self.section == val)
1690 # This signals to use the normal comparison operator
1691 return NotImplemented
1693 def __ne__(self, val):
1694 if isinstance(val, str):
1695 return (self.section != val)
1696 # This signals to use the normal comparison operator
1697 return NotImplemented
1700 return '<Section %s>' % self.section
1702 __all__.append('Section')
1704 def get_section(section, session=None):
1706 Returns Section object for given C{section name}.
1708 @type section: string
1709 @param section: The name of the section
1711 @type session: Session
1712 @param session: Optional SQLA session object (a temporary one will be
1713 generated if not supplied)
1716 @return: Section object for the given section name
1719 privatetrans = False
1721 session = DBConn().session()
1724 q = session.query(Section).filter_by(section=section)
1735 __all__.append('get_section')
1737 def get_sections(session=None):
1739 Returns dictionary of section names -> id mappings
1741 @type session: Session
1742 @param session: Optional SQL session object (a temporary one will be
1743 generated if not supplied)
1746 @return: dictionary of section names -> id mappings
1748 privatetrans = False
1750 session = DBConn().session()
1754 q = session.query(Section)
1756 ret[x.section] = x.section_id
1763 __all__.append('get_sections')
1765 ################################################################################
1767 class DBSource(object):
1768 def __init__(self, *args, **kwargs):
1772 return '<DBSource %s (%s)>' % (self.source, self.version)
1774 __all__.append('DBSource')
1776 def source_exists(source, source_version, suites = ["any"], session=None):
1778 Ensure that source exists somewhere in the archive for the binary
1779 upload being processed.
1780 1. exact match => 1.0-3
1781 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1783 @type package: string
1784 @param package: package source name
1786 @type source_version: string
1787 @param source_version: expected source version
1790 @param suites: list of suites to check in, default I{any}
1792 @type session: Session
1793 @param session: Optional SQLA session object (a temporary one will be
1794 generated if not supplied)
1797 @return: returns 1 if a source with expected version is found, otherwise 0
1801 privatetrans = False
1803 session = DBConn().session()
1809 for suite in suites:
1810 q = session.query(DBSource).filter_by(source=source)
1812 # source must exist in suite X, or in some other suite that's
1813 # mapped to X, recursively... silent-maps are counted too,
1814 # unreleased-maps aren't.
1815 maps = cnf.ValueList("SuiteMappings")[:]
1817 maps = [ m.split() for m in maps ]
1818 maps = [ (x[1], x[2]) for x in maps
1819 if x[0] == "map" or x[0] == "silent-map" ]
1822 if x[1] in s and x[0] not in s:
1825 q = q.join(SrcAssociation).join(Suite)
1826 q = q.filter(Suite.suite_name.in_(s))
1828 # Reduce the query results to a list of version numbers
1829 ql = [ j.version for j in q.all() ]
1832 if source_version in ql:
1836 from daklib.regexes import re_bin_only_nmu
1837 orig_source_version = re_bin_only_nmu.sub('', source_version)
1838 if orig_source_version in ql:
1841 # No source found so return not ok
1849 __all__.append('source_exists')
1851 def get_suites_source_in(source, session=None):
1853 Returns list of Suite objects which given C{source} name is in
1856 @param source: DBSource package name to search for
1859 @return: list of Suite objects for the given source
1862 privatetrans = False
1864 session = DBConn().session()
1867 ret = session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1874 __all__.append('get_suites_source_in')
1876 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1878 Returns list of DBSource objects for given C{source} name and other parameters
1881 @param source: DBSource package name to search for
1883 @type source: str or None
1884 @param source: DBSource version name to search for or None if not applicable
1886 @type dm_upload_allowed: bool
1887 @param dm_upload_allowed: If None, no effect. If True or False, only
1888 return packages with that dm_upload_allowed setting
1890 @type session: Session
1891 @param session: Optional SQL session object (a temporary one will be
1892 generated if not supplied)
1895 @return: list of DBSource objects for the given name (may be empty)
1897 privatetrans = False
1899 session = DBConn().session()
1902 q = session.query(DBSource).filter_by(source=source)
1904 if version is not None:
1905 q = q.filter_by(version=version)
1907 if dm_upload_allowed is not None:
1908 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1917 __all__.append('get_sources_from_name')
1919 def get_source_in_suite(source, suite, session=None):
1921 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1923 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1924 - B{suite} - a suite name, eg. I{unstable}
1926 @type source: string
1927 @param source: source package name
1930 @param suite: the suite name
1933 @return: the version for I{source} in I{suite}
1936 privatetrans = False
1938 session = DBConn().session()
1941 q = session.query(SrcAssociation)
1942 q = q.join('source').filter_by(source=source)
1943 q = q.join('suite').filter_by(suite_name=suite)
1948 # ???: Maybe we should just return the SrcAssociation object instead
1949 ret = q.one().source
1956 __all__.append('get_source_in_suite')
1958 ################################################################################
1960 class SrcAssociation(object):
1961 def __init__(self, *args, **kwargs):
1965 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1967 __all__.append('SrcAssociation')
1969 ################################################################################
1971 class SrcUploader(object):
1972 def __init__(self, *args, **kwargs):
1976 return '<SrcUploader %s>' % self.uploader_id
1978 __all__.append('SrcUploader')
1980 ################################################################################
1982 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1983 ('SuiteID', 'suite_id'),
1984 ('Version', 'version'),
1985 ('Origin', 'origin'),
1987 ('Description', 'description'),
1988 ('Untouchable', 'untouchable'),
1989 ('Announce', 'announce'),
1990 ('Codename', 'codename'),
1991 ('OverrideCodename', 'overridecodename'),
1992 ('ValidTime', 'validtime'),
1993 ('Priority', 'priority'),
1994 ('NotAutomatic', 'notautomatic'),
1995 ('CopyChanges', 'copychanges'),
1996 ('CopyDotDak', 'copydotdak'),
1997 ('CommentsDir', 'commentsdir'),
1998 ('OverrideSuite', 'overridesuite'),
1999 ('ChangelogBase', 'changelogbase')]
2002 class Suite(object):
2003 def __init__(self, *args, **kwargs):
2007 return '<Suite %s>' % self.suite_name
2009 def __eq__(self, val):
2010 if isinstance(val, str):
2011 return (self.suite_name == val)
2012 # This signals to use the normal comparison operator
2013 return NotImplemented
2015 def __ne__(self, val):
2016 if isinstance(val, str):
2017 return (self.suite_name != val)
2018 # This signals to use the normal comparison operator
2019 return NotImplemented
2023 for disp, field in SUITE_FIELDS:
2024 val = getattr(self, field, None)
2026 ret.append("%s: %s" % (disp, val))
2028 return "\n".join(ret)
2030 __all__.append('Suite')
2032 def get_suite_architecture(suite, architecture, session=None):
2034 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2038 @param suite: Suite name to search for
2040 @type architecture: str
2041 @param architecture: Architecture name to search for
2043 @type session: Session
2044 @param session: Optional SQL session object (a temporary one will be
2045 generated if not supplied)
2047 @rtype: SuiteArchitecture
2048 @return: the SuiteArchitecture object or None
2051 privatetrans = False
2053 session = DBConn().session()
2056 q = session.query(SuiteArchitecture)
2057 q = q.join(Architecture).filter_by(arch_string=architecture)
2058 q = q.join(Suite).filter_by(suite_name=suite)
2070 __all__.append('get_suite_architecture')
2072 def get_suite(suite, session=None):
2074 Returns Suite object for given C{suite name}.
2077 @param suite: The name of the suite
2079 @type session: Session
2080 @param session: Optional SQLA session object (a temporary one will be
2081 generated if not supplied)
2084 @return: Suite object for the requested suite name (None if not presenT)
2087 privatetrans = False
2089 session = DBConn().session()
2092 q = session.query(Suite).filter_by(suite_name=suite)
2104 __all__.append('get_suite')
2106 ################################################################################
2108 class SuiteArchitecture(object):
2109 def __init__(self, *args, **kwargs):
2113 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2115 __all__.append('SuiteArchitecture')
2117 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2119 Returns list of Architecture objects for given C{suite} name
2122 @param source: Suite name to search for
2124 @type skipsrc: boolean
2125 @param skipsrc: Whether to skip returning the 'source' architecture entry
2128 @type skipall: boolean
2129 @param skipall: Whether to skip returning the 'all' architecture entry
2132 @type session: Session
2133 @param session: Optional SQL session object (a temporary one will be
2134 generated if not supplied)
2137 @return: list of Architecture objects for the given name (may be empty)
2140 privatetrans = False
2142 session = DBConn().session()
2145 q = session.query(Architecture)
2146 q = q.join(SuiteArchitecture)
2147 q = q.join(Suite).filter_by(suite_name=suite)
2150 q = q.filter(Architecture.arch_string != 'source')
2153 q = q.filter(Architecture.arch_string != 'all')
2155 q = q.order_by('arch_string')
2164 __all__.append('get_suite_architectures')
2166 ################################################################################
2169 def __init__(self, *args, **kwargs):
2172 def __eq__(self, val):
2173 if isinstance(val, str):
2174 return (self.uid == val)
2175 # This signals to use the normal comparison operator
2176 return NotImplemented
2178 def __ne__(self, val):
2179 if isinstance(val, str):
2180 return (self.uid != val)
2181 # This signals to use the normal comparison operator
2182 return NotImplemented
2185 return '<Uid %s (%s)>' % (self.uid, self.name)
2187 __all__.append('Uid')
2189 def add_database_user(uidname, session=None):
2191 Adds a database user
2193 @type uidname: string
2194 @param uidname: The uid of the user to add
2196 @type session: SQLAlchemy
2197 @param session: Optional SQL session object (a temporary one will be
2198 generated if not supplied). If not passed, a commit will be performed at
2199 the end of the function, otherwise the caller is responsible for commiting.
2202 @return: the uid object for the given uidname
2205 privatetrans = False
2207 session = DBConn().session()
2210 session.execute("CREATE USER :uid", {'uid': uidname})
2216 __all__.append('add_database_user')
2218 def get_or_set_uid(uidname, session=None):
2220 Returns uid object for given uidname.
2222 If no matching uidname is found, a row is inserted.
2224 @type uidname: string
2225 @param uidname: The uid to add
2227 @type session: SQLAlchemy
2228 @param session: Optional SQL session object (a temporary one will be
2229 generated if not supplied). If not passed, a commit will be performed at
2230 the end of the function, otherwise the caller is responsible for commiting.
2233 @return: the uid object for the given uidname
2236 privatetrans = False
2238 session = DBConn().session()
2241 q = session.query(Uid).filter_by(uid=uidname)
2260 __all__.append('get_or_set_uid')
2263 def get_uid_from_fingerprint(fpr, session=None):
2264 privatetrans = False
2266 session = DBConn().session()
2269 q = session.query(Uid)
2270 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2282 __all__.append('get_uid_from_fingerprint')
2284 ################################################################################
2286 class DBConn(Singleton):
2288 database module init.
2290 def __init__(self, *args, **kwargs):
2291 super(DBConn, self).__init__(*args, **kwargs)
2293 def _startup(self, *args, **kwargs):
2295 if kwargs.has_key('debug'):
2299 def __setuptables(self):
2300 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2301 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2302 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2303 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2304 self.tbl_component = Table('component', self.db_meta, autoload=True)
2305 self.tbl_config = Table('config', self.db_meta, autoload=True)
2306 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2307 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2308 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2309 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2310 self.tbl_files = Table('files', self.db_meta, autoload=True)
2311 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2312 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2313 self.tbl_location = Table('location', self.db_meta, autoload=True)
2314 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2315 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2316 self.tbl_override = Table('override', self.db_meta, autoload=True)
2317 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2318 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2319 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2320 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2321 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2322 self.tbl_section = Table('section', self.db_meta, autoload=True)
2323 self.tbl_source = Table('source', self.db_meta, autoload=True)
2324 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2325 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2326 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2327 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2328 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2330 def __setupmappers(self):
2331 mapper(Architecture, self.tbl_architecture,
2332 properties = dict(arch_id = self.tbl_architecture.c.id))
2334 mapper(Archive, self.tbl_archive,
2335 properties = dict(archive_id = self.tbl_archive.c.id,
2336 archive_name = self.tbl_archive.c.name))
2338 mapper(BinAssociation, self.tbl_bin_associations,
2339 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2340 suite_id = self.tbl_bin_associations.c.suite,
2341 suite = relation(Suite),
2342 binary_id = self.tbl_bin_associations.c.bin,
2343 binary = relation(DBBinary)))
2345 mapper(DBBinary, self.tbl_binaries,
2346 properties = dict(binary_id = self.tbl_binaries.c.id,
2347 package = self.tbl_binaries.c.package,
2348 version = self.tbl_binaries.c.version,
2349 maintainer_id = self.tbl_binaries.c.maintainer,
2350 maintainer = relation(Maintainer),
2351 source_id = self.tbl_binaries.c.source,
2352 source = relation(DBSource),
2353 arch_id = self.tbl_binaries.c.architecture,
2354 architecture = relation(Architecture),
2355 poolfile_id = self.tbl_binaries.c.file,
2356 poolfile = relation(PoolFile),
2357 binarytype = self.tbl_binaries.c.type,
2358 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2359 fingerprint = relation(Fingerprint),
2360 install_date = self.tbl_binaries.c.install_date,
2361 binassociations = relation(BinAssociation,
2362 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2364 mapper(Component, self.tbl_component,
2365 properties = dict(component_id = self.tbl_component.c.id,
2366 component_name = self.tbl_component.c.name))
2368 mapper(DBConfig, self.tbl_config,
2369 properties = dict(config_id = self.tbl_config.c.id))
2371 mapper(ContentAssociation, self.tbl_content_associations,
2372 properties = dict(ca_id = self.tbl_content_associations.c.id,
2373 filename_id = self.tbl_content_associations.c.filename,
2374 filename = relation(ContentFilename),
2375 filepath_id = self.tbl_content_associations.c.filepath,
2376 filepath = relation(ContentFilepath),
2377 binary_id = self.tbl_content_associations.c.binary_pkg,
2378 binary = relation(DBBinary)))
2381 mapper(ContentFilename, self.tbl_content_file_names,
2382 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2383 filename = self.tbl_content_file_names.c.file))
2385 mapper(ContentFilepath, self.tbl_content_file_paths,
2386 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2387 filepath = self.tbl_content_file_paths.c.path))
2389 mapper(DSCFile, self.tbl_dsc_files,
2390 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2391 source_id = self.tbl_dsc_files.c.source,
2392 source = relation(DBSource),
2393 poolfile_id = self.tbl_dsc_files.c.file,
2394 poolfile = relation(PoolFile)))
2396 mapper(PoolFile, self.tbl_files,
2397 properties = dict(file_id = self.tbl_files.c.id,
2398 filesize = self.tbl_files.c.size,
2399 location_id = self.tbl_files.c.location,
2400 location = relation(Location)))
2402 mapper(Fingerprint, self.tbl_fingerprint,
2403 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2404 uid_id = self.tbl_fingerprint.c.uid,
2405 uid = relation(Uid),
2406 keyring_id = self.tbl_fingerprint.c.keyring,
2407 keyring = relation(Keyring)))
2409 mapper(Keyring, self.tbl_keyrings,
2410 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2411 keyring_id = self.tbl_keyrings.c.id))
2413 mapper(Location, self.tbl_location,
2414 properties = dict(location_id = self.tbl_location.c.id,
2415 component_id = self.tbl_location.c.component,
2416 component = relation(Component),
2417 archive_id = self.tbl_location.c.archive,
2418 archive = relation(Archive),
2419 archive_type = self.tbl_location.c.type))
2421 mapper(Maintainer, self.tbl_maintainer,
2422 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2424 mapper(NewComment, self.tbl_new_comments,
2425 properties = dict(comment_id = self.tbl_new_comments.c.id))
2427 mapper(Override, self.tbl_override,
2428 properties = dict(suite_id = self.tbl_override.c.suite,
2429 suite = relation(Suite),
2430 component_id = self.tbl_override.c.component,
2431 component = relation(Component),
2432 priority_id = self.tbl_override.c.priority,
2433 priority = relation(Priority),
2434 section_id = self.tbl_override.c.section,
2435 section = relation(Section),
2436 overridetype_id = self.tbl_override.c.type,
2437 overridetype = relation(OverrideType)))
2439 mapper(OverrideType, self.tbl_override_type,
2440 properties = dict(overridetype = self.tbl_override_type.c.type,
2441 overridetype_id = self.tbl_override_type.c.id))
2443 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2444 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2445 filepath_id = self.tbl_pending_content_associations.c.filepath,
2446 filepath = relation(ContentFilepath),
2447 filename_id = self.tbl_pending_content_associations.c.filename,
2448 filename = relation(ContentFilename)))
2450 mapper(Priority, self.tbl_priority,
2451 properties = dict(priority_id = self.tbl_priority.c.id))
2453 mapper(Queue, self.tbl_queue,
2454 properties = dict(queue_id = self.tbl_queue.c.id))
2456 mapper(QueueBuild, self.tbl_queue_build,
2457 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2458 queue_id = self.tbl_queue_build.c.queue,
2459 queue = relation(Queue, backref='queuebuild')))
2461 mapper(Section, self.tbl_section,
2462 properties = dict(section_id = self.tbl_section.c.id))
2464 mapper(DBSource, self.tbl_source,
2465 properties = dict(source_id = self.tbl_source.c.id,
2466 version = self.tbl_source.c.version,
2467 maintainer_id = self.tbl_source.c.maintainer,
2468 maintainer = relation(Maintainer,
2469 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2470 poolfile_id = self.tbl_source.c.file,
2471 poolfile = relation(PoolFile),
2472 fingerprint_id = self.tbl_source.c.sig_fpr,
2473 fingerprint = relation(Fingerprint),
2474 changedby_id = self.tbl_source.c.changedby,
2475 changedby = relation(Maintainer,
2476 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2477 srcfiles = relation(DSCFile,
2478 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2479 srcassociations = relation(SrcAssociation,
2480 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2482 mapper(SrcAssociation, self.tbl_src_associations,
2483 properties = dict(sa_id = self.tbl_src_associations.c.id,
2484 suite_id = self.tbl_src_associations.c.suite,
2485 suite = relation(Suite),
2486 source_id = self.tbl_src_associations.c.source,
2487 source = relation(DBSource)))
2489 mapper(SrcUploader, self.tbl_src_uploaders,
2490 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2491 source_id = self.tbl_src_uploaders.c.source,
2492 source = relation(DBSource,
2493 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2494 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2495 maintainer = relation(Maintainer,
2496 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2498 mapper(Suite, self.tbl_suite,
2499 properties = dict(suite_id = self.tbl_suite.c.id))
2501 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2502 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2503 suite = relation(Suite, backref='suitearchitectures'),
2504 arch_id = self.tbl_suite_architectures.c.architecture,
2505 architecture = relation(Architecture)))
2507 mapper(Uid, self.tbl_uid,
2508 properties = dict(uid_id = self.tbl_uid.c.id,
2509 fingerprint = relation(Fingerprint)))
2511 ## Connection functions
2512 def __createconn(self):
2513 from config import Config
2517 connstr = "postgres://%s" % cnf["DB::Host"]
2518 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2519 connstr += ":%s" % cnf["DB::Port"]
2520 connstr += "/%s" % cnf["DB::Name"]
2523 connstr = "postgres:///%s" % cnf["DB::Name"]
2524 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2525 connstr += "?port=%s" % cnf["DB::Port"]
2527 self.db_pg = create_engine(connstr, echo=self.debug)
2528 self.db_meta = MetaData()
2529 self.db_meta.bind = self.db_pg
2530 self.db_smaker = sessionmaker(bind=self.db_pg,
2534 self.__setuptables()
2535 self.__setupmappers()
2538 return self.db_smaker()
2540 __all__.append('DBConn')