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 inspect import getargspec
42 from sqlalchemy import create_engine, Table, MetaData, select
43 from sqlalchemy.orm import sessionmaker, mapper, relation
45 # Don't remove this, we re-export the exceptions to scripts which import us
46 from sqlalchemy.exc import *
47 from sqlalchemy.orm.exc import NoResultFound
49 # Only import Config until Queue stuff is changed to store its config
51 from config import Config
52 from singleton import Singleton
53 from textutils import fix_maintainer
55 ################################################################################
57 __all__ = ['IntegrityError', 'SQLAlchemyError']
59 ################################################################################
61 def session_wrapper(fn):
62 def wrapped(*args, **kwargs):
63 private_transaction = False
64 session = kwargs.get('session')
66 # No session specified as last argument or in kwargs, create one.
67 if session is None and len(args) == len(getargspec(fn)[0]) - 1:
68 private_transaction = True
69 kwargs['session'] = DBConn().session()
72 return fn(*args, **kwargs)
74 if private_transaction:
75 # We created a session; close it.
76 kwargs['session'].close()
80 ################################################################################
82 class Architecture(object):
83 def __init__(self, *args, **kwargs):
86 def __eq__(self, val):
87 if isinstance(val, str):
88 return (self.arch_string== val)
89 # This signals to use the normal comparison operator
92 def __ne__(self, val):
93 if isinstance(val, str):
94 return (self.arch_string != val)
95 # This signals to use the normal comparison operator
99 return '<Architecture %s>' % self.arch_string
101 __all__.append('Architecture')
104 def get_architecture(architecture, session=None):
106 Returns database id for given C{architecture}.
108 @type architecture: string
109 @param architecture: The name of the architecture
111 @type session: Session
112 @param session: Optional SQLA session object (a temporary one will be
113 generated if not supplied)
116 @return: Architecture object for the given arch (None if not present)
119 q = session.query(Architecture).filter_by(arch_string=architecture)
123 except NoResultFound:
126 __all__.append('get_architecture')
129 def get_architecture_suites(architecture, session=None):
131 Returns list of Suite objects for given C{architecture} name
134 @param source: Architecture name to search for
136 @type session: Session
137 @param session: Optional SQL session object (a temporary one will be
138 generated if not supplied)
141 @return: list of Suite objects for the given name (may be empty)
144 q = session.query(Suite)
145 q = q.join(SuiteArchitecture)
146 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
152 __all__.append('get_architecture_suites')
154 ################################################################################
156 class Archive(object):
157 def __init__(self, *args, **kwargs):
161 return '<Archive %s>' % self.archive_name
163 __all__.append('Archive')
166 def get_archive(archive, session=None):
168 returns database id for given c{archive}.
170 @type archive: string
171 @param archive: the name of the arhive
173 @type session: Session
174 @param session: Optional SQLA session object (a temporary one will be
175 generated if not supplied)
178 @return: Archive object for the given name (None if not present)
181 archive = archive.lower()
183 q = session.query(Archive).filter_by(archive_name=archive)
187 except NoResultFound:
190 __all__.append('get_archive')
192 ################################################################################
194 class BinAssociation(object):
195 def __init__(self, *args, **kwargs):
199 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
201 __all__.append('BinAssociation')
203 ################################################################################
205 class DBBinary(object):
206 def __init__(self, *args, **kwargs):
210 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
212 __all__.append('DBBinary')
215 def get_suites_binary_in(package, session=None):
217 Returns list of Suite objects which given C{package} name is in
220 @param source: DBBinary package name to search for
223 @return: list of Suite objects for the given package
226 ret = session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
230 __all__.append('get_suites_binary_in')
233 def get_binary_from_id(id, session=None):
235 Returns DBBinary object for given C{id}
238 @param id: Id of the required binary
240 @type session: Session
241 @param session: Optional SQLA session object (a temporary one will be
242 generated if not supplied)
245 @return: DBBinary object for the given binary (None if not present)
248 q = session.query(DBBinary).filter_by(binary_id=id)
252 except NoResultFound:
255 __all__.append('get_binary_from_id')
258 def get_binaries_from_name(package, version=None, architecture=None, session=None):
260 Returns list of DBBinary objects for given C{package} name
263 @param package: DBBinary package name to search for
265 @type version: str or None
266 @param version: Version to search for (or None)
268 @type package: str, list or None
269 @param package: Architectures to limit to (or None if no limit)
271 @type session: Session
272 @param session: Optional SQL session object (a temporary one will be
273 generated if not supplied)
276 @return: list of DBBinary objects for the given name (may be empty)
279 q = session.query(DBBinary).filter_by(package=package)
281 if version is not None:
282 q = q.filter_by(version=version)
284 if architecture is not None:
285 if not isinstance(architecture, list):
286 architecture = [architecture]
287 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
293 __all__.append('get_binaries_from_name')
296 def get_binaries_from_source_id(source_id, session=None):
298 Returns list of DBBinary objects for given C{source_id}
301 @param source_id: source_id to search for
303 @type session: Session
304 @param session: Optional SQL session object (a temporary one will be
305 generated if not supplied)
308 @return: list of DBBinary objects for the given name (may be empty)
311 ret = session.query(DBBinary).filter_by(source_id=source_id).all()
315 __all__.append('get_binaries_from_source_id')
318 def get_binary_from_name_suite(package, suitename, session=None):
319 ### For dak examine-package
320 ### XXX: Doesn't use object API yet
322 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
323 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
324 WHERE b.package=:package
326 AND fi.location = l.id
327 AND l.component = c.id
330 AND su.suite_name=:suitename
331 ORDER BY b.version DESC"""
333 ret = session.execute(sql, {'package': package, 'suitename': suitename})
337 __all__.append('get_binary_from_name_suite')
340 def get_binary_components(package, suitename, arch, session=None):
341 # Check for packages that have moved from one component to another
342 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
343 WHERE b.package=:package AND s.suite_name=:suitename
344 AND (a.arch_string = :arch OR a.arch_string = 'all')
345 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
346 AND f.location = l.id
347 AND l.component = c.id
350 vals = {'package': package, 'suitename': suitename, 'arch': arch}
352 ret = session.execute(query, vals)
356 __all__.append('get_binary_components')
358 ################################################################################
360 class Component(object):
361 def __init__(self, *args, **kwargs):
364 def __eq__(self, val):
365 if isinstance(val, str):
366 return (self.component_name == val)
367 # This signals to use the normal comparison operator
368 return NotImplemented
370 def __ne__(self, val):
371 if isinstance(val, str):
372 return (self.component_name != val)
373 # This signals to use the normal comparison operator
374 return NotImplemented
377 return '<Component %s>' % self.component_name
380 __all__.append('Component')
383 def get_component(component, session=None):
385 Returns database id for given C{component}.
387 @type component: string
388 @param component: The name of the override type
391 @return: the database id for the given component
394 component = component.lower()
396 q = session.query(Component).filter_by(component_name=component)
400 except NoResultFound:
403 __all__.append('get_component')
405 ################################################################################
407 class DBConfig(object):
408 def __init__(self, *args, **kwargs):
412 return '<DBConfig %s>' % self.name
414 __all__.append('DBConfig')
416 ################################################################################
418 class ContentFilename(object):
419 def __init__(self, *args, **kwargs):
423 return '<ContentFilename %s>' % self.filename
425 __all__.append('ContentFilename')
427 def get_or_set_contents_file_id(filename, session=None):
429 Returns database id for given filename.
431 If no matching file is found, a row is inserted.
433 @type filename: string
434 @param filename: The filename
435 @type session: SQLAlchemy
436 @param session: Optional SQL session object (a temporary one will be
437 generated if not supplied). If not passed, a commit will be performed at
438 the end of the function, otherwise the caller is responsible for commiting.
441 @return: the database id for the given component
445 session = DBConn().session()
448 q = session.query(ContentFilename).filter_by(filename=filename)
451 ret = q.one().cafilename_id
452 except NoResultFound:
453 cf = ContentFilename()
454 cf.filename = filename
460 ret = cf.cafilename_id
467 __all__.append('get_or_set_contents_file_id')
470 def get_contents(suite, overridetype, section=None, session=None):
472 Returns contents for a suite / overridetype combination, limiting
473 to a section if not None.
476 @param suite: Suite object
478 @type overridetype: OverrideType
479 @param overridetype: OverrideType object
481 @type section: Section
482 @param section: Optional section object to limit results to
484 @type session: SQLAlchemy
485 @param session: Optional SQL session object (a temporary one will be
486 generated if not supplied)
489 @return: ResultsProxy object set up to return tuples of (filename, section,
493 # find me all of the contents for a given suite
494 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
498 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
499 JOIN content_file_names n ON (c.filename=n.id)
500 JOIN binaries b ON (b.id=c.binary_pkg)
501 JOIN override o ON (o.package=b.package)
502 JOIN section s ON (s.id=o.section)
503 WHERE o.suite = :suiteid AND o.type = :overridetypeid
504 AND b.type=:overridetypename"""
506 vals = {'suiteid': suite.suite_id,
507 'overridetypeid': overridetype.overridetype_id,
508 'overridetypename': overridetype.overridetype}
510 if section is not None:
511 contents_q += " AND s.id = :sectionid"
512 vals['sectionid'] = section.section_id
514 contents_q += " ORDER BY fn"
516 ret = session.execute(contents_q, vals)
520 __all__.append('get_contents')
522 ################################################################################
524 class ContentFilepath(object):
525 def __init__(self, *args, **kwargs):
529 return '<ContentFilepath %s>' % self.filepath
531 __all__.append('ContentFilepath')
533 def get_or_set_contents_path_id(filepath, session=None):
535 Returns database id for given path.
537 If no matching file is found, a row is inserted.
539 @type filename: string
540 @param filename: The filepath
541 @type session: SQLAlchemy
542 @param session: Optional SQL session object (a temporary one will be
543 generated if not supplied). If not passed, a commit will be performed at
544 the end of the function, otherwise the caller is responsible for commiting.
547 @return: the database id for the given path
551 session = DBConn().session()
554 q = session.query(ContentFilepath).filter_by(filepath=filepath)
557 ret = q.one().cafilepath_id
558 except NoResultFound:
559 cf = ContentFilepath()
560 cf.filepath = filepath
566 ret = cf.cafilepath_id
573 __all__.append('get_or_set_contents_path_id')
575 ################################################################################
577 class ContentAssociation(object):
578 def __init__(self, *args, **kwargs):
582 return '<ContentAssociation %s>' % self.ca_id
584 __all__.append('ContentAssociation')
586 def insert_content_paths(binary_id, fullpaths, session=None):
588 Make sure given path is associated with given binary id
591 @param binary_id: the id of the binary
592 @type fullpaths: list
593 @param fullpaths: the list of paths of the file being associated with the binary
594 @type session: SQLAlchemy session
595 @param session: Optional SQLAlchemy session. If this is passed, the caller
596 is responsible for ensuring a transaction has begun and committing the
597 results or rolling back based on the result code. If not passed, a commit
598 will be performed at the end of the function, otherwise the caller is
599 responsible for commiting.
601 @return: True upon success
606 session = DBConn().session()
612 for fullpath in fullpaths:
613 # Get the necessary IDs ...
614 (path, file) = os.path.split(fullpath)
616 filepath_id = get_or_set_contents_path_id(path, session)
617 filename_id = get_or_set_contents_file_id(file, session)
619 pathcache[fullpath] = (filepath_id, filename_id)
621 for fullpath, dat in pathcache.items():
622 ca = ContentAssociation()
623 ca.binary_id = binary_id
624 ca.filepath_id = dat[0]
625 ca.filename_id = dat[1]
628 # Only commit if we set up the session ourself
638 traceback.print_exc()
640 # Only rollback if we set up the session ourself
647 __all__.append('insert_content_paths')
649 ################################################################################
651 class DSCFile(object):
652 def __init__(self, *args, **kwargs):
656 return '<DSCFile %s>' % self.dscfile_id
658 __all__.append('DSCFile')
661 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
663 Returns a list of DSCFiles which may be empty
665 @type dscfile_id: int (optional)
666 @param dscfile_id: the dscfile_id of the DSCFiles to find
668 @type source_id: int (optional)
669 @param source_id: the source id related to the DSCFiles to find
671 @type poolfile_id: int (optional)
672 @param poolfile_id: the poolfile id related to the DSCFiles to find
675 @return: Possibly empty list of DSCFiles
678 q = session.query(DSCFile)
680 if dscfile_id is not None:
681 q = q.filter_by(dscfile_id=dscfile_id)
683 if source_id is not None:
684 q = q.filter_by(source_id=source_id)
686 if poolfile_id is not None:
687 q = q.filter_by(poolfile_id=poolfile_id)
693 __all__.append('get_dscfiles')
695 ################################################################################
697 class PoolFile(object):
698 def __init__(self, *args, **kwargs):
702 return '<PoolFile %s>' % self.filename
704 __all__.append('PoolFile')
707 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
710 (ValidFileFound [boolean or None], PoolFile object or None)
712 @type filename: string
713 @param filename: the filename of the file to check against the DB
716 @param filesize: the size of the file to check against the DB
719 @param md5sum: the md5sum of the file to check against the DB
721 @type location_id: int
722 @param location_id: the id of the location to look in
725 @return: Tuple of length 2.
726 If more than one file found with that name:
728 If valid pool file found: (True, PoolFile object)
729 If valid pool file not found:
730 (False, None) if no file found
731 (False, PoolFile object) if file found with size/md5sum mismatch
734 q = session.query(PoolFile).filter_by(filename=filename)
735 q = q.join(Location).filter_by(location_id=location_id)
745 if obj.md5sum != md5sum or obj.filesize != filesize:
753 __all__.append('check_poolfile')
756 def get_poolfile_by_id(file_id, session=None):
758 Returns a PoolFile objects or None for the given id
761 @param file_id: the id of the file to look for
763 @rtype: PoolFile or None
764 @return: either the PoolFile object or None
767 q = session.query(PoolFile).filter_by(file_id=file_id)
771 except NoResultFound:
774 __all__.append('get_poolfile_by_id')
778 def get_poolfile_by_name(filename, location_id=None, session=None):
780 Returns an array of PoolFile objects for the given filename and
781 (optionally) location_id
783 @type filename: string
784 @param filename: the filename of the file to check against the DB
786 @type location_id: int
787 @param location_id: the id of the location to look in (optional)
790 @return: array of PoolFile objects
793 q = session.query(PoolFile).filter_by(filename=filename)
795 if location_id is not None:
796 q = q.join(Location).filter_by(location_id=location_id)
802 __all__.append('get_poolfile_by_name')
805 def get_poolfile_like_name(filename, session=None):
807 Returns an array of PoolFile objects which are like the given name
809 @type filename: string
810 @param filename: the filename of the file to check against the DB
813 @return: array of PoolFile objects
816 # TODO: There must be a way of properly using bind parameters with %FOO%
817 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
823 __all__.append('get_poolfile_like_name')
825 ################################################################################
827 class Fingerprint(object):
828 def __init__(self, *args, **kwargs):
832 return '<Fingerprint %s>' % self.fingerprint
834 __all__.append('Fingerprint')
836 def get_or_set_fingerprint(fpr, session=None):
838 Returns Fingerprint object for given fpr.
840 If no matching fpr is found, a row is inserted.
843 @param fpr: The fpr to find / add
845 @type session: SQLAlchemy
846 @param session: Optional SQL session object (a temporary one will be
847 generated if not supplied). If not passed, a commit will be performed at
848 the end of the function, otherwise the caller is responsible for commiting.
849 A flush will be performed either way.
852 @return: the Fingerprint object for the given fpr
856 session = DBConn().session()
859 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
863 except NoResultFound:
864 fingerprint = Fingerprint()
865 fingerprint.fingerprint = fpr
866 session.add(fingerprint)
878 __all__.append('get_or_set_fingerprint')
880 ################################################################################
882 class Keyring(object):
883 def __init__(self, *args, **kwargs):
887 return '<Keyring %s>' % self.keyring_name
889 __all__.append('Keyring')
891 def get_or_set_keyring(keyring, session=None):
893 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
894 and return the new Keyring
895 If C{keyring} already has an entry, simply return the existing Keyring
897 @type keyring: string
898 @param keyring: the keyring name
901 @return: the Keyring object for this keyring
906 session = DBConn().session()
910 obj = session.query(Keyring).filter_by(keyring_name=keyring).first()
913 obj = Keyring(keyring_name=keyring)
925 __all__.append('get_or_set_keyring')
927 ################################################################################
929 class Location(object):
930 def __init__(self, *args, **kwargs):
934 return '<Location %s (%s)>' % (self.path, self.location_id)
936 __all__.append('Location')
939 def get_location(location, component=None, archive=None, session=None):
941 Returns Location object for the given combination of location, component
944 @type location: string
945 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
947 @type component: string
948 @param component: the component name (if None, no restriction applied)
950 @type archive: string
951 @param archive_id: the archive name (if None, no restriction applied)
953 @rtype: Location / None
954 @return: Either a Location object or None if one can't be found
957 q = session.query(Location).filter_by(path=location)
959 if archive is not None:
960 q = q.join(Archive).filter_by(archive_name=archive)
962 if component is not None:
963 q = q.join(Component).filter_by(component_name=component)
967 except NoResultFound:
970 __all__.append('get_location')
972 ################################################################################
974 class Maintainer(object):
975 def __init__(self, *args, **kwargs):
979 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
981 def get_split_maintainer(self):
982 if not hasattr(self, 'name') or self.name is None:
983 return ('', '', '', '')
985 return fix_maintainer(self.name.strip())
987 __all__.append('Maintainer')
989 def get_or_set_maintainer(name, session=None):
991 Returns Maintainer object for given maintainer name.
993 If no matching maintainer name is found, a row is inserted.
996 @param name: The maintainer name to add
998 @type session: SQLAlchemy
999 @param session: Optional SQL session object (a temporary one will be
1000 generated if not supplied). If not passed, a commit will be performed at
1001 the end of the function, otherwise the caller is responsible for commiting.
1002 A flush will be performed either way.
1005 @return: the Maintainer object for the given maintainer
1007 privatetrans = False
1009 session = DBConn().session()
1012 q = session.query(Maintainer).filter_by(name=name)
1015 except NoResultFound:
1016 maintainer = Maintainer()
1017 maintainer.name = name
1018 session.add(maintainer)
1030 __all__.append('get_or_set_maintainer')
1032 def get_maintainer(maintainer_id, session=None):
1034 Return the name of the maintainer behind C{maintainer_id} or None if that
1035 maintainer_id is invalid.
1037 @type maintainer_id: int
1038 @param maintainer_id: the id of the maintainer
1041 @return: the Maintainer with this C{maintainer_id}
1044 privatetrans = False
1046 session = DBConn().session()
1050 return session.query(Maintainer).get(maintainer_id)
1055 __all__.append('get_maintainer')
1057 ################################################################################
1059 class NewComment(object):
1060 def __init__(self, *args, **kwargs):
1064 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1066 __all__.append('NewComment')
1069 def has_new_comment(package, version, session=None):
1071 Returns true if the given combination of C{package}, C{version} has a comment.
1073 @type package: string
1074 @param package: name of the package
1076 @type version: string
1077 @param version: package version
1079 @type session: Session
1080 @param session: Optional SQLA session object (a temporary one will be
1081 generated if not supplied)
1087 q = session.query(NewComment)
1088 q = q.filter_by(package=package)
1089 q = q.filter_by(version=version)
1095 __all__.append('has_new_comment')
1098 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1100 Returns (possibly empty) list of NewComment objects for the given
1103 @type package: string (optional)
1104 @param package: name of the package
1106 @type version: string (optional)
1107 @param version: package version
1109 @type comment_id: int (optional)
1110 @param comment_id: An id of a comment
1112 @type session: Session
1113 @param session: Optional SQLA session object (a temporary one will be
1114 generated if not supplied)
1117 @return: A (possibly empty) list of NewComment objects will be returned
1120 q = session.query(NewComment)
1121 if package is not None: q = q.filter_by(package=package)
1122 if version is not None: q = q.filter_by(version=version)
1123 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1129 __all__.append('get_new_comments')
1131 ################################################################################
1133 class Override(object):
1134 def __init__(self, *args, **kwargs):
1138 return '<Override %s (%s)>' % (self.package, self.suite_id)
1140 __all__.append('Override')
1143 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1145 Returns Override object for the given parameters
1147 @type package: string
1148 @param package: The name of the package
1150 @type suite: string, list or None
1151 @param suite: The name of the suite (or suites if a list) to limit to. If
1152 None, don't limit. Defaults to None.
1154 @type component: string, list or None
1155 @param component: The name of the component (or components if a list) to
1156 limit to. If None, don't limit. Defaults to None.
1158 @type overridetype: string, list or None
1159 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1160 limit to. If None, don't limit. Defaults to None.
1162 @type session: Session
1163 @param session: Optional SQLA session object (a temporary one will be
1164 generated if not supplied)
1167 @return: A (possibly empty) list of Override objects will be returned
1170 q = session.query(Override)
1171 q = q.filter_by(package=package)
1173 if suite is not None:
1174 if not isinstance(suite, list): suite = [suite]
1175 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1177 if component is not None:
1178 if not isinstance(component, list): component = [component]
1179 q = q.join(Component).filter(Component.component_name.in_(component))
1181 if overridetype is not None:
1182 if not isinstance(overridetype, list): overridetype = [overridetype]
1183 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1189 __all__.append('get_override')
1192 ################################################################################
1194 class OverrideType(object):
1195 def __init__(self, *args, **kwargs):
1199 return '<OverrideType %s>' % self.overridetype
1201 __all__.append('OverrideType')
1204 def get_override_type(override_type, session=None):
1206 Returns OverrideType object for given C{override type}.
1208 @type override_type: string
1209 @param override_type: The name of the override type
1211 @type session: Session
1212 @param session: Optional SQLA session object (a temporary one will be
1213 generated if not supplied)
1216 @return: the database id for the given override type
1219 q = session.query(OverrideType).filter_by(overridetype=override_type)
1223 except NoResultFound:
1228 __all__.append('get_override_type')
1230 ################################################################################
1232 class PendingContentAssociation(object):
1233 def __init__(self, *args, **kwargs):
1237 return '<PendingContentAssociation %s>' % self.pca_id
1239 __all__.append('PendingContentAssociation')
1241 def insert_pending_content_paths(package, fullpaths, session=None):
1243 Make sure given paths are temporarily associated with given
1247 @param package: the package to associate with should have been read in from the binary control file
1248 @type fullpaths: list
1249 @param fullpaths: the list of paths of the file being associated with the binary
1250 @type session: SQLAlchemy session
1251 @param session: Optional SQLAlchemy session. If this is passed, the caller
1252 is responsible for ensuring a transaction has begun and committing the
1253 results or rolling back based on the result code. If not passed, a commit
1254 will be performed at the end of the function
1256 @return: True upon success, False if there is a problem
1259 privatetrans = False
1262 session = DBConn().session()
1266 arch = get_architecture(package['Architecture'], session)
1267 arch_id = arch.arch_id
1269 # Remove any already existing recorded files for this package
1270 q = session.query(PendingContentAssociation)
1271 q = q.filter_by(package=package['Package'])
1272 q = q.filter_by(version=package['Version'])
1273 q = q.filter_by(architecture=arch_id)
1278 for fullpath in fullpaths:
1279 (path, file) = os.path.split(fullpath)
1281 if path.startswith( "./" ):
1284 filepath_id = get_or_set_contents_path_id(path, session)
1285 filename_id = get_or_set_contents_file_id(file, session)
1287 pathcache[fullpath] = (filepath_id, filename_id)
1289 for fullpath, dat in pathcache.items():
1290 pca = PendingContentAssociation()
1291 pca.package = package['Package']
1292 pca.version = package['Version']
1293 pca.filepath_id = dat[0]
1294 pca.filename_id = dat[1]
1295 pca.architecture = arch_id
1298 # Only commit if we set up the session ourself
1306 except Exception, e:
1307 traceback.print_exc()
1309 # Only rollback if we set up the session ourself
1316 __all__.append('insert_pending_content_paths')
1318 ################################################################################
1320 class Priority(object):
1321 def __init__(self, *args, **kwargs):
1324 def __eq__(self, val):
1325 if isinstance(val, str):
1326 return (self.priority == val)
1327 # This signals to use the normal comparison operator
1328 return NotImplemented
1330 def __ne__(self, val):
1331 if isinstance(val, str):
1332 return (self.priority != val)
1333 # This signals to use the normal comparison operator
1334 return NotImplemented
1337 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1339 __all__.append('Priority')
1342 def get_priority(priority, session=None):
1344 Returns Priority object for given C{priority name}.
1346 @type priority: string
1347 @param priority: The name of the priority
1349 @type session: Session
1350 @param session: Optional SQLA session object (a temporary one will be
1351 generated if not supplied)
1354 @return: Priority object for the given priority
1357 q = session.query(Priority).filter_by(priority=priority)
1361 except NoResultFound:
1364 __all__.append('get_priority')
1367 def get_priorities(session=None):
1369 Returns dictionary of priority names -> id mappings
1371 @type session: Session
1372 @param session: Optional SQL session object (a temporary one will be
1373 generated if not supplied)
1376 @return: dictionary of priority names -> id mappings
1380 q = session.query(Priority)
1382 ret[x.priority] = x.priority_id
1386 __all__.append('get_priorities')
1388 ################################################################################
1390 class Queue(object):
1391 def __init__(self, *args, **kwargs):
1395 return '<Queue %s>' % self.queue_name
1397 def autobuild_upload(self, changes, srcpath, session=None):
1399 Update queue_build database table used for incoming autobuild support.
1401 @type changes: Changes
1402 @param changes: changes object for the upload to process
1404 @type srcpath: string
1405 @param srcpath: path for the queue file entries/link destinations
1407 @type session: SQLAlchemy session
1408 @param session: Optional SQLAlchemy session. If this is passed, the
1409 caller is responsible for ensuring a transaction has begun and
1410 committing the results or rolling back based on the result code. If
1411 not passed, a commit will be performed at the end of the function,
1412 otherwise the caller is responsible for commiting.
1414 @rtype: NoneType or string
1415 @return: None if the operation failed, a string describing the error if not
1418 privatetrans = False
1420 session = DBConn().session()
1423 # TODO: Remove by moving queue config into the database
1426 for suitename in changes.changes["distribution"].keys():
1427 # TODO: Move into database as:
1428 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1429 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1430 # This also gets rid of the SecurityQueueBuild hack below
1431 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1435 s = get_suite(suitename, session)
1437 return "INTERNAL ERROR: Could not find suite %s" % suitename
1439 # TODO: Get from database as above
1440 dest_dir = conf["Dir::QueueBuild"]
1442 # TODO: Move into database as above
1443 if conf.FindB("Dinstall::SecurityQueueBuild"):
1444 dest_dir = os.path.join(dest_dir, suitename)
1446 for file_entry in changes.files.keys():
1447 src = os.path.join(srcpath, file_entry)
1448 dest = os.path.join(dest_dir, file_entry)
1450 # TODO: Move into database as above
1451 if conf.FindB("Dinstall::SecurityQueueBuild"):
1452 # Copy it since the original won't be readable by www-data
1453 utils.copy(src, dest)
1455 # Create a symlink to it
1456 os.symlink(src, dest)
1459 qb.suite_id = s.suite_id
1460 qb.queue_id = self.queue_id
1466 # If the .orig.tar.gz is in the pool, create a symlink to
1467 # it (if one doesn't already exist)
1468 if changes.orig_tar_id:
1469 # Determine the .orig.tar.gz file name
1470 for dsc_file in changes.dsc_files.keys():
1471 if dsc_file.endswith(".orig.tar.gz"):
1474 dest = os.path.join(dest_dir, filename)
1476 # If it doesn't exist, create a symlink
1477 if not os.path.exists(dest):
1478 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1479 {'id': changes.orig_tar_id})
1482 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1484 src = os.path.join(res[0], res[1])
1485 os.symlink(src, dest)
1487 # Add it to the list of packages for later processing by apt-ftparchive
1489 qb.suite_id = s.suite_id
1490 qb.queue_id = self.queue_id
1495 # If it does, update things to ensure it's not removed prematurely
1497 qb = get_queue_build(dest, s.suite_id, session)
1509 __all__.append('Queue')
1512 def get_queue(queuename, session=None):
1514 Returns Queue object for given C{queue name}.
1516 @type queuename: string
1517 @param queuename: The name of the queue
1519 @type session: Session
1520 @param session: Optional SQLA session object (a temporary one will be
1521 generated if not supplied)
1524 @return: Queue object for the given queue
1527 q = session.query(Queue).filter_by(queue_name=queuename)
1531 except NoResultFound:
1534 __all__.append('get_queue')
1536 ################################################################################
1538 class QueueBuild(object):
1539 def __init__(self, *args, **kwargs):
1543 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1545 __all__.append('QueueBuild')
1548 def get_queue_build(filename, suite, session=None):
1550 Returns QueueBuild object for given C{filename} and C{suite}.
1552 @type filename: string
1553 @param filename: The name of the file
1555 @type suiteid: int or str
1556 @param suiteid: Suite name or ID
1558 @type session: Session
1559 @param session: Optional SQLA session object (a temporary one will be
1560 generated if not supplied)
1563 @return: Queue object for the given queue
1566 if isinstance(suite, int):
1567 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1569 q = session.query(QueueBuild).filter_by(filename=filename)
1570 q = q.join(Suite).filter_by(suite_name=suite)
1574 except NoResultFound:
1577 __all__.append('get_queue_build')
1579 ################################################################################
1581 class Section(object):
1582 def __init__(self, *args, **kwargs):
1585 def __eq__(self, val):
1586 if isinstance(val, str):
1587 return (self.section == val)
1588 # This signals to use the normal comparison operator
1589 return NotImplemented
1591 def __ne__(self, val):
1592 if isinstance(val, str):
1593 return (self.section != val)
1594 # This signals to use the normal comparison operator
1595 return NotImplemented
1598 return '<Section %s>' % self.section
1600 __all__.append('Section')
1603 def get_section(section, session=None):
1605 Returns Section object for given C{section name}.
1607 @type section: string
1608 @param section: The name of the section
1610 @type session: Session
1611 @param session: Optional SQLA session object (a temporary one will be
1612 generated if not supplied)
1615 @return: Section object for the given section name
1618 q = session.query(Section).filter_by(section=section)
1622 except NoResultFound:
1625 __all__.append('get_section')
1628 def get_sections(session=None):
1630 Returns dictionary of section names -> id mappings
1632 @type session: Session
1633 @param session: Optional SQL session object (a temporary one will be
1634 generated if not supplied)
1637 @return: dictionary of section names -> id mappings
1641 q = session.query(Section)
1643 ret[x.section] = x.section_id
1647 __all__.append('get_sections')
1649 ################################################################################
1651 class DBSource(object):
1652 def __init__(self, *args, **kwargs):
1656 return '<DBSource %s (%s)>' % (self.source, self.version)
1658 __all__.append('DBSource')
1661 def source_exists(source, source_version, suites = ["any"], session=None):
1663 Ensure that source exists somewhere in the archive for the binary
1664 upload being processed.
1665 1. exact match => 1.0-3
1666 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1668 @type package: string
1669 @param package: package source name
1671 @type source_version: string
1672 @param source_version: expected source version
1675 @param suites: list of suites to check in, default I{any}
1677 @type session: Session
1678 @param session: Optional SQLA session object (a temporary one will be
1679 generated if not supplied)
1682 @return: returns 1 if a source with expected version is found, otherwise 0
1689 for suite in suites:
1690 q = session.query(DBSource).filter_by(source=source)
1692 # source must exist in suite X, or in some other suite that's
1693 # mapped to X, recursively... silent-maps are counted too,
1694 # unreleased-maps aren't.
1695 maps = cnf.ValueList("SuiteMappings")[:]
1697 maps = [ m.split() for m in maps ]
1698 maps = [ (x[1], x[2]) for x in maps
1699 if x[0] == "map" or x[0] == "silent-map" ]
1702 if x[1] in s and x[0] not in s:
1705 q = q.join(SrcAssociation).join(Suite)
1706 q = q.filter(Suite.suite_name.in_(s))
1708 # Reduce the query results to a list of version numbers
1709 ql = [ j.version for j in q.all() ]
1712 if source_version in ql:
1716 from daklib.regexes import re_bin_only_nmu
1717 orig_source_version = re_bin_only_nmu.sub('', source_version)
1718 if orig_source_version in ql:
1721 # No source found so return not ok
1726 __all__.append('source_exists')
1729 def get_suites_source_in(source, session=None):
1731 Returns list of Suite objects which given C{source} name is in
1734 @param source: DBSource package name to search for
1737 @return: list of Suite objects for the given source
1740 ret = session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1744 __all__.append('get_suites_source_in')
1747 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1749 Returns list of DBSource objects for given C{source} name and other parameters
1752 @param source: DBSource package name to search for
1754 @type source: str or None
1755 @param source: DBSource version name to search for or None if not applicable
1757 @type dm_upload_allowed: bool
1758 @param dm_upload_allowed: If None, no effect. If True or False, only
1759 return packages with that dm_upload_allowed setting
1761 @type session: Session
1762 @param session: Optional SQL session object (a temporary one will be
1763 generated if not supplied)
1766 @return: list of DBSource objects for the given name (may be empty)
1769 q = session.query(DBSource).filter_by(source=source)
1771 if version is not None:
1772 q = q.filter_by(version=version)
1774 if dm_upload_allowed is not None:
1775 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1781 __all__.append('get_sources_from_name')
1784 def get_source_in_suite(source, suite, session=None):
1786 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1788 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1789 - B{suite} - a suite name, eg. I{unstable}
1791 @type source: string
1792 @param source: source package name
1795 @param suite: the suite name
1798 @return: the version for I{source} in I{suite}
1802 q = session.query(SrcAssociation)
1803 q = q.join('source').filter_by(source=source)
1804 q = q.join('suite').filter_by(suite_name=suite)
1807 return q.one().source
1808 except NoResultFound:
1811 __all__.append('get_source_in_suite')
1813 ################################################################################
1815 class SrcAssociation(object):
1816 def __init__(self, *args, **kwargs):
1820 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1822 __all__.append('SrcAssociation')
1824 ################################################################################
1826 class SrcUploader(object):
1827 def __init__(self, *args, **kwargs):
1831 return '<SrcUploader %s>' % self.uploader_id
1833 __all__.append('SrcUploader')
1835 ################################################################################
1837 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1838 ('SuiteID', 'suite_id'),
1839 ('Version', 'version'),
1840 ('Origin', 'origin'),
1842 ('Description', 'description'),
1843 ('Untouchable', 'untouchable'),
1844 ('Announce', 'announce'),
1845 ('Codename', 'codename'),
1846 ('OverrideCodename', 'overridecodename'),
1847 ('ValidTime', 'validtime'),
1848 ('Priority', 'priority'),
1849 ('NotAutomatic', 'notautomatic'),
1850 ('CopyChanges', 'copychanges'),
1851 ('CopyDotDak', 'copydotdak'),
1852 ('CommentsDir', 'commentsdir'),
1853 ('OverrideSuite', 'overridesuite'),
1854 ('ChangelogBase', 'changelogbase')]
1857 class Suite(object):
1858 def __init__(self, *args, **kwargs):
1862 return '<Suite %s>' % self.suite_name
1864 def __eq__(self, val):
1865 if isinstance(val, str):
1866 return (self.suite_name == val)
1867 # This signals to use the normal comparison operator
1868 return NotImplemented
1870 def __ne__(self, val):
1871 if isinstance(val, str):
1872 return (self.suite_name != val)
1873 # This signals to use the normal comparison operator
1874 return NotImplemented
1878 for disp, field in SUITE_FIELDS:
1879 val = getattr(self, field, None)
1881 ret.append("%s: %s" % (disp, val))
1883 return "\n".join(ret)
1885 __all__.append('Suite')
1888 def get_suite_architecture(suite, architecture, session=None):
1890 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1894 @param suite: Suite name to search for
1896 @type architecture: str
1897 @param architecture: Architecture name to search for
1899 @type session: Session
1900 @param session: Optional SQL session object (a temporary one will be
1901 generated if not supplied)
1903 @rtype: SuiteArchitecture
1904 @return: the SuiteArchitecture object or None
1907 q = session.query(SuiteArchitecture)
1908 q = q.join(Architecture).filter_by(arch_string=architecture)
1909 q = q.join(Suite).filter_by(suite_name=suite)
1913 except NoResultFound:
1916 __all__.append('get_suite_architecture')
1919 def get_suite(suite, session=None):
1921 Returns Suite object for given C{suite name}.
1924 @param suite: The name of the suite
1926 @type session: Session
1927 @param session: Optional SQLA session object (a temporary one will be
1928 generated if not supplied)
1931 @return: Suite object for the requested suite name (None if not presenT)
1934 q = session.query(Suite).filter_by(suite_name=suite)
1938 except NoResultFound:
1941 __all__.append('get_suite')
1943 ################################################################################
1945 class SuiteArchitecture(object):
1946 def __init__(self, *args, **kwargs):
1950 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1952 __all__.append('SuiteArchitecture')
1955 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1957 Returns list of Architecture objects for given C{suite} name
1960 @param source: Suite name to search for
1962 @type skipsrc: boolean
1963 @param skipsrc: Whether to skip returning the 'source' architecture entry
1966 @type skipall: boolean
1967 @param skipall: Whether to skip returning the 'all' architecture entry
1970 @type session: Session
1971 @param session: Optional SQL session object (a temporary one will be
1972 generated if not supplied)
1975 @return: list of Architecture objects for the given name (may be empty)
1978 q = session.query(Architecture)
1979 q = q.join(SuiteArchitecture)
1980 q = q.join(Suite).filter_by(suite_name=suite)
1983 q = q.filter(Architecture.arch_string != 'source')
1986 q = q.filter(Architecture.arch_string != 'all')
1988 q = q.order_by('arch_string')
1994 __all__.append('get_suite_architectures')
1996 ################################################################################
1999 def __init__(self, *args, **kwargs):
2002 def __eq__(self, val):
2003 if isinstance(val, str):
2004 return (self.uid == val)
2005 # This signals to use the normal comparison operator
2006 return NotImplemented
2008 def __ne__(self, val):
2009 if isinstance(val, str):
2010 return (self.uid != val)
2011 # This signals to use the normal comparison operator
2012 return NotImplemented
2015 return '<Uid %s (%s)>' % (self.uid, self.name)
2017 __all__.append('Uid')
2019 def add_database_user(uidname, session=None):
2021 Adds a database user
2023 @type uidname: string
2024 @param uidname: The uid of the user to add
2026 @type session: SQLAlchemy
2027 @param session: Optional SQL session object (a temporary one will be
2028 generated if not supplied). If not passed, a commit will be performed at
2029 the end of the function, otherwise the caller is responsible for commiting.
2032 @return: the uid object for the given uidname
2035 privatetrans = False
2037 session = DBConn().session()
2040 session.execute("CREATE USER :uid", {'uid': uidname})
2046 __all__.append('add_database_user')
2048 def get_or_set_uid(uidname, session=None):
2050 Returns uid object for given uidname.
2052 If no matching uidname is found, a row is inserted.
2054 @type uidname: string
2055 @param uidname: The uid to add
2057 @type session: SQLAlchemy
2058 @param session: Optional SQL session object (a temporary one will be
2059 generated if not supplied). If not passed, a commit will be performed at
2060 the end of the function, otherwise the caller is responsible for commiting.
2063 @return: the uid object for the given uidname
2066 privatetrans = False
2068 session = DBConn().session()
2071 q = session.query(Uid).filter_by(uid=uidname)
2075 except NoResultFound:
2090 __all__.append('get_or_set_uid')
2093 def get_uid_from_fingerprint(fpr, session=None):
2094 q = session.query(Uid)
2095 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2099 except NoResultFound:
2102 __all__.append('get_uid_from_fingerprint')
2104 ################################################################################
2106 class DBConn(Singleton):
2108 database module init.
2110 def __init__(self, *args, **kwargs):
2111 super(DBConn, self).__init__(*args, **kwargs)
2113 def _startup(self, *args, **kwargs):
2115 if kwargs.has_key('debug'):
2119 def __setuptables(self):
2120 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2121 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2122 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2123 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2124 self.tbl_component = Table('component', self.db_meta, autoload=True)
2125 self.tbl_config = Table('config', self.db_meta, autoload=True)
2126 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2127 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2128 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2129 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2130 self.tbl_files = Table('files', self.db_meta, autoload=True)
2131 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2132 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2133 self.tbl_location = Table('location', self.db_meta, autoload=True)
2134 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2135 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2136 self.tbl_override = Table('override', self.db_meta, autoload=True)
2137 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2138 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2139 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2140 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2141 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2142 self.tbl_section = Table('section', self.db_meta, autoload=True)
2143 self.tbl_source = Table('source', self.db_meta, autoload=True)
2144 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2145 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2146 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2147 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2148 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2150 def __setupmappers(self):
2151 mapper(Architecture, self.tbl_architecture,
2152 properties = dict(arch_id = self.tbl_architecture.c.id))
2154 mapper(Archive, self.tbl_archive,
2155 properties = dict(archive_id = self.tbl_archive.c.id,
2156 archive_name = self.tbl_archive.c.name))
2158 mapper(BinAssociation, self.tbl_bin_associations,
2159 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2160 suite_id = self.tbl_bin_associations.c.suite,
2161 suite = relation(Suite),
2162 binary_id = self.tbl_bin_associations.c.bin,
2163 binary = relation(DBBinary)))
2165 mapper(DBBinary, self.tbl_binaries,
2166 properties = dict(binary_id = self.tbl_binaries.c.id,
2167 package = self.tbl_binaries.c.package,
2168 version = self.tbl_binaries.c.version,
2169 maintainer_id = self.tbl_binaries.c.maintainer,
2170 maintainer = relation(Maintainer),
2171 source_id = self.tbl_binaries.c.source,
2172 source = relation(DBSource),
2173 arch_id = self.tbl_binaries.c.architecture,
2174 architecture = relation(Architecture),
2175 poolfile_id = self.tbl_binaries.c.file,
2176 poolfile = relation(PoolFile),
2177 binarytype = self.tbl_binaries.c.type,
2178 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2179 fingerprint = relation(Fingerprint),
2180 install_date = self.tbl_binaries.c.install_date,
2181 binassociations = relation(BinAssociation,
2182 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2184 mapper(Component, self.tbl_component,
2185 properties = dict(component_id = self.tbl_component.c.id,
2186 component_name = self.tbl_component.c.name))
2188 mapper(DBConfig, self.tbl_config,
2189 properties = dict(config_id = self.tbl_config.c.id))
2191 mapper(ContentAssociation, self.tbl_content_associations,
2192 properties = dict(ca_id = self.tbl_content_associations.c.id,
2193 filename_id = self.tbl_content_associations.c.filename,
2194 filename = relation(ContentFilename),
2195 filepath_id = self.tbl_content_associations.c.filepath,
2196 filepath = relation(ContentFilepath),
2197 binary_id = self.tbl_content_associations.c.binary_pkg,
2198 binary = relation(DBBinary)))
2201 mapper(ContentFilename, self.tbl_content_file_names,
2202 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2203 filename = self.tbl_content_file_names.c.file))
2205 mapper(ContentFilepath, self.tbl_content_file_paths,
2206 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2207 filepath = self.tbl_content_file_paths.c.path))
2209 mapper(DSCFile, self.tbl_dsc_files,
2210 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2211 source_id = self.tbl_dsc_files.c.source,
2212 source = relation(DBSource),
2213 poolfile_id = self.tbl_dsc_files.c.file,
2214 poolfile = relation(PoolFile)))
2216 mapper(PoolFile, self.tbl_files,
2217 properties = dict(file_id = self.tbl_files.c.id,
2218 filesize = self.tbl_files.c.size,
2219 location_id = self.tbl_files.c.location,
2220 location = relation(Location)))
2222 mapper(Fingerprint, self.tbl_fingerprint,
2223 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2224 uid_id = self.tbl_fingerprint.c.uid,
2225 uid = relation(Uid),
2226 keyring_id = self.tbl_fingerprint.c.keyring,
2227 keyring = relation(Keyring)))
2229 mapper(Keyring, self.tbl_keyrings,
2230 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2231 keyring_id = self.tbl_keyrings.c.id))
2233 mapper(Location, self.tbl_location,
2234 properties = dict(location_id = self.tbl_location.c.id,
2235 component_id = self.tbl_location.c.component,
2236 component = relation(Component),
2237 archive_id = self.tbl_location.c.archive,
2238 archive = relation(Archive),
2239 archive_type = self.tbl_location.c.type))
2241 mapper(Maintainer, self.tbl_maintainer,
2242 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2244 mapper(NewComment, self.tbl_new_comments,
2245 properties = dict(comment_id = self.tbl_new_comments.c.id))
2247 mapper(Override, self.tbl_override,
2248 properties = dict(suite_id = self.tbl_override.c.suite,
2249 suite = relation(Suite),
2250 component_id = self.tbl_override.c.component,
2251 component = relation(Component),
2252 priority_id = self.tbl_override.c.priority,
2253 priority = relation(Priority),
2254 section_id = self.tbl_override.c.section,
2255 section = relation(Section),
2256 overridetype_id = self.tbl_override.c.type,
2257 overridetype = relation(OverrideType)))
2259 mapper(OverrideType, self.tbl_override_type,
2260 properties = dict(overridetype = self.tbl_override_type.c.type,
2261 overridetype_id = self.tbl_override_type.c.id))
2263 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2264 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2265 filepath_id = self.tbl_pending_content_associations.c.filepath,
2266 filepath = relation(ContentFilepath),
2267 filename_id = self.tbl_pending_content_associations.c.filename,
2268 filename = relation(ContentFilename)))
2270 mapper(Priority, self.tbl_priority,
2271 properties = dict(priority_id = self.tbl_priority.c.id))
2273 mapper(Queue, self.tbl_queue,
2274 properties = dict(queue_id = self.tbl_queue.c.id))
2276 mapper(QueueBuild, self.tbl_queue_build,
2277 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2278 queue_id = self.tbl_queue_build.c.queue,
2279 queue = relation(Queue, backref='queuebuild')))
2281 mapper(Section, self.tbl_section,
2282 properties = dict(section_id = self.tbl_section.c.id))
2284 mapper(DBSource, self.tbl_source,
2285 properties = dict(source_id = self.tbl_source.c.id,
2286 version = self.tbl_source.c.version,
2287 maintainer_id = self.tbl_source.c.maintainer,
2288 maintainer = relation(Maintainer,
2289 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2290 poolfile_id = self.tbl_source.c.file,
2291 poolfile = relation(PoolFile),
2292 fingerprint_id = self.tbl_source.c.sig_fpr,
2293 fingerprint = relation(Fingerprint),
2294 changedby_id = self.tbl_source.c.changedby,
2295 changedby = relation(Maintainer,
2296 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2297 srcfiles = relation(DSCFile,
2298 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2299 srcassociations = relation(SrcAssociation,
2300 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2302 mapper(SrcAssociation, self.tbl_src_associations,
2303 properties = dict(sa_id = self.tbl_src_associations.c.id,
2304 suite_id = self.tbl_src_associations.c.suite,
2305 suite = relation(Suite),
2306 source_id = self.tbl_src_associations.c.source,
2307 source = relation(DBSource)))
2309 mapper(SrcUploader, self.tbl_src_uploaders,
2310 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2311 source_id = self.tbl_src_uploaders.c.source,
2312 source = relation(DBSource,
2313 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2314 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2315 maintainer = relation(Maintainer,
2316 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2318 mapper(Suite, self.tbl_suite,
2319 properties = dict(suite_id = self.tbl_suite.c.id))
2321 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2322 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2323 suite = relation(Suite, backref='suitearchitectures'),
2324 arch_id = self.tbl_suite_architectures.c.architecture,
2325 architecture = relation(Architecture)))
2327 mapper(Uid, self.tbl_uid,
2328 properties = dict(uid_id = self.tbl_uid.c.id,
2329 fingerprint = relation(Fingerprint)))
2331 ## Connection functions
2332 def __createconn(self):
2333 from config import Config
2337 connstr = "postgres://%s" % cnf["DB::Host"]
2338 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2339 connstr += ":%s" % cnf["DB::Port"]
2340 connstr += "/%s" % cnf["DB::Name"]
2343 connstr = "postgres:///%s" % cnf["DB::Name"]
2344 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2345 connstr += "?port=%s" % cnf["DB::Port"]
2347 self.db_pg = create_engine(connstr, echo=self.debug)
2348 self.db_meta = MetaData()
2349 self.db_meta.bind = self.db_pg
2350 self.db_smaker = sessionmaker(bind=self.db_pg,
2354 self.__setuptables()
2355 self.__setupmappers()
2358 return self.db_smaker()
2360 __all__.append('DBConn')