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 def get_maintainer(maintainer_id, session=None):
1100 Return the name of the maintainer behind C{maintainer_id} or None if that
1101 maintainer_id is invalid.
1103 @type maintainer_id: int
1104 @param maintainer_id: the id of the maintainer
1107 @return: the Maintainer with this C{maintainer_id}
1110 privatetrans = False
1112 session = DBConn().session()
1116 return session.query(Maintainer).get(maintainer_id)
1121 __all__.append('get_maintainer')
1123 ################################################################################
1125 class NewComment(object):
1126 def __init__(self, *args, **kwargs):
1130 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1132 __all__.append('NewComment')
1134 def has_new_comment(package, version, session=None):
1136 Returns true if the given combination of C{package}, C{version} has a comment.
1138 @type package: string
1139 @param package: name of the package
1141 @type version: string
1142 @param version: package version
1144 @type session: Session
1145 @param session: Optional SQLA session object (a temporary one will be
1146 generated if not supplied)
1152 privatetrans = False
1154 session = DBConn().session()
1157 q = session.query(NewComment)
1158 q = q.filter_by(package=package)
1159 q = q.filter_by(version=version)
1168 __all__.append('has_new_comment')
1170 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1172 Returns (possibly empty) list of NewComment objects for the given
1175 @type package: string (optional)
1176 @param package: name of the package
1178 @type version: string (optional)
1179 @param version: package version
1181 @type comment_id: int (optional)
1182 @param comment_id: An id of a comment
1184 @type session: Session
1185 @param session: Optional SQLA session object (a temporary one will be
1186 generated if not supplied)
1189 @return: A (possibly empty) list of NewComment objects will be returned
1193 privatetrans = False
1195 session = DBConn().session()
1198 q = session.query(NewComment)
1199 if package is not None: q = q.filter_by(package=package)
1200 if version is not None: q = q.filter_by(version=version)
1201 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1210 __all__.append('get_new_comments')
1212 ################################################################################
1214 class Override(object):
1215 def __init__(self, *args, **kwargs):
1219 return '<Override %s (%s)>' % (self.package, self.suite_id)
1221 __all__.append('Override')
1223 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1225 Returns Override object for the given parameters
1227 @type package: string
1228 @param package: The name of the package
1230 @type suite: string, list or None
1231 @param suite: The name of the suite (or suites if a list) to limit to. If
1232 None, don't limit. Defaults to None.
1234 @type component: string, list or None
1235 @param component: The name of the component (or components if a list) to
1236 limit to. If None, don't limit. Defaults to None.
1238 @type overridetype: string, list or None
1239 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1240 limit to. If None, don't limit. Defaults to None.
1242 @type session: Session
1243 @param session: Optional SQLA session object (a temporary one will be
1244 generated if not supplied)
1247 @return: A (possibly empty) list of Override objects will be returned
1250 privatetrans = False
1252 session = DBConn().session()
1255 q = session.query(Override)
1256 q = q.filter_by(package=package)
1258 if suite is not None:
1259 if not isinstance(suite, list): suite = [suite]
1260 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1262 if component is not None:
1263 if not isinstance(component, list): component = [component]
1264 q = q.join(Component).filter(Component.component_name.in_(component))
1266 if overridetype is not None:
1267 if not isinstance(overridetype, list): overridetype = [overridetype]
1268 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1277 __all__.append('get_override')
1280 ################################################################################
1282 class OverrideType(object):
1283 def __init__(self, *args, **kwargs):
1287 return '<OverrideType %s>' % self.overridetype
1289 __all__.append('OverrideType')
1291 def get_override_type(override_type, session=None):
1293 Returns OverrideType object for given C{override type}.
1295 @type override_type: string
1296 @param override_type: The name of the override type
1298 @type session: Session
1299 @param session: Optional SQLA session object (a temporary one will be
1300 generated if not supplied)
1303 @return: the database id for the given override type
1306 privatetrans = False
1308 session = DBConn().session()
1311 q = session.query(OverrideType).filter_by(overridetype=override_type)
1323 __all__.append('get_override_type')
1325 ################################################################################
1327 class PendingContentAssociation(object):
1328 def __init__(self, *args, **kwargs):
1332 return '<PendingContentAssociation %s>' % self.pca_id
1334 __all__.append('PendingContentAssociation')
1336 def insert_pending_content_paths(package, fullpaths, session=None):
1338 Make sure given paths are temporarily associated with given
1342 @param package: the package to associate with should have been read in from the binary control file
1343 @type fullpaths: list
1344 @param fullpaths: the list of paths of the file being associated with the binary
1345 @type session: SQLAlchemy session
1346 @param session: Optional SQLAlchemy session. If this is passed, the caller
1347 is responsible for ensuring a transaction has begun and committing the
1348 results or rolling back based on the result code. If not passed, a commit
1349 will be performed at the end of the function
1351 @return: True upon success, False if there is a problem
1354 privatetrans = False
1357 session = DBConn().session()
1361 arch = get_architecture(package['Architecture'], session)
1362 arch_id = arch.arch_id
1364 # Remove any already existing recorded files for this package
1365 q = session.query(PendingContentAssociation)
1366 q = q.filter_by(package=package['Package'])
1367 q = q.filter_by(version=package['Version'])
1368 q = q.filter_by(architecture=arch_id)
1373 for fullpath in fullpaths:
1374 (path, file) = os.path.split(fullpath)
1376 if path.startswith( "./" ):
1379 filepath_id = get_or_set_contents_path_id(path, session)
1380 filename_id = get_or_set_contents_file_id(file, session)
1382 pathcache[fullpath] = (filepath_id, filename_id)
1384 for fullpath, dat in pathcache.items():
1385 pca = PendingContentAssociation()
1386 pca.package = package['Package']
1387 pca.version = package['Version']
1388 pca.filepath_id = dat[0]
1389 pca.filename_id = dat[1]
1390 pca.architecture = arch_id
1393 # Only commit if we set up the session ourself
1401 except Exception, e:
1402 traceback.print_exc()
1404 # Only rollback if we set up the session ourself
1411 __all__.append('insert_pending_content_paths')
1413 ################################################################################
1415 class Priority(object):
1416 def __init__(self, *args, **kwargs):
1419 def __eq__(self, val):
1420 if isinstance(val, str):
1421 return (self.priority == val)
1422 # This signals to use the normal comparison operator
1423 return NotImplemented
1425 def __ne__(self, val):
1426 if isinstance(val, str):
1427 return (self.priority != val)
1428 # This signals to use the normal comparison operator
1429 return NotImplemented
1432 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1434 __all__.append('Priority')
1436 def get_priority(priority, session=None):
1438 Returns Priority object for given C{priority name}.
1440 @type priority: string
1441 @param priority: The name of the priority
1443 @type session: Session
1444 @param session: Optional SQLA session object (a temporary one will be
1445 generated if not supplied)
1448 @return: Priority object for the given priority
1451 privatetrans = False
1453 session = DBConn().session()
1456 q = session.query(Priority).filter_by(priority=priority)
1468 __all__.append('get_priority')
1470 def get_priorities(session=None):
1472 Returns dictionary of priority names -> id mappings
1474 @type session: Session
1475 @param session: Optional SQL session object (a temporary one will be
1476 generated if not supplied)
1479 @return: dictionary of priority names -> id mappings
1481 privatetrans = False
1483 session = DBConn().session()
1487 q = session.query(Priority)
1489 ret[x.priority] = x.priority_id
1496 __all__.append('get_priorities')
1498 ################################################################################
1500 class Queue(object):
1501 def __init__(self, *args, **kwargs):
1505 return '<Queue %s>' % self.queue_name
1507 def autobuild_upload(self, changes, srcpath, session=None):
1509 Update queue_build database table used for incoming autobuild support.
1511 @type changes: Changes
1512 @param changes: changes object for the upload to process
1514 @type srcpath: string
1515 @param srcpath: path for the queue file entries/link destinations
1517 @type session: SQLAlchemy session
1518 @param session: Optional SQLAlchemy session. If this is passed, the
1519 caller is responsible for ensuring a transaction has begun and
1520 committing the results or rolling back based on the result code. If
1521 not passed, a commit will be performed at the end of the function,
1522 otherwise the caller is responsible for commiting.
1524 @rtype: NoneType or string
1525 @return: None if the operation failed, a string describing the error if not
1528 privatetrans = False
1530 session = DBConn().session()
1533 # TODO: Remove by moving queue config into the database
1536 for suitename in changes.changes["distribution"].keys():
1537 # TODO: Move into database as:
1538 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1539 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1540 # This also gets rid of the SecurityQueueBuild hack below
1541 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1545 s = get_suite(suitename, session)
1547 return "INTERNAL ERROR: Could not find suite %s" % suitename
1549 # TODO: Get from database as above
1550 dest_dir = conf["Dir::QueueBuild"]
1552 # TODO: Move into database as above
1553 if conf.FindB("Dinstall::SecurityQueueBuild"):
1554 dest_dir = os.path.join(dest_dir, suitename)
1556 for file_entry in changes.files.keys():
1557 src = os.path.join(srcpath, file_entry)
1558 dest = os.path.join(dest_dir, file_entry)
1560 # TODO: Move into database as above
1561 if conf.FindB("Dinstall::SecurityQueueBuild"):
1562 # Copy it since the original won't be readable by www-data
1563 utils.copy(src, dest)
1565 # Create a symlink to it
1566 os.symlink(src, dest)
1569 qb.suite_id = s.suite_id
1570 qb.queue_id = self.queue_id
1576 # If the .orig.tar.gz is in the pool, create a symlink to
1577 # it (if one doesn't already exist)
1578 if changes.orig_tar_id:
1579 # Determine the .orig.tar.gz file name
1580 for dsc_file in changes.dsc_files.keys():
1581 if dsc_file.endswith(".orig.tar.gz"):
1584 dest = os.path.join(dest_dir, filename)
1586 # If it doesn't exist, create a symlink
1587 if not os.path.exists(dest):
1588 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1589 {'id': changes.orig_tar_id})
1592 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1594 src = os.path.join(res[0], res[1])
1595 os.symlink(src, dest)
1597 # Add it to the list of packages for later processing by apt-ftparchive
1599 qb.suite_id = s.suite_id
1600 qb.queue_id = self.queue_id
1605 # If it does, update things to ensure it's not removed prematurely
1607 qb = get_queue_build(dest, s.suite_id, session)
1619 __all__.append('Queue')
1621 def get_queue(queuename, session=None):
1623 Returns Queue object for given C{queue name}.
1625 @type queuename: string
1626 @param queuename: The name of the queue
1628 @type session: Session
1629 @param session: Optional SQLA session object (a temporary one will be
1630 generated if not supplied)
1633 @return: Queue object for the given queue
1636 privatetrans = False
1638 session = DBConn().session()
1641 q = session.query(Queue).filter_by(queue_name=queuename)
1652 __all__.append('get_queue')
1654 ################################################################################
1656 class QueueBuild(object):
1657 def __init__(self, *args, **kwargs):
1661 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1663 __all__.append('QueueBuild')
1665 def get_queue_build(filename, suite, session=None):
1667 Returns QueueBuild object for given C{filename} and C{suite}.
1669 @type filename: string
1670 @param filename: The name of the file
1672 @type suiteid: int or str
1673 @param suiteid: Suite name or ID
1675 @type session: Session
1676 @param session: Optional SQLA session object (a temporary one will be
1677 generated if not supplied)
1680 @return: Queue object for the given queue
1683 privatetrans = False
1685 session = DBConn().session()
1688 if isinstance(suite, int):
1689 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1691 q = session.query(QueueBuild).filter_by(filename=filename)
1692 q = q.join(Suite).filter_by(suite_name=suite)
1704 __all__.append('get_queue_build')
1706 ################################################################################
1708 class Section(object):
1709 def __init__(self, *args, **kwargs):
1712 def __eq__(self, val):
1713 if isinstance(val, str):
1714 return (self.section == val)
1715 # This signals to use the normal comparison operator
1716 return NotImplemented
1718 def __ne__(self, val):
1719 if isinstance(val, str):
1720 return (self.section != val)
1721 # This signals to use the normal comparison operator
1722 return NotImplemented
1725 return '<Section %s>' % self.section
1727 __all__.append('Section')
1729 def get_section(section, session=None):
1731 Returns Section object for given C{section name}.
1733 @type section: string
1734 @param section: The name of the section
1736 @type session: Session
1737 @param session: Optional SQLA session object (a temporary one will be
1738 generated if not supplied)
1741 @return: Section object for the given section name
1744 privatetrans = False
1746 session = DBConn().session()
1749 q = session.query(Section).filter_by(section=section)
1760 __all__.append('get_section')
1762 def get_sections(session=None):
1764 Returns dictionary of section names -> id mappings
1766 @type session: Session
1767 @param session: Optional SQL session object (a temporary one will be
1768 generated if not supplied)
1771 @return: dictionary of section names -> id mappings
1773 privatetrans = False
1775 session = DBConn().session()
1779 q = session.query(Section)
1781 ret[x.section] = x.section_id
1788 __all__.append('get_sections')
1790 ################################################################################
1792 class DBSource(object):
1793 def __init__(self, *args, **kwargs):
1797 return '<DBSource %s (%s)>' % (self.source, self.version)
1799 __all__.append('DBSource')
1801 def source_exists(source, source_version, suites = ["any"], session=None):
1803 Ensure that source exists somewhere in the archive for the binary
1804 upload being processed.
1805 1. exact match => 1.0-3
1806 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1808 @type package: string
1809 @param package: package source name
1811 @type source_version: string
1812 @param source_version: expected source version
1815 @param suites: list of suites to check in, default I{any}
1817 @type session: Session
1818 @param session: Optional SQLA session object (a temporary one will be
1819 generated if not supplied)
1822 @return: returns 1 if a source with expected version is found, otherwise 0
1826 privatetrans = False
1828 session = DBConn().session()
1834 for suite in suites:
1835 q = session.query(DBSource).filter_by(source=source)
1837 # source must exist in suite X, or in some other suite that's
1838 # mapped to X, recursively... silent-maps are counted too,
1839 # unreleased-maps aren't.
1840 maps = cnf.ValueList("SuiteMappings")[:]
1842 maps = [ m.split() for m in maps ]
1843 maps = [ (x[1], x[2]) for x in maps
1844 if x[0] == "map" or x[0] == "silent-map" ]
1847 if x[1] in s and x[0] not in s:
1850 q = q.join(SrcAssociation).join(Suite)
1851 q = q.filter(Suite.suite_name.in_(s))
1853 # Reduce the query results to a list of version numbers
1854 ql = [ j.version for j in q.all() ]
1857 if source_version in ql:
1861 from daklib.regexes import re_bin_only_nmu
1862 orig_source_version = re_bin_only_nmu.sub('', source_version)
1863 if orig_source_version in ql:
1866 # No source found so return not ok
1874 __all__.append('source_exists')
1876 def get_suites_source_in(source, session=None):
1878 Returns list of Suite objects which given C{source} name is in
1881 @param source: DBSource package name to search for
1884 @return: list of Suite objects for the given source
1887 privatetrans = False
1889 session = DBConn().session()
1892 ret = session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1899 __all__.append('get_suites_source_in')
1901 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1903 Returns list of DBSource objects for given C{source} name and other parameters
1906 @param source: DBSource package name to search for
1908 @type source: str or None
1909 @param source: DBSource version name to search for or None if not applicable
1911 @type dm_upload_allowed: bool
1912 @param dm_upload_allowed: If None, no effect. If True or False, only
1913 return packages with that dm_upload_allowed setting
1915 @type session: Session
1916 @param session: Optional SQL session object (a temporary one will be
1917 generated if not supplied)
1920 @return: list of DBSource objects for the given name (may be empty)
1922 privatetrans = False
1924 session = DBConn().session()
1927 q = session.query(DBSource).filter_by(source=source)
1929 if version is not None:
1930 q = q.filter_by(version=version)
1932 if dm_upload_allowed is not None:
1933 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1942 __all__.append('get_sources_from_name')
1944 def get_source_in_suite(source, suite, session=None):
1946 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1948 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1949 - B{suite} - a suite name, eg. I{unstable}
1951 @type source: string
1952 @param source: source package name
1955 @param suite: the suite name
1958 @return: the version for I{source} in I{suite}
1961 privatetrans = False
1963 session = DBConn().session()
1966 q = session.query(SrcAssociation)
1967 q = q.join('source').filter_by(source=source)
1968 q = q.join('suite').filter_by(suite_name=suite)
1973 # ???: Maybe we should just return the SrcAssociation object instead
1974 ret = q.one().source
1981 __all__.append('get_source_in_suite')
1983 ################################################################################
1985 class SrcAssociation(object):
1986 def __init__(self, *args, **kwargs):
1990 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1992 __all__.append('SrcAssociation')
1994 ################################################################################
1996 class SrcUploader(object):
1997 def __init__(self, *args, **kwargs):
2001 return '<SrcUploader %s>' % self.uploader_id
2003 __all__.append('SrcUploader')
2005 ################################################################################
2007 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2008 ('SuiteID', 'suite_id'),
2009 ('Version', 'version'),
2010 ('Origin', 'origin'),
2012 ('Description', 'description'),
2013 ('Untouchable', 'untouchable'),
2014 ('Announce', 'announce'),
2015 ('Codename', 'codename'),
2016 ('OverrideCodename', 'overridecodename'),
2017 ('ValidTime', 'validtime'),
2018 ('Priority', 'priority'),
2019 ('NotAutomatic', 'notautomatic'),
2020 ('CopyChanges', 'copychanges'),
2021 ('CopyDotDak', 'copydotdak'),
2022 ('CommentsDir', 'commentsdir'),
2023 ('OverrideSuite', 'overridesuite'),
2024 ('ChangelogBase', 'changelogbase')]
2027 class Suite(object):
2028 def __init__(self, *args, **kwargs):
2032 return '<Suite %s>' % self.suite_name
2034 def __eq__(self, val):
2035 if isinstance(val, str):
2036 return (self.suite_name == val)
2037 # This signals to use the normal comparison operator
2038 return NotImplemented
2040 def __ne__(self, val):
2041 if isinstance(val, str):
2042 return (self.suite_name != val)
2043 # This signals to use the normal comparison operator
2044 return NotImplemented
2048 for disp, field in SUITE_FIELDS:
2049 val = getattr(self, field, None)
2051 ret.append("%s: %s" % (disp, val))
2053 return "\n".join(ret)
2055 __all__.append('Suite')
2057 def get_suite_architecture(suite, architecture, session=None):
2059 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2063 @param suite: Suite name to search for
2065 @type architecture: str
2066 @param architecture: Architecture name to search for
2068 @type session: Session
2069 @param session: Optional SQL session object (a temporary one will be
2070 generated if not supplied)
2072 @rtype: SuiteArchitecture
2073 @return: the SuiteArchitecture object or None
2076 privatetrans = False
2078 session = DBConn().session()
2081 q = session.query(SuiteArchitecture)
2082 q = q.join(Architecture).filter_by(arch_string=architecture)
2083 q = q.join(Suite).filter_by(suite_name=suite)
2095 __all__.append('get_suite_architecture')
2097 def get_suite(suite, session=None):
2099 Returns Suite object for given C{suite name}.
2102 @param suite: The name of the suite
2104 @type session: Session
2105 @param session: Optional SQLA session object (a temporary one will be
2106 generated if not supplied)
2109 @return: Suite object for the requested suite name (None if not presenT)
2112 privatetrans = False
2114 session = DBConn().session()
2117 q = session.query(Suite).filter_by(suite_name=suite)
2129 __all__.append('get_suite')
2131 ################################################################################
2133 class SuiteArchitecture(object):
2134 def __init__(self, *args, **kwargs):
2138 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2140 __all__.append('SuiteArchitecture')
2142 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2144 Returns list of Architecture objects for given C{suite} name
2147 @param source: Suite name to search for
2149 @type skipsrc: boolean
2150 @param skipsrc: Whether to skip returning the 'source' architecture entry
2153 @type skipall: boolean
2154 @param skipall: Whether to skip returning the 'all' architecture entry
2157 @type session: Session
2158 @param session: Optional SQL session object (a temporary one will be
2159 generated if not supplied)
2162 @return: list of Architecture objects for the given name (may be empty)
2165 privatetrans = False
2167 session = DBConn().session()
2170 q = session.query(Architecture)
2171 q = q.join(SuiteArchitecture)
2172 q = q.join(Suite).filter_by(suite_name=suite)
2175 q = q.filter(Architecture.arch_string != 'source')
2178 q = q.filter(Architecture.arch_string != 'all')
2180 q = q.order_by('arch_string')
2189 __all__.append('get_suite_architectures')
2191 ################################################################################
2194 def __init__(self, *args, **kwargs):
2197 def __eq__(self, val):
2198 if isinstance(val, str):
2199 return (self.uid == val)
2200 # This signals to use the normal comparison operator
2201 return NotImplemented
2203 def __ne__(self, val):
2204 if isinstance(val, str):
2205 return (self.uid != val)
2206 # This signals to use the normal comparison operator
2207 return NotImplemented
2210 return '<Uid %s (%s)>' % (self.uid, self.name)
2212 __all__.append('Uid')
2214 def add_database_user(uidname, session=None):
2216 Adds a database user
2218 @type uidname: string
2219 @param uidname: The uid of the user to add
2221 @type session: SQLAlchemy
2222 @param session: Optional SQL session object (a temporary one will be
2223 generated if not supplied). If not passed, a commit will be performed at
2224 the end of the function, otherwise the caller is responsible for commiting.
2227 @return: the uid object for the given uidname
2230 privatetrans = False
2232 session = DBConn().session()
2235 session.execute("CREATE USER :uid", {'uid': uidname})
2241 __all__.append('add_database_user')
2243 def get_or_set_uid(uidname, session=None):
2245 Returns uid object for given uidname.
2247 If no matching uidname is found, a row is inserted.
2249 @type uidname: string
2250 @param uidname: The uid to add
2252 @type session: SQLAlchemy
2253 @param session: Optional SQL session object (a temporary one will be
2254 generated if not supplied). If not passed, a commit will be performed at
2255 the end of the function, otherwise the caller is responsible for commiting.
2258 @return: the uid object for the given uidname
2261 privatetrans = False
2263 session = DBConn().session()
2266 q = session.query(Uid).filter_by(uid=uidname)
2285 __all__.append('get_or_set_uid')
2288 def get_uid_from_fingerprint(fpr, session=None):
2289 privatetrans = False
2291 session = DBConn().session()
2294 q = session.query(Uid)
2295 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2307 __all__.append('get_uid_from_fingerprint')
2309 ################################################################################
2311 class DBConn(Singleton):
2313 database module init.
2315 def __init__(self, *args, **kwargs):
2316 super(DBConn, self).__init__(*args, **kwargs)
2318 def _startup(self, *args, **kwargs):
2320 if kwargs.has_key('debug'):
2324 def __setuptables(self):
2325 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2326 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2327 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2328 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2329 self.tbl_component = Table('component', self.db_meta, autoload=True)
2330 self.tbl_config = Table('config', self.db_meta, autoload=True)
2331 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2332 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2333 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2334 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2335 self.tbl_files = Table('files', self.db_meta, autoload=True)
2336 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2337 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2338 self.tbl_location = Table('location', self.db_meta, autoload=True)
2339 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2340 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2341 self.tbl_override = Table('override', self.db_meta, autoload=True)
2342 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2343 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2344 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2345 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2346 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2347 self.tbl_section = Table('section', self.db_meta, autoload=True)
2348 self.tbl_source = Table('source', self.db_meta, autoload=True)
2349 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2350 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2351 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2352 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2353 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2355 def __setupmappers(self):
2356 mapper(Architecture, self.tbl_architecture,
2357 properties = dict(arch_id = self.tbl_architecture.c.id))
2359 mapper(Archive, self.tbl_archive,
2360 properties = dict(archive_id = self.tbl_archive.c.id,
2361 archive_name = self.tbl_archive.c.name))
2363 mapper(BinAssociation, self.tbl_bin_associations,
2364 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2365 suite_id = self.tbl_bin_associations.c.suite,
2366 suite = relation(Suite),
2367 binary_id = self.tbl_bin_associations.c.bin,
2368 binary = relation(DBBinary)))
2370 mapper(DBBinary, self.tbl_binaries,
2371 properties = dict(binary_id = self.tbl_binaries.c.id,
2372 package = self.tbl_binaries.c.package,
2373 version = self.tbl_binaries.c.version,
2374 maintainer_id = self.tbl_binaries.c.maintainer,
2375 maintainer = relation(Maintainer),
2376 source_id = self.tbl_binaries.c.source,
2377 source = relation(DBSource),
2378 arch_id = self.tbl_binaries.c.architecture,
2379 architecture = relation(Architecture),
2380 poolfile_id = self.tbl_binaries.c.file,
2381 poolfile = relation(PoolFile),
2382 binarytype = self.tbl_binaries.c.type,
2383 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2384 fingerprint = relation(Fingerprint),
2385 install_date = self.tbl_binaries.c.install_date,
2386 binassociations = relation(BinAssociation,
2387 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2389 mapper(Component, self.tbl_component,
2390 properties = dict(component_id = self.tbl_component.c.id,
2391 component_name = self.tbl_component.c.name))
2393 mapper(DBConfig, self.tbl_config,
2394 properties = dict(config_id = self.tbl_config.c.id))
2396 mapper(ContentAssociation, self.tbl_content_associations,
2397 properties = dict(ca_id = self.tbl_content_associations.c.id,
2398 filename_id = self.tbl_content_associations.c.filename,
2399 filename = relation(ContentFilename),
2400 filepath_id = self.tbl_content_associations.c.filepath,
2401 filepath = relation(ContentFilepath),
2402 binary_id = self.tbl_content_associations.c.binary_pkg,
2403 binary = relation(DBBinary)))
2406 mapper(ContentFilename, self.tbl_content_file_names,
2407 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2408 filename = self.tbl_content_file_names.c.file))
2410 mapper(ContentFilepath, self.tbl_content_file_paths,
2411 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2412 filepath = self.tbl_content_file_paths.c.path))
2414 mapper(DSCFile, self.tbl_dsc_files,
2415 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2416 source_id = self.tbl_dsc_files.c.source,
2417 source = relation(DBSource),
2418 poolfile_id = self.tbl_dsc_files.c.file,
2419 poolfile = relation(PoolFile)))
2421 mapper(PoolFile, self.tbl_files,
2422 properties = dict(file_id = self.tbl_files.c.id,
2423 filesize = self.tbl_files.c.size,
2424 location_id = self.tbl_files.c.location,
2425 location = relation(Location)))
2427 mapper(Fingerprint, self.tbl_fingerprint,
2428 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2429 uid_id = self.tbl_fingerprint.c.uid,
2430 uid = relation(Uid),
2431 keyring_id = self.tbl_fingerprint.c.keyring,
2432 keyring = relation(Keyring)))
2434 mapper(Keyring, self.tbl_keyrings,
2435 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2436 keyring_id = self.tbl_keyrings.c.id))
2438 mapper(Location, self.tbl_location,
2439 properties = dict(location_id = self.tbl_location.c.id,
2440 component_id = self.tbl_location.c.component,
2441 component = relation(Component),
2442 archive_id = self.tbl_location.c.archive,
2443 archive = relation(Archive),
2444 archive_type = self.tbl_location.c.type))
2446 mapper(Maintainer, self.tbl_maintainer,
2447 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2449 mapper(NewComment, self.tbl_new_comments,
2450 properties = dict(comment_id = self.tbl_new_comments.c.id))
2452 mapper(Override, self.tbl_override,
2453 properties = dict(suite_id = self.tbl_override.c.suite,
2454 suite = relation(Suite),
2455 component_id = self.tbl_override.c.component,
2456 component = relation(Component),
2457 priority_id = self.tbl_override.c.priority,
2458 priority = relation(Priority),
2459 section_id = self.tbl_override.c.section,
2460 section = relation(Section),
2461 overridetype_id = self.tbl_override.c.type,
2462 overridetype = relation(OverrideType)))
2464 mapper(OverrideType, self.tbl_override_type,
2465 properties = dict(overridetype = self.tbl_override_type.c.type,
2466 overridetype_id = self.tbl_override_type.c.id))
2468 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2469 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2470 filepath_id = self.tbl_pending_content_associations.c.filepath,
2471 filepath = relation(ContentFilepath),
2472 filename_id = self.tbl_pending_content_associations.c.filename,
2473 filename = relation(ContentFilename)))
2475 mapper(Priority, self.tbl_priority,
2476 properties = dict(priority_id = self.tbl_priority.c.id))
2478 mapper(Queue, self.tbl_queue,
2479 properties = dict(queue_id = self.tbl_queue.c.id))
2481 mapper(QueueBuild, self.tbl_queue_build,
2482 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2483 queue_id = self.tbl_queue_build.c.queue,
2484 queue = relation(Queue, backref='queuebuild')))
2486 mapper(Section, self.tbl_section,
2487 properties = dict(section_id = self.tbl_section.c.id))
2489 mapper(DBSource, self.tbl_source,
2490 properties = dict(source_id = self.tbl_source.c.id,
2491 version = self.tbl_source.c.version,
2492 maintainer_id = self.tbl_source.c.maintainer,
2493 maintainer = relation(Maintainer,
2494 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2495 poolfile_id = self.tbl_source.c.file,
2496 poolfile = relation(PoolFile),
2497 fingerprint_id = self.tbl_source.c.sig_fpr,
2498 fingerprint = relation(Fingerprint),
2499 changedby_id = self.tbl_source.c.changedby,
2500 changedby = relation(Maintainer,
2501 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2502 srcfiles = relation(DSCFile,
2503 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2504 srcassociations = relation(SrcAssociation,
2505 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2507 mapper(SrcAssociation, self.tbl_src_associations,
2508 properties = dict(sa_id = self.tbl_src_associations.c.id,
2509 suite_id = self.tbl_src_associations.c.suite,
2510 suite = relation(Suite),
2511 source_id = self.tbl_src_associations.c.source,
2512 source = relation(DBSource)))
2514 mapper(SrcUploader, self.tbl_src_uploaders,
2515 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2516 source_id = self.tbl_src_uploaders.c.source,
2517 source = relation(DBSource,
2518 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2519 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2520 maintainer = relation(Maintainer,
2521 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2523 mapper(Suite, self.tbl_suite,
2524 properties = dict(suite_id = self.tbl_suite.c.id))
2526 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2527 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2528 suite = relation(Suite, backref='suitearchitectures'),
2529 arch_id = self.tbl_suite_architectures.c.architecture,
2530 architecture = relation(Architecture)))
2532 mapper(Uid, self.tbl_uid,
2533 properties = dict(uid_id = self.tbl_uid.c.id,
2534 fingerprint = relation(Fingerprint)))
2536 ## Connection functions
2537 def __createconn(self):
2538 from config import Config
2542 connstr = "postgres://%s" % cnf["DB::Host"]
2543 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2544 connstr += ":%s" % cnf["DB::Port"]
2545 connstr += "/%s" % cnf["DB::Name"]
2548 connstr = "postgres:///%s" % cnf["DB::Name"]
2549 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2550 connstr += "?port=%s" % cnf["DB::Port"]
2552 self.db_pg = create_engine(connstr, echo=self.debug)
2553 self.db_meta = MetaData()
2554 self.db_meta.bind = self.db_pg
2555 self.db_smaker = sessionmaker(bind=self.db_pg,
2559 self.__setuptables()
2560 self.__setupmappers()
2563 return self.db_smaker()
2565 __all__.append('DBConn')