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}.
1102 @type maintainer_id: int
1103 @param maintainer_id: the id of the maintainer
1106 @return: the name of the maintainer
1109 privatetrans = False
1111 session = DBConn().session()
1115 return session.query(Maintainer).get(maintainer_id).name
1120 __all__.append('get_maintainer')
1122 ################################################################################
1124 class NewComment(object):
1125 def __init__(self, *args, **kwargs):
1129 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1131 __all__.append('NewComment')
1133 def has_new_comment(package, version, session=None):
1135 Returns true if the given combination of C{package}, C{version} has a comment.
1137 @type package: string
1138 @param package: name of the package
1140 @type version: string
1141 @param version: package version
1143 @type session: Session
1144 @param session: Optional SQLA session object (a temporary one will be
1145 generated if not supplied)
1151 privatetrans = False
1153 session = DBConn().session()
1156 q = session.query(NewComment)
1157 q = q.filter_by(package=package)
1158 q = q.filter_by(version=version)
1167 __all__.append('has_new_comment')
1169 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1171 Returns (possibly empty) list of NewComment objects for the given
1174 @type package: string (optional)
1175 @param package: name of the package
1177 @type version: string (optional)
1178 @param version: package version
1180 @type comment_id: int (optional)
1181 @param comment_id: An id of a comment
1183 @type session: Session
1184 @param session: Optional SQLA session object (a temporary one will be
1185 generated if not supplied)
1188 @return: A (possibly empty) list of NewComment objects will be returned
1192 privatetrans = False
1194 session = DBConn().session()
1197 q = session.query(NewComment)
1198 if package is not None: q = q.filter_by(package=package)
1199 if version is not None: q = q.filter_by(version=version)
1200 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1209 __all__.append('get_new_comments')
1211 ################################################################################
1213 class Override(object):
1214 def __init__(self, *args, **kwargs):
1218 return '<Override %s (%s)>' % (self.package, self.suite_id)
1220 __all__.append('Override')
1222 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1224 Returns Override object for the given parameters
1226 @type package: string
1227 @param package: The name of the package
1229 @type suite: string, list or None
1230 @param suite: The name of the suite (or suites if a list) to limit to. If
1231 None, don't limit. Defaults to None.
1233 @type component: string, list or None
1234 @param component: The name of the component (or components if a list) to
1235 limit to. If None, don't limit. Defaults to None.
1237 @type overridetype: string, list or None
1238 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1239 limit to. If None, don't limit. Defaults to None.
1241 @type session: Session
1242 @param session: Optional SQLA session object (a temporary one will be
1243 generated if not supplied)
1246 @return: A (possibly empty) list of Override objects will be returned
1249 privatetrans = False
1251 session = DBConn().session()
1254 q = session.query(Override)
1255 q = q.filter_by(package=package)
1257 if suite is not None:
1258 if not isinstance(suite, list): suite = [suite]
1259 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1261 if component is not None:
1262 if not isinstance(component, list): component = [component]
1263 q = q.join(Component).filter(Component.component_name.in_(component))
1265 if overridetype is not None:
1266 if not isinstance(overridetype, list): overridetype = [overridetype]
1267 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1276 __all__.append('get_override')
1279 ################################################################################
1281 class OverrideType(object):
1282 def __init__(self, *args, **kwargs):
1286 return '<OverrideType %s>' % self.overridetype
1288 __all__.append('OverrideType')
1290 def get_override_type(override_type, session=None):
1292 Returns OverrideType object for given C{override type}.
1294 @type override_type: string
1295 @param override_type: The name of the override type
1297 @type session: Session
1298 @param session: Optional SQLA session object (a temporary one will be
1299 generated if not supplied)
1302 @return: the database id for the given override type
1305 privatetrans = False
1307 session = DBConn().session()
1310 q = session.query(OverrideType).filter_by(overridetype=override_type)
1322 __all__.append('get_override_type')
1324 ################################################################################
1326 class PendingContentAssociation(object):
1327 def __init__(self, *args, **kwargs):
1331 return '<PendingContentAssociation %s>' % self.pca_id
1333 __all__.append('PendingContentAssociation')
1335 def insert_pending_content_paths(package, fullpaths, session=None):
1337 Make sure given paths are temporarily associated with given
1341 @param package: the package to associate with should have been read in from the binary control file
1342 @type fullpaths: list
1343 @param fullpaths: the list of paths of the file being associated with the binary
1344 @type session: SQLAlchemy session
1345 @param session: Optional SQLAlchemy session. If this is passed, the caller
1346 is responsible for ensuring a transaction has begun and committing the
1347 results or rolling back based on the result code. If not passed, a commit
1348 will be performed at the end of the function
1350 @return: True upon success, False if there is a problem
1353 privatetrans = False
1356 session = DBConn().session()
1360 arch = get_architecture(package['Architecture'], session)
1361 arch_id = arch.arch_id
1363 # Remove any already existing recorded files for this package
1364 q = session.query(PendingContentAssociation)
1365 q = q.filter_by(package=package['Package'])
1366 q = q.filter_by(version=package['Version'])
1367 q = q.filter_by(architecture=arch_id)
1372 for fullpath in fullpaths:
1373 (path, file) = os.path.split(fullpath)
1375 if path.startswith( "./" ):
1378 filepath_id = get_or_set_contents_path_id(path, session)
1379 filename_id = get_or_set_contents_file_id(file, session)
1381 pathcache[fullpath] = (filepath_id, filename_id)
1383 for fullpath, dat in pathcache.items():
1384 pca = PendingContentAssociation()
1385 pca.package = package['Package']
1386 pca.version = package['Version']
1387 pca.filepath_id = dat[0]
1388 pca.filename_id = dat[1]
1389 pca.architecture = arch_id
1392 # Only commit if we set up the session ourself
1400 except Exception, e:
1401 traceback.print_exc()
1403 # Only rollback if we set up the session ourself
1410 __all__.append('insert_pending_content_paths')
1412 ################################################################################
1414 class Priority(object):
1415 def __init__(self, *args, **kwargs):
1418 def __eq__(self, val):
1419 if isinstance(val, str):
1420 return (self.priority == val)
1421 # This signals to use the normal comparison operator
1422 return NotImplemented
1424 def __ne__(self, val):
1425 if isinstance(val, str):
1426 return (self.priority != val)
1427 # This signals to use the normal comparison operator
1428 return NotImplemented
1431 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1433 __all__.append('Priority')
1435 def get_priority(priority, session=None):
1437 Returns Priority object for given C{priority name}.
1439 @type priority: string
1440 @param priority: The name of the priority
1442 @type session: Session
1443 @param session: Optional SQLA session object (a temporary one will be
1444 generated if not supplied)
1447 @return: Priority object for the given priority
1450 privatetrans = False
1452 session = DBConn().session()
1455 q = session.query(Priority).filter_by(priority=priority)
1467 __all__.append('get_priority')
1469 def get_priorities(session=None):
1471 Returns dictionary of priority names -> id mappings
1473 @type session: Session
1474 @param session: Optional SQL session object (a temporary one will be
1475 generated if not supplied)
1478 @return: dictionary of priority names -> id mappings
1480 privatetrans = False
1482 session = DBConn().session()
1486 q = session.query(Priority)
1488 ret[x.priority] = x.priority_id
1495 __all__.append('get_priorities')
1497 ################################################################################
1499 class Queue(object):
1500 def __init__(self, *args, **kwargs):
1504 return '<Queue %s>' % self.queue_name
1506 def autobuild_upload(self, changes, srcpath, session=None):
1508 Update queue_build database table used for incoming autobuild support.
1510 @type changes: Changes
1511 @param changes: changes object for the upload to process
1513 @type srcpath: string
1514 @param srcpath: path for the queue file entries/link destinations
1516 @type session: SQLAlchemy session
1517 @param session: Optional SQLAlchemy session. If this is passed, the
1518 caller is responsible for ensuring a transaction has begun and
1519 committing the results or rolling back based on the result code. If
1520 not passed, a commit will be performed at the end of the function,
1521 otherwise the caller is responsible for commiting.
1523 @rtype: NoneType or string
1524 @return: None if the operation failed, a string describing the error if not
1527 privatetrans = False
1529 session = DBConn().session()
1532 # TODO: Remove by moving queue config into the database
1535 for suitename in changes.changes["distribution"].keys():
1536 # TODO: Move into database as:
1537 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1538 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1539 # This also gets rid of the SecurityQueueBuild hack below
1540 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1544 s = get_suite(suitename, session)
1546 return "INTERNAL ERROR: Could not find suite %s" % suitename
1548 # TODO: Get from database as above
1549 dest_dir = conf["Dir::QueueBuild"]
1551 # TODO: Move into database as above
1552 if conf.FindB("Dinstall::SecurityQueueBuild"):
1553 dest_dir = os.path.join(dest_dir, suitename)
1555 for file_entry in changes.files.keys():
1556 src = os.path.join(srcpath, file_entry)
1557 dest = os.path.join(dest_dir, file_entry)
1559 # TODO: Move into database as above
1560 if conf.FindB("Dinstall::SecurityQueueBuild"):
1561 # Copy it since the original won't be readable by www-data
1562 utils.copy(src, dest)
1564 # Create a symlink to it
1565 os.symlink(src, dest)
1568 qb.suite_id = s.suite_id
1569 qb.queue_id = self.queue_id
1575 # If the .orig.tar.gz is in the pool, create a symlink to
1576 # it (if one doesn't already exist)
1577 if changes.orig_tar_id:
1578 # Determine the .orig.tar.gz file name
1579 for dsc_file in changes.dsc_files.keys():
1580 if dsc_file.endswith(".orig.tar.gz"):
1583 dest = os.path.join(dest_dir, filename)
1585 # If it doesn't exist, create a symlink
1586 if not os.path.exists(dest):
1587 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1588 {'id': changes.orig_tar_id})
1591 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1593 src = os.path.join(res[0], res[1])
1594 os.symlink(src, dest)
1596 # Add it to the list of packages for later processing by apt-ftparchive
1598 qb.suite_id = s.suite_id
1599 qb.queue_id = self.queue_id
1604 # If it does, update things to ensure it's not removed prematurely
1606 qb = get_queue_build(dest, s.suite_id, session)
1618 __all__.append('Queue')
1620 def get_queue(queuename, session=None):
1622 Returns Queue object for given C{queue name}.
1624 @type queuename: string
1625 @param queuename: The name of the queue
1627 @type session: Session
1628 @param session: Optional SQLA session object (a temporary one will be
1629 generated if not supplied)
1632 @return: Queue object for the given queue
1635 privatetrans = False
1637 session = DBConn().session()
1640 q = session.query(Queue).filter_by(queue_name=queuename)
1651 __all__.append('get_queue')
1653 ################################################################################
1655 class QueueBuild(object):
1656 def __init__(self, *args, **kwargs):
1660 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1662 __all__.append('QueueBuild')
1664 def get_queue_build(filename, suite, session=None):
1666 Returns QueueBuild object for given C{filename} and C{suite}.
1668 @type filename: string
1669 @param filename: The name of the file
1671 @type suiteid: int or str
1672 @param suiteid: Suite name or ID
1674 @type session: Session
1675 @param session: Optional SQLA session object (a temporary one will be
1676 generated if not supplied)
1679 @return: Queue object for the given queue
1682 privatetrans = False
1684 session = DBConn().session()
1687 if isinstance(suite, int):
1688 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1690 q = session.query(QueueBuild).filter_by(filename=filename)
1691 q = q.join(Suite).filter_by(suite_name=suite)
1703 __all__.append('get_queue_build')
1705 ################################################################################
1707 class Section(object):
1708 def __init__(self, *args, **kwargs):
1711 def __eq__(self, val):
1712 if isinstance(val, str):
1713 return (self.section == val)
1714 # This signals to use the normal comparison operator
1715 return NotImplemented
1717 def __ne__(self, val):
1718 if isinstance(val, str):
1719 return (self.section != val)
1720 # This signals to use the normal comparison operator
1721 return NotImplemented
1724 return '<Section %s>' % self.section
1726 __all__.append('Section')
1728 def get_section(section, session=None):
1730 Returns Section object for given C{section name}.
1732 @type section: string
1733 @param section: The name of the section
1735 @type session: Session
1736 @param session: Optional SQLA session object (a temporary one will be
1737 generated if not supplied)
1740 @return: Section object for the given section name
1743 privatetrans = False
1745 session = DBConn().session()
1748 q = session.query(Section).filter_by(section=section)
1759 __all__.append('get_section')
1761 def get_sections(session=None):
1763 Returns dictionary of section names -> id mappings
1765 @type session: Session
1766 @param session: Optional SQL session object (a temporary one will be
1767 generated if not supplied)
1770 @return: dictionary of section names -> id mappings
1772 privatetrans = False
1774 session = DBConn().session()
1778 q = session.query(Section)
1780 ret[x.section] = x.section_id
1787 __all__.append('get_sections')
1789 ################################################################################
1791 class DBSource(object):
1792 def __init__(self, *args, **kwargs):
1796 return '<DBSource %s (%s)>' % (self.source, self.version)
1798 __all__.append('DBSource')
1800 def source_exists(source, source_version, suites = ["any"], session=None):
1802 Ensure that source exists somewhere in the archive for the binary
1803 upload being processed.
1804 1. exact match => 1.0-3
1805 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1807 @type package: string
1808 @param package: package source name
1810 @type source_version: string
1811 @param source_version: expected source version
1814 @param suites: list of suites to check in, default I{any}
1816 @type session: Session
1817 @param session: Optional SQLA session object (a temporary one will be
1818 generated if not supplied)
1821 @return: returns 1 if a source with expected version is found, otherwise 0
1825 privatetrans = False
1827 session = DBConn().session()
1833 for suite in suites:
1834 q = session.query(DBSource).filter_by(source=source)
1836 # source must exist in suite X, or in some other suite that's
1837 # mapped to X, recursively... silent-maps are counted too,
1838 # unreleased-maps aren't.
1839 maps = cnf.ValueList("SuiteMappings")[:]
1841 maps = [ m.split() for m in maps ]
1842 maps = [ (x[1], x[2]) for x in maps
1843 if x[0] == "map" or x[0] == "silent-map" ]
1846 if x[1] in s and x[0] not in s:
1849 q = q.join(SrcAssociation).join(Suite)
1850 q = q.filter(Suite.suite_name.in_(s))
1852 # Reduce the query results to a list of version numbers
1853 ql = [ j.version for j in q.all() ]
1856 if source_version in ql:
1860 from daklib.regexes import re_bin_only_nmu
1861 orig_source_version = re_bin_only_nmu.sub('', source_version)
1862 if orig_source_version in ql:
1865 # No source found so return not ok
1873 __all__.append('source_exists')
1875 def get_suites_source_in(source, session=None):
1877 Returns list of Suite objects which given C{source} name is in
1880 @param source: DBSource package name to search for
1883 @return: list of Suite objects for the given source
1886 privatetrans = False
1888 session = DBConn().session()
1891 ret = session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1898 __all__.append('get_suites_source_in')
1900 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1902 Returns list of DBSource objects for given C{source} name and other parameters
1905 @param source: DBSource package name to search for
1907 @type source: str or None
1908 @param source: DBSource version name to search for or None if not applicable
1910 @type dm_upload_allowed: bool
1911 @param dm_upload_allowed: If None, no effect. If True or False, only
1912 return packages with that dm_upload_allowed setting
1914 @type session: Session
1915 @param session: Optional SQL session object (a temporary one will be
1916 generated if not supplied)
1919 @return: list of DBSource objects for the given name (may be empty)
1921 privatetrans = False
1923 session = DBConn().session()
1926 q = session.query(DBSource).filter_by(source=source)
1928 if version is not None:
1929 q = q.filter_by(version=version)
1931 if dm_upload_allowed is not None:
1932 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1941 __all__.append('get_sources_from_name')
1943 def get_source_in_suite(source, suite, session=None):
1945 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1947 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1948 - B{suite} - a suite name, eg. I{unstable}
1950 @type source: string
1951 @param source: source package name
1954 @param suite: the suite name
1957 @return: the version for I{source} in I{suite}
1960 privatetrans = False
1962 session = DBConn().session()
1965 q = session.query(SrcAssociation)
1966 q = q.join('source').filter_by(source=source)
1967 q = q.join('suite').filter_by(suite_name=suite)
1972 # ???: Maybe we should just return the SrcAssociation object instead
1973 ret = q.one().source
1980 __all__.append('get_source_in_suite')
1982 ################################################################################
1984 class SrcAssociation(object):
1985 def __init__(self, *args, **kwargs):
1989 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1991 __all__.append('SrcAssociation')
1993 ################################################################################
1995 class SrcUploader(object):
1996 def __init__(self, *args, **kwargs):
2000 return '<SrcUploader %s>' % self.uploader_id
2002 __all__.append('SrcUploader')
2004 ################################################################################
2006 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
2007 ('SuiteID', 'suite_id'),
2008 ('Version', 'version'),
2009 ('Origin', 'origin'),
2011 ('Description', 'description'),
2012 ('Untouchable', 'untouchable'),
2013 ('Announce', 'announce'),
2014 ('Codename', 'codename'),
2015 ('OverrideCodename', 'overridecodename'),
2016 ('ValidTime', 'validtime'),
2017 ('Priority', 'priority'),
2018 ('NotAutomatic', 'notautomatic'),
2019 ('CopyChanges', 'copychanges'),
2020 ('CopyDotDak', 'copydotdak'),
2021 ('CommentsDir', 'commentsdir'),
2022 ('OverrideSuite', 'overridesuite'),
2023 ('ChangelogBase', 'changelogbase')]
2026 class Suite(object):
2027 def __init__(self, *args, **kwargs):
2031 return '<Suite %s>' % self.suite_name
2033 def __eq__(self, val):
2034 if isinstance(val, str):
2035 return (self.suite_name == val)
2036 # This signals to use the normal comparison operator
2037 return NotImplemented
2039 def __ne__(self, val):
2040 if isinstance(val, str):
2041 return (self.suite_name != val)
2042 # This signals to use the normal comparison operator
2043 return NotImplemented
2047 for disp, field in SUITE_FIELDS:
2048 val = getattr(self, field, None)
2050 ret.append("%s: %s" % (disp, val))
2052 return "\n".join(ret)
2054 __all__.append('Suite')
2056 def get_suite_architecture(suite, architecture, session=None):
2058 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
2062 @param suite: Suite name to search for
2064 @type architecture: str
2065 @param architecture: Architecture name to search for
2067 @type session: Session
2068 @param session: Optional SQL session object (a temporary one will be
2069 generated if not supplied)
2071 @rtype: SuiteArchitecture
2072 @return: the SuiteArchitecture object or None
2075 privatetrans = False
2077 session = DBConn().session()
2080 q = session.query(SuiteArchitecture)
2081 q = q.join(Architecture).filter_by(arch_string=architecture)
2082 q = q.join(Suite).filter_by(suite_name=suite)
2094 __all__.append('get_suite_architecture')
2096 def get_suite(suite, session=None):
2098 Returns Suite object for given C{suite name}.
2101 @param suite: The name of the suite
2103 @type session: Session
2104 @param session: Optional SQLA session object (a temporary one will be
2105 generated if not supplied)
2108 @return: Suite object for the requested suite name (None if not presenT)
2111 privatetrans = False
2113 session = DBConn().session()
2116 q = session.query(Suite).filter_by(suite_name=suite)
2128 __all__.append('get_suite')
2130 ################################################################################
2132 class SuiteArchitecture(object):
2133 def __init__(self, *args, **kwargs):
2137 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
2139 __all__.append('SuiteArchitecture')
2141 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
2143 Returns list of Architecture objects for given C{suite} name
2146 @param source: Suite name to search for
2148 @type skipsrc: boolean
2149 @param skipsrc: Whether to skip returning the 'source' architecture entry
2152 @type skipall: boolean
2153 @param skipall: Whether to skip returning the 'all' architecture entry
2156 @type session: Session
2157 @param session: Optional SQL session object (a temporary one will be
2158 generated if not supplied)
2161 @return: list of Architecture objects for the given name (may be empty)
2164 privatetrans = False
2166 session = DBConn().session()
2169 q = session.query(Architecture)
2170 q = q.join(SuiteArchitecture)
2171 q = q.join(Suite).filter_by(suite_name=suite)
2174 q = q.filter(Architecture.arch_string != 'source')
2177 q = q.filter(Architecture.arch_string != 'all')
2179 q = q.order_by('arch_string')
2188 __all__.append('get_suite_architectures')
2190 ################################################################################
2193 def __init__(self, *args, **kwargs):
2196 def __eq__(self, val):
2197 if isinstance(val, str):
2198 return (self.uid == val)
2199 # This signals to use the normal comparison operator
2200 return NotImplemented
2202 def __ne__(self, val):
2203 if isinstance(val, str):
2204 return (self.uid != val)
2205 # This signals to use the normal comparison operator
2206 return NotImplemented
2209 return '<Uid %s (%s)>' % (self.uid, self.name)
2211 __all__.append('Uid')
2213 def add_database_user(uidname, session=None):
2215 Adds a database user
2217 @type uidname: string
2218 @param uidname: The uid of the user to add
2220 @type session: SQLAlchemy
2221 @param session: Optional SQL session object (a temporary one will be
2222 generated if not supplied). If not passed, a commit will be performed at
2223 the end of the function, otherwise the caller is responsible for commiting.
2226 @return: the uid object for the given uidname
2229 privatetrans = False
2231 session = DBConn().session()
2234 session.execute("CREATE USER :uid", {'uid': uidname})
2240 __all__.append('add_database_user')
2242 def get_or_set_uid(uidname, session=None):
2244 Returns uid object for given uidname.
2246 If no matching uidname is found, a row is inserted.
2248 @type uidname: string
2249 @param uidname: The uid to add
2251 @type session: SQLAlchemy
2252 @param session: Optional SQL session object (a temporary one will be
2253 generated if not supplied). If not passed, a commit will be performed at
2254 the end of the function, otherwise the caller is responsible for commiting.
2257 @return: the uid object for the given uidname
2260 privatetrans = False
2262 session = DBConn().session()
2265 q = session.query(Uid).filter_by(uid=uidname)
2284 __all__.append('get_or_set_uid')
2287 def get_uid_from_fingerprint(fpr, session=None):
2288 privatetrans = False
2290 session = DBConn().session()
2293 q = session.query(Uid)
2294 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2306 __all__.append('get_uid_from_fingerprint')
2308 ################################################################################
2310 class DBConn(Singleton):
2312 database module init.
2314 def __init__(self, *args, **kwargs):
2315 super(DBConn, self).__init__(*args, **kwargs)
2317 def _startup(self, *args, **kwargs):
2319 if kwargs.has_key('debug'):
2323 def __setuptables(self):
2324 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2325 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2326 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2327 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2328 self.tbl_component = Table('component', self.db_meta, autoload=True)
2329 self.tbl_config = Table('config', self.db_meta, autoload=True)
2330 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2331 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2332 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2333 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2334 self.tbl_files = Table('files', self.db_meta, autoload=True)
2335 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2336 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2337 self.tbl_location = Table('location', self.db_meta, autoload=True)
2338 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2339 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2340 self.tbl_override = Table('override', self.db_meta, autoload=True)
2341 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2342 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2343 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2344 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2345 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2346 self.tbl_section = Table('section', self.db_meta, autoload=True)
2347 self.tbl_source = Table('source', self.db_meta, autoload=True)
2348 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2349 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2350 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2351 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2352 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2354 def __setupmappers(self):
2355 mapper(Architecture, self.tbl_architecture,
2356 properties = dict(arch_id = self.tbl_architecture.c.id))
2358 mapper(Archive, self.tbl_archive,
2359 properties = dict(archive_id = self.tbl_archive.c.id,
2360 archive_name = self.tbl_archive.c.name))
2362 mapper(BinAssociation, self.tbl_bin_associations,
2363 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2364 suite_id = self.tbl_bin_associations.c.suite,
2365 suite = relation(Suite),
2366 binary_id = self.tbl_bin_associations.c.bin,
2367 binary = relation(DBBinary)))
2369 mapper(DBBinary, self.tbl_binaries,
2370 properties = dict(binary_id = self.tbl_binaries.c.id,
2371 package = self.tbl_binaries.c.package,
2372 version = self.tbl_binaries.c.version,
2373 maintainer_id = self.tbl_binaries.c.maintainer,
2374 maintainer = relation(Maintainer),
2375 source_id = self.tbl_binaries.c.source,
2376 source = relation(DBSource),
2377 arch_id = self.tbl_binaries.c.architecture,
2378 architecture = relation(Architecture),
2379 poolfile_id = self.tbl_binaries.c.file,
2380 poolfile = relation(PoolFile),
2381 binarytype = self.tbl_binaries.c.type,
2382 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2383 fingerprint = relation(Fingerprint),
2384 install_date = self.tbl_binaries.c.install_date,
2385 binassociations = relation(BinAssociation,
2386 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2388 mapper(Component, self.tbl_component,
2389 properties = dict(component_id = self.tbl_component.c.id,
2390 component_name = self.tbl_component.c.name))
2392 mapper(DBConfig, self.tbl_config,
2393 properties = dict(config_id = self.tbl_config.c.id))
2395 mapper(ContentAssociation, self.tbl_content_associations,
2396 properties = dict(ca_id = self.tbl_content_associations.c.id,
2397 filename_id = self.tbl_content_associations.c.filename,
2398 filename = relation(ContentFilename),
2399 filepath_id = self.tbl_content_associations.c.filepath,
2400 filepath = relation(ContentFilepath),
2401 binary_id = self.tbl_content_associations.c.binary_pkg,
2402 binary = relation(DBBinary)))
2405 mapper(ContentFilename, self.tbl_content_file_names,
2406 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2407 filename = self.tbl_content_file_names.c.file))
2409 mapper(ContentFilepath, self.tbl_content_file_paths,
2410 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2411 filepath = self.tbl_content_file_paths.c.path))
2413 mapper(DSCFile, self.tbl_dsc_files,
2414 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2415 source_id = self.tbl_dsc_files.c.source,
2416 source = relation(DBSource),
2417 poolfile_id = self.tbl_dsc_files.c.file,
2418 poolfile = relation(PoolFile)))
2420 mapper(PoolFile, self.tbl_files,
2421 properties = dict(file_id = self.tbl_files.c.id,
2422 filesize = self.tbl_files.c.size,
2423 location_id = self.tbl_files.c.location,
2424 location = relation(Location)))
2426 mapper(Fingerprint, self.tbl_fingerprint,
2427 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2428 uid_id = self.tbl_fingerprint.c.uid,
2429 uid = relation(Uid),
2430 keyring_id = self.tbl_fingerprint.c.keyring,
2431 keyring = relation(Keyring)))
2433 mapper(Keyring, self.tbl_keyrings,
2434 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2435 keyring_id = self.tbl_keyrings.c.id))
2437 mapper(Location, self.tbl_location,
2438 properties = dict(location_id = self.tbl_location.c.id,
2439 component_id = self.tbl_location.c.component,
2440 component = relation(Component),
2441 archive_id = self.tbl_location.c.archive,
2442 archive = relation(Archive),
2443 archive_type = self.tbl_location.c.type))
2445 mapper(Maintainer, self.tbl_maintainer,
2446 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2448 mapper(NewComment, self.tbl_new_comments,
2449 properties = dict(comment_id = self.tbl_new_comments.c.id))
2451 mapper(Override, self.tbl_override,
2452 properties = dict(suite_id = self.tbl_override.c.suite,
2453 suite = relation(Suite),
2454 component_id = self.tbl_override.c.component,
2455 component = relation(Component),
2456 priority_id = self.tbl_override.c.priority,
2457 priority = relation(Priority),
2458 section_id = self.tbl_override.c.section,
2459 section = relation(Section),
2460 overridetype_id = self.tbl_override.c.type,
2461 overridetype = relation(OverrideType)))
2463 mapper(OverrideType, self.tbl_override_type,
2464 properties = dict(overridetype = self.tbl_override_type.c.type,
2465 overridetype_id = self.tbl_override_type.c.id))
2467 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2468 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2469 filepath_id = self.tbl_pending_content_associations.c.filepath,
2470 filepath = relation(ContentFilepath),
2471 filename_id = self.tbl_pending_content_associations.c.filename,
2472 filename = relation(ContentFilename)))
2474 mapper(Priority, self.tbl_priority,
2475 properties = dict(priority_id = self.tbl_priority.c.id))
2477 mapper(Queue, self.tbl_queue,
2478 properties = dict(queue_id = self.tbl_queue.c.id))
2480 mapper(QueueBuild, self.tbl_queue_build,
2481 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2482 queue_id = self.tbl_queue_build.c.queue,
2483 queue = relation(Queue, backref='queuebuild')))
2485 mapper(Section, self.tbl_section,
2486 properties = dict(section_id = self.tbl_section.c.id))
2488 mapper(DBSource, self.tbl_source,
2489 properties = dict(source_id = self.tbl_source.c.id,
2490 version = self.tbl_source.c.version,
2491 maintainer_id = self.tbl_source.c.maintainer,
2492 maintainer = relation(Maintainer,
2493 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2494 poolfile_id = self.tbl_source.c.file,
2495 poolfile = relation(PoolFile),
2496 fingerprint_id = self.tbl_source.c.sig_fpr,
2497 fingerprint = relation(Fingerprint),
2498 changedby_id = self.tbl_source.c.changedby,
2499 changedby = relation(Maintainer,
2500 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2501 srcfiles = relation(DSCFile,
2502 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2503 srcassociations = relation(SrcAssociation,
2504 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2506 mapper(SrcAssociation, self.tbl_src_associations,
2507 properties = dict(sa_id = self.tbl_src_associations.c.id,
2508 suite_id = self.tbl_src_associations.c.suite,
2509 suite = relation(Suite),
2510 source_id = self.tbl_src_associations.c.source,
2511 source = relation(DBSource)))
2513 mapper(SrcUploader, self.tbl_src_uploaders,
2514 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2515 source_id = self.tbl_src_uploaders.c.source,
2516 source = relation(DBSource,
2517 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2518 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2519 maintainer = relation(Maintainer,
2520 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2522 mapper(Suite, self.tbl_suite,
2523 properties = dict(suite_id = self.tbl_suite.c.id))
2525 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2526 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2527 suite = relation(Suite, backref='suitearchitectures'),
2528 arch_id = self.tbl_suite_architectures.c.architecture,
2529 architecture = relation(Architecture)))
2531 mapper(Uid, self.tbl_uid,
2532 properties = dict(uid_id = self.tbl_uid.c.id,
2533 fingerprint = relation(Fingerprint)))
2535 ## Connection functions
2536 def __createconn(self):
2537 from config import Config
2541 connstr = "postgres://%s" % cnf["DB::Host"]
2542 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2543 connstr += ":%s" % cnf["DB::Port"]
2544 connstr += "/%s" % cnf["DB::Name"]
2547 connstr = "postgres:///%s" % cnf["DB::Name"]
2548 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2549 connstr += "?port=%s" % cnf["DB::Port"]
2551 self.db_pg = create_engine(connstr, echo=self.debug)
2552 self.db_meta = MetaData()
2553 self.db_meta.bind = self.db_pg
2554 self.db_smaker = sessionmaker(bind=self.db_pg,
2558 self.__setuptables()
2559 self.__setupmappers()
2562 return self.db_smaker()
2564 __all__.append('DBConn')