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):
63 Wrapper around common ".., session=None):" handling. If the wrapped
64 function is called without passing 'session', we create a local one
65 and destroy it when the function ends.
67 Also attaches a commit_or_flush method to the session; if we created a
68 local session, this is a synonym for session.commit(), otherwise it is a
69 synonym for session.flush().
72 def wrapped(*args, **kwargs):
73 private_transaction = False
75 # Find the session object
77 session = kwargs['session']
79 if len(args) <= len(getargspec(fn)[0]) - 1:
80 # No session specified as last argument or in kwargs
81 private_transaction = True
82 session = kwargs['session'] = DBConn().session()
84 # Session is last argument in args
87 if private_transaction:
88 session.commit_or_flush = session.commit
90 session.commit_or_flush = session.flush
93 return fn(*args, **kwargs)
95 if private_transaction:
96 # We created a session; close it.
99 wrapped.__doc__ = fn.__doc__
100 wrapped.func_name = fn.func_name
104 ################################################################################
106 class Architecture(object):
107 def __init__(self, *args, **kwargs):
110 def __eq__(self, val):
111 if isinstance(val, str):
112 return (self.arch_string== val)
113 # This signals to use the normal comparison operator
114 return NotImplemented
116 def __ne__(self, val):
117 if isinstance(val, str):
118 return (self.arch_string != val)
119 # This signals to use the normal comparison operator
120 return NotImplemented
123 return '<Architecture %s>' % self.arch_string
125 __all__.append('Architecture')
128 def get_architecture(architecture, session=None):
130 Returns database id for given C{architecture}.
132 @type architecture: string
133 @param architecture: The name of the architecture
135 @type session: Session
136 @param session: Optional SQLA session object (a temporary one will be
137 generated if not supplied)
140 @return: Architecture object for the given arch (None if not present)
143 q = session.query(Architecture).filter_by(arch_string=architecture)
147 except NoResultFound:
150 __all__.append('get_architecture')
153 def get_architecture_suites(architecture, session=None):
155 Returns list of Suite objects for given C{architecture} name
158 @param source: Architecture name to search for
160 @type session: Session
161 @param session: Optional SQL session object (a temporary one will be
162 generated if not supplied)
165 @return: list of Suite objects for the given name (may be empty)
168 q = session.query(Suite)
169 q = q.join(SuiteArchitecture)
170 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
176 __all__.append('get_architecture_suites')
178 ################################################################################
180 class Archive(object):
181 def __init__(self, *args, **kwargs):
185 return '<Archive %s>' % self.archive_name
187 __all__.append('Archive')
190 def get_archive(archive, session=None):
192 returns database id for given C{archive}.
194 @type archive: string
195 @param archive: the name of the arhive
197 @type session: Session
198 @param session: Optional SQLA session object (a temporary one will be
199 generated if not supplied)
202 @return: Archive object for the given name (None if not present)
205 archive = archive.lower()
207 q = session.query(Archive).filter_by(archive_name=archive)
211 except NoResultFound:
214 __all__.append('get_archive')
216 ################################################################################
218 class BinAssociation(object):
219 def __init__(self, *args, **kwargs):
223 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
225 __all__.append('BinAssociation')
227 ################################################################################
229 class DBBinary(object):
230 def __init__(self, *args, **kwargs):
234 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
236 __all__.append('DBBinary')
239 def get_suites_binary_in(package, session=None):
241 Returns list of Suite objects which given C{package} name is in
244 @param source: DBBinary package name to search for
247 @return: list of Suite objects for the given package
250 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
252 __all__.append('get_suites_binary_in')
255 def get_binary_from_id(id, session=None):
257 Returns DBBinary object for given C{id}
260 @param id: Id of the required binary
262 @type session: Session
263 @param session: Optional SQLA session object (a temporary one will be
264 generated if not supplied)
267 @return: DBBinary object for the given binary (None if not present)
270 q = session.query(DBBinary).filter_by(binary_id=id)
274 except NoResultFound:
277 __all__.append('get_binary_from_id')
280 def get_binaries_from_name(package, version=None, architecture=None, session=None):
282 Returns list of DBBinary objects for given C{package} name
285 @param package: DBBinary package name to search for
287 @type version: str or None
288 @param version: Version to search for (or None)
290 @type package: str, list or None
291 @param package: Architectures to limit to (or None if no limit)
293 @type session: Session
294 @param session: Optional SQL session object (a temporary one will be
295 generated if not supplied)
298 @return: list of DBBinary objects for the given name (may be empty)
301 q = session.query(DBBinary).filter_by(package=package)
303 if version is not None:
304 q = q.filter_by(version=version)
306 if architecture is not None:
307 if not isinstance(architecture, list):
308 architecture = [architecture]
309 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
315 __all__.append('get_binaries_from_name')
318 def get_binaries_from_source_id(source_id, session=None):
320 Returns list of DBBinary objects for given C{source_id}
323 @param source_id: source_id to search for
325 @type session: Session
326 @param session: Optional SQL session object (a temporary one will be
327 generated if not supplied)
330 @return: list of DBBinary objects for the given name (may be empty)
333 return session.query(DBBinary).filter_by(source_id=source_id).all()
335 __all__.append('get_binaries_from_source_id')
338 def get_binary_from_name_suite(package, suitename, session=None):
339 ### For dak examine-package
340 ### XXX: Doesn't use object API yet
342 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
343 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
344 WHERE b.package=:package
346 AND fi.location = l.id
347 AND l.component = c.id
350 AND su.suite_name=:suitename
351 ORDER BY b.version DESC"""
353 return session.execute(sql, {'package': package, 'suitename': suitename})
355 __all__.append('get_binary_from_name_suite')
358 def get_binary_components(package, suitename, arch, session=None):
359 # Check for packages that have moved from one component to another
360 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
361 WHERE b.package=:package AND s.suite_name=:suitename
362 AND (a.arch_string = :arch OR a.arch_string = 'all')
363 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
364 AND f.location = l.id
365 AND l.component = c.id
368 vals = {'package': package, 'suitename': suitename, 'arch': arch}
370 return session.execute(query, vals)
372 __all__.append('get_binary_components')
374 ################################################################################
376 class Component(object):
377 def __init__(self, *args, **kwargs):
380 def __eq__(self, val):
381 if isinstance(val, str):
382 return (self.component_name == val)
383 # This signals to use the normal comparison operator
384 return NotImplemented
386 def __ne__(self, val):
387 if isinstance(val, str):
388 return (self.component_name != val)
389 # This signals to use the normal comparison operator
390 return NotImplemented
393 return '<Component %s>' % self.component_name
396 __all__.append('Component')
399 def get_component(component, session=None):
401 Returns database id for given C{component}.
403 @type component: string
404 @param component: The name of the override type
407 @return: the database id for the given component
410 component = component.lower()
412 q = session.query(Component).filter_by(component_name=component)
416 except NoResultFound:
419 __all__.append('get_component')
421 ################################################################################
423 class DBConfig(object):
424 def __init__(self, *args, **kwargs):
428 return '<DBConfig %s>' % self.name
430 __all__.append('DBConfig')
432 ################################################################################
434 class ContentFilename(object):
435 def __init__(self, *args, **kwargs):
439 return '<ContentFilename %s>' % self.filename
441 __all__.append('ContentFilename')
443 def get_or_set_contents_file_id(filename, session=None):
445 Returns database id for given filename.
447 If no matching file is found, a row is inserted.
449 @type filename: string
450 @param filename: The filename
451 @type session: SQLAlchemy
452 @param session: Optional SQL session object (a temporary one will be
453 generated if not supplied). If not passed, a commit will be performed at
454 the end of the function, otherwise the caller is responsible for commiting.
457 @return: the database id for the given component
461 session = DBConn().session()
464 q = session.query(ContentFilename).filter_by(filename=filename)
467 ret = q.one().cafilename_id
468 except NoResultFound:
469 cf = ContentFilename()
470 cf.filename = filename
476 ret = cf.cafilename_id
483 __all__.append('get_or_set_contents_file_id')
486 def get_contents(suite, overridetype, section=None, session=None):
488 Returns contents for a suite / overridetype combination, limiting
489 to a section if not None.
492 @param suite: Suite object
494 @type overridetype: OverrideType
495 @param overridetype: OverrideType object
497 @type section: Section
498 @param section: Optional section object to limit results to
500 @type session: SQLAlchemy
501 @param session: Optional SQL session object (a temporary one will be
502 generated if not supplied)
505 @return: ResultsProxy object set up to return tuples of (filename, section,
509 # find me all of the contents for a given suite
510 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
514 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
515 JOIN content_file_names n ON (c.filename=n.id)
516 JOIN binaries b ON (b.id=c.binary_pkg)
517 JOIN override o ON (o.package=b.package)
518 JOIN section s ON (s.id=o.section)
519 WHERE o.suite = :suiteid AND o.type = :overridetypeid
520 AND b.type=:overridetypename"""
522 vals = {'suiteid': suite.suite_id,
523 'overridetypeid': overridetype.overridetype_id,
524 'overridetypename': overridetype.overridetype}
526 if section is not None:
527 contents_q += " AND s.id = :sectionid"
528 vals['sectionid'] = section.section_id
530 contents_q += " ORDER BY fn"
532 return session.execute(contents_q, vals)
534 __all__.append('get_contents')
536 ################################################################################
538 class ContentFilepath(object):
539 def __init__(self, *args, **kwargs):
543 return '<ContentFilepath %s>' % self.filepath
545 __all__.append('ContentFilepath')
547 def get_or_set_contents_path_id(filepath, session=None):
549 Returns database id for given path.
551 If no matching file is found, a row is inserted.
553 @type filename: string
554 @param filename: The filepath
555 @type session: SQLAlchemy
556 @param session: Optional SQL session object (a temporary one will be
557 generated if not supplied). If not passed, a commit will be performed at
558 the end of the function, otherwise the caller is responsible for commiting.
561 @return: the database id for the given path
565 session = DBConn().session()
568 q = session.query(ContentFilepath).filter_by(filepath=filepath)
571 ret = q.one().cafilepath_id
572 except NoResultFound:
573 cf = ContentFilepath()
574 cf.filepath = filepath
580 ret = cf.cafilepath_id
587 __all__.append('get_or_set_contents_path_id')
589 ################################################################################
591 class ContentAssociation(object):
592 def __init__(self, *args, **kwargs):
596 return '<ContentAssociation %s>' % self.ca_id
598 __all__.append('ContentAssociation')
600 def insert_content_paths(binary_id, fullpaths, session=None):
602 Make sure given path is associated with given binary id
605 @param binary_id: the id of the binary
606 @type fullpaths: list
607 @param fullpaths: the list of paths of the file being associated with the binary
608 @type session: SQLAlchemy session
609 @param session: Optional SQLAlchemy session. If this is passed, the caller
610 is responsible for ensuring a transaction has begun and committing the
611 results or rolling back based on the result code. If not passed, a commit
612 will be performed at the end of the function, otherwise the caller is
613 responsible for commiting.
615 @return: True upon success
620 session = DBConn().session()
626 for fullpath in fullpaths:
627 # Get the necessary IDs ...
628 (path, file) = os.path.split(fullpath)
630 filepath_id = get_or_set_contents_path_id(path, session)
631 filename_id = get_or_set_contents_file_id(file, session)
633 pathcache[fullpath] = (filepath_id, filename_id)
635 for fullpath, dat in pathcache.items():
636 ca = ContentAssociation()
637 ca.binary_id = binary_id
638 ca.filepath_id = dat[0]
639 ca.filename_id = dat[1]
642 # Only commit if we set up the session ourself
652 traceback.print_exc()
654 # Only rollback if we set up the session ourself
661 __all__.append('insert_content_paths')
663 ################################################################################
665 class DSCFile(object):
666 def __init__(self, *args, **kwargs):
670 return '<DSCFile %s>' % self.dscfile_id
672 __all__.append('DSCFile')
675 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
677 Returns a list of DSCFiles which may be empty
679 @type dscfile_id: int (optional)
680 @param dscfile_id: the dscfile_id of the DSCFiles to find
682 @type source_id: int (optional)
683 @param source_id: the source id related to the DSCFiles to find
685 @type poolfile_id: int (optional)
686 @param poolfile_id: the poolfile id related to the DSCFiles to find
689 @return: Possibly empty list of DSCFiles
692 q = session.query(DSCFile)
694 if dscfile_id is not None:
695 q = q.filter_by(dscfile_id=dscfile_id)
697 if source_id is not None:
698 q = q.filter_by(source_id=source_id)
700 if poolfile_id is not None:
701 q = q.filter_by(poolfile_id=poolfile_id)
705 __all__.append('get_dscfiles')
707 ################################################################################
709 class PoolFile(object):
710 def __init__(self, *args, **kwargs):
714 return '<PoolFile %s>' % self.filename
716 __all__.append('PoolFile')
719 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
722 (ValidFileFound [boolean or None], PoolFile object or None)
724 @type filename: string
725 @param filename: the filename of the file to check against the DB
728 @param filesize: the size of the file to check against the DB
731 @param md5sum: the md5sum of the file to check against the DB
733 @type location_id: int
734 @param location_id: the id of the location to look in
737 @return: Tuple of length 2.
738 If more than one file found with that name:
740 If valid pool file found: (True, PoolFile object)
741 If valid pool file not found:
742 (False, None) if no file found
743 (False, PoolFile object) if file found with size/md5sum mismatch
746 q = session.query(PoolFile).filter_by(filename=filename)
747 q = q.join(Location).filter_by(location_id=location_id)
757 if obj.md5sum != md5sum or obj.filesize != filesize:
765 __all__.append('check_poolfile')
768 def get_poolfile_by_id(file_id, session=None):
770 Returns a PoolFile objects or None for the given id
773 @param file_id: the id of the file to look for
775 @rtype: PoolFile or None
776 @return: either the PoolFile object or None
779 q = session.query(PoolFile).filter_by(file_id=file_id)
783 except NoResultFound:
786 __all__.append('get_poolfile_by_id')
790 def get_poolfile_by_name(filename, location_id=None, session=None):
792 Returns an array of PoolFile objects for the given filename and
793 (optionally) location_id
795 @type filename: string
796 @param filename: the filename of the file to check against the DB
798 @type location_id: int
799 @param location_id: the id of the location to look in (optional)
802 @return: array of PoolFile objects
805 q = session.query(PoolFile).filter_by(filename=filename)
807 if location_id is not None:
808 q = q.join(Location).filter_by(location_id=location_id)
812 __all__.append('get_poolfile_by_name')
815 def get_poolfile_like_name(filename, session=None):
817 Returns an array of PoolFile objects which are like the given name
819 @type filename: string
820 @param filename: the filename of the file to check against the DB
823 @return: array of PoolFile objects
826 # TODO: There must be a way of properly using bind parameters with %FOO%
827 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
831 __all__.append('get_poolfile_like_name')
833 ################################################################################
835 class Fingerprint(object):
836 def __init__(self, *args, **kwargs):
840 return '<Fingerprint %s>' % self.fingerprint
842 __all__.append('Fingerprint')
844 def get_or_set_fingerprint(fpr, session=None):
846 Returns Fingerprint object for given fpr.
848 If no matching fpr is found, a row is inserted.
851 @param fpr: The fpr to find / add
853 @type session: SQLAlchemy
854 @param session: Optional SQL session object (a temporary one will be
855 generated if not supplied). If not passed, a commit will be performed at
856 the end of the function, otherwise the caller is responsible for commiting.
857 A flush will be performed either way.
860 @return: the Fingerprint object for the given fpr
864 session = DBConn().session()
867 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
871 except NoResultFound:
872 fingerprint = Fingerprint()
873 fingerprint.fingerprint = fpr
874 session.add(fingerprint)
886 __all__.append('get_or_set_fingerprint')
888 ################################################################################
890 class Keyring(object):
891 def __init__(self, *args, **kwargs):
895 return '<Keyring %s>' % self.keyring_name
897 __all__.append('Keyring')
899 def get_or_set_keyring(keyring, session=None):
901 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
902 and return the new Keyring
903 If C{keyring} already has an entry, simply return the existing Keyring
905 @type keyring: string
906 @param keyring: the keyring name
909 @return: the Keyring object for this keyring
914 session = DBConn().session()
918 obj = session.query(Keyring).filter_by(keyring_name=keyring).first()
921 obj = Keyring(keyring_name=keyring)
933 __all__.append('get_or_set_keyring')
935 ################################################################################
937 class Location(object):
938 def __init__(self, *args, **kwargs):
942 return '<Location %s (%s)>' % (self.path, self.location_id)
944 __all__.append('Location')
947 def get_location(location, component=None, archive=None, session=None):
949 Returns Location object for the given combination of location, component
952 @type location: string
953 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
955 @type component: string
956 @param component: the component name (if None, no restriction applied)
958 @type archive: string
959 @param archive_id: the archive name (if None, no restriction applied)
961 @rtype: Location / None
962 @return: Either a Location object or None if one can't be found
965 q = session.query(Location).filter_by(path=location)
967 if archive is not None:
968 q = q.join(Archive).filter_by(archive_name=archive)
970 if component is not None:
971 q = q.join(Component).filter_by(component_name=component)
975 except NoResultFound:
978 __all__.append('get_location')
980 ################################################################################
982 class Maintainer(object):
983 def __init__(self, *args, **kwargs):
987 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
989 def get_split_maintainer(self):
990 if not hasattr(self, 'name') or self.name is None:
991 return ('', '', '', '')
993 return fix_maintainer(self.name.strip())
995 __all__.append('Maintainer')
997 def get_or_set_maintainer(name, session=None):
999 Returns Maintainer object for given maintainer name.
1001 If no matching maintainer name is found, a row is inserted.
1004 @param name: The maintainer name to add
1006 @type session: SQLAlchemy
1007 @param session: Optional SQL session object (a temporary one will be
1008 generated if not supplied). If not passed, a commit will be performed at
1009 the end of the function, otherwise the caller is responsible for commiting.
1010 A flush will be performed either way.
1013 @return: the Maintainer object for the given maintainer
1015 privatetrans = False
1017 session = DBConn().session()
1020 q = session.query(Maintainer).filter_by(name=name)
1023 except NoResultFound:
1024 maintainer = Maintainer()
1025 maintainer.name = name
1026 session.add(maintainer)
1038 __all__.append('get_or_set_maintainer')
1040 def get_maintainer(maintainer_id, session=None):
1042 Return the name of the maintainer behind C{maintainer_id} or None if that
1043 maintainer_id is invalid.
1045 @type maintainer_id: int
1046 @param maintainer_id: the id of the maintainer
1049 @return: the Maintainer with this C{maintainer_id}
1052 privatetrans = False
1054 session = DBConn().session()
1058 return session.query(Maintainer).get(maintainer_id)
1063 __all__.append('get_maintainer')
1065 ################################################################################
1067 class NewComment(object):
1068 def __init__(self, *args, **kwargs):
1072 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1074 __all__.append('NewComment')
1077 def has_new_comment(package, version, session=None):
1079 Returns true if the given combination of C{package}, C{version} has a comment.
1081 @type package: string
1082 @param package: name of the package
1084 @type version: string
1085 @param version: package version
1087 @type session: Session
1088 @param session: Optional SQLA session object (a temporary one will be
1089 generated if not supplied)
1095 q = session.query(NewComment)
1096 q = q.filter_by(package=package)
1097 q = q.filter_by(version=version)
1099 return bool(q.count() > 0)
1101 __all__.append('has_new_comment')
1104 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1106 Returns (possibly empty) list of NewComment objects for the given
1109 @type package: string (optional)
1110 @param package: name of the package
1112 @type version: string (optional)
1113 @param version: package version
1115 @type comment_id: int (optional)
1116 @param comment_id: An id of a comment
1118 @type session: Session
1119 @param session: Optional SQLA session object (a temporary one will be
1120 generated if not supplied)
1123 @return: A (possibly empty) list of NewComment objects will be returned
1126 q = session.query(NewComment)
1127 if package is not None: q = q.filter_by(package=package)
1128 if version is not None: q = q.filter_by(version=version)
1129 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1133 __all__.append('get_new_comments')
1135 ################################################################################
1137 class Override(object):
1138 def __init__(self, *args, **kwargs):
1142 return '<Override %s (%s)>' % (self.package, self.suite_id)
1144 __all__.append('Override')
1147 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1149 Returns Override object for the given parameters
1151 @type package: string
1152 @param package: The name of the package
1154 @type suite: string, list or None
1155 @param suite: The name of the suite (or suites if a list) to limit to. If
1156 None, don't limit. Defaults to None.
1158 @type component: string, list or None
1159 @param component: The name of the component (or components if a list) to
1160 limit to. If None, don't limit. Defaults to None.
1162 @type overridetype: string, list or None
1163 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1164 limit to. If None, don't limit. Defaults to None.
1166 @type session: Session
1167 @param session: Optional SQLA session object (a temporary one will be
1168 generated if not supplied)
1171 @return: A (possibly empty) list of Override objects will be returned
1174 q = session.query(Override)
1175 q = q.filter_by(package=package)
1177 if suite is not None:
1178 if not isinstance(suite, list): suite = [suite]
1179 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1181 if component is not None:
1182 if not isinstance(component, list): component = [component]
1183 q = q.join(Component).filter(Component.component_name.in_(component))
1185 if overridetype is not None:
1186 if not isinstance(overridetype, list): overridetype = [overridetype]
1187 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1191 __all__.append('get_override')
1194 ################################################################################
1196 class OverrideType(object):
1197 def __init__(self, *args, **kwargs):
1201 return '<OverrideType %s>' % self.overridetype
1203 __all__.append('OverrideType')
1206 def get_override_type(override_type, session=None):
1208 Returns OverrideType object for given C{override type}.
1210 @type override_type: string
1211 @param override_type: The name of the override type
1213 @type session: Session
1214 @param session: Optional SQLA session object (a temporary one will be
1215 generated if not supplied)
1218 @return: the database id for the given override type
1221 q = session.query(OverrideType).filter_by(overridetype=override_type)
1225 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
1454 utils.copy(src, dest)
1456 # Create a symlink to it
1457 os.symlink(src, dest)
1460 qb.suite_id = s.suite_id
1461 qb.queue_id = self.queue_id
1467 # If the .orig.tar.gz is in the pool, create a symlink to
1468 # it (if one doesn't already exist)
1469 if changes.orig_tar_id:
1470 # Determine the .orig.tar.gz file name
1471 for dsc_file in changes.dsc_files.keys():
1472 if dsc_file.endswith(".orig.tar.gz"):
1475 dest = os.path.join(dest_dir, filename)
1477 # If it doesn't exist, create a symlink
1478 if not os.path.exists(dest):
1479 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1480 {'id': changes.orig_tar_id})
1483 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1485 src = os.path.join(res[0], res[1])
1486 os.symlink(src, dest)
1488 # Add it to the list of packages for later processing by apt-ftparchive
1490 qb.suite_id = s.suite_id
1491 qb.queue_id = self.queue_id
1496 # If it does, update things to ensure it's not removed prematurely
1498 qb = get_queue_build(dest, s.suite_id, session)
1510 __all__.append('Queue')
1513 def get_queue(queuename, session=None):
1515 Returns Queue object for given C{queue name}.
1517 @type queuename: string
1518 @param queuename: The name of the queue
1520 @type session: Session
1521 @param session: Optional SQLA session object (a temporary one will be
1522 generated if not supplied)
1525 @return: Queue object for the given queue
1528 q = session.query(Queue).filter_by(queue_name=queuename)
1532 except NoResultFound:
1535 __all__.append('get_queue')
1537 ################################################################################
1539 class QueueBuild(object):
1540 def __init__(self, *args, **kwargs):
1544 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1546 __all__.append('QueueBuild')
1549 def get_queue_build(filename, suite, session=None):
1551 Returns QueueBuild object for given C{filename} and C{suite}.
1553 @type filename: string
1554 @param filename: The name of the file
1556 @type suiteid: int or str
1557 @param suiteid: Suite name or ID
1559 @type session: Session
1560 @param session: Optional SQLA session object (a temporary one will be
1561 generated if not supplied)
1564 @return: Queue object for the given queue
1567 if isinstance(suite, int):
1568 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1570 q = session.query(QueueBuild).filter_by(filename=filename)
1571 q = q.join(Suite).filter_by(suite_name=suite)
1575 except NoResultFound:
1578 __all__.append('get_queue_build')
1580 ################################################################################
1582 class Section(object):
1583 def __init__(self, *args, **kwargs):
1586 def __eq__(self, val):
1587 if isinstance(val, str):
1588 return (self.section == val)
1589 # This signals to use the normal comparison operator
1590 return NotImplemented
1592 def __ne__(self, val):
1593 if isinstance(val, str):
1594 return (self.section != val)
1595 # This signals to use the normal comparison operator
1596 return NotImplemented
1599 return '<Section %s>' % self.section
1601 __all__.append('Section')
1604 def get_section(section, session=None):
1606 Returns Section object for given C{section name}.
1608 @type section: string
1609 @param section: The name of the section
1611 @type session: Session
1612 @param session: Optional SQLA session object (a temporary one will be
1613 generated if not supplied)
1616 @return: Section object for the given section name
1619 q = session.query(Section).filter_by(section=section)
1623 except NoResultFound:
1626 __all__.append('get_section')
1629 def get_sections(session=None):
1631 Returns dictionary of section names -> id mappings
1633 @type session: Session
1634 @param session: Optional SQL session object (a temporary one will be
1635 generated if not supplied)
1638 @return: dictionary of section names -> id mappings
1642 q = session.query(Section)
1644 ret[x.section] = x.section_id
1648 __all__.append('get_sections')
1650 ################################################################################
1652 class DBSource(object):
1653 def __init__(self, *args, **kwargs):
1657 return '<DBSource %s (%s)>' % (self.source, self.version)
1659 __all__.append('DBSource')
1662 def source_exists(source, source_version, suites = ["any"], session=None):
1664 Ensure that source exists somewhere in the archive for the binary
1665 upload being processed.
1666 1. exact match => 1.0-3
1667 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1669 @type package: string
1670 @param package: package source name
1672 @type source_version: string
1673 @param source_version: expected source version
1676 @param suites: list of suites to check in, default I{any}
1678 @type session: Session
1679 @param session: Optional SQLA session object (a temporary one will be
1680 generated if not supplied)
1683 @return: returns 1 if a source with expected version is found, otherwise 0
1690 for suite in suites:
1691 q = session.query(DBSource).filter_by(source=source)
1693 # source must exist in suite X, or in some other suite that's
1694 # mapped to X, recursively... silent-maps are counted too,
1695 # unreleased-maps aren't.
1696 maps = cnf.ValueList("SuiteMappings")[:]
1698 maps = [ m.split() for m in maps ]
1699 maps = [ (x[1], x[2]) for x in maps
1700 if x[0] == "map" or x[0] == "silent-map" ]
1703 if x[1] in s and x[0] not in s:
1706 q = q.join(SrcAssociation).join(Suite)
1707 q = q.filter(Suite.suite_name.in_(s))
1709 # Reduce the query results to a list of version numbers
1710 ql = [ j.version for j in q.all() ]
1713 if source_version in ql:
1717 from daklib.regexes import re_bin_only_nmu
1718 orig_source_version = re_bin_only_nmu.sub('', source_version)
1719 if orig_source_version in ql:
1722 # No source found so return not ok
1727 __all__.append('source_exists')
1730 def get_suites_source_in(source, session=None):
1732 Returns list of Suite objects which given C{source} name is in
1735 @param source: DBSource package name to search for
1738 @return: list of Suite objects for the given source
1741 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1743 __all__.append('get_suites_source_in')
1746 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1748 Returns list of DBSource objects for given C{source} name and other parameters
1751 @param source: DBSource package name to search for
1753 @type source: str or None
1754 @param source: DBSource version name to search for or None if not applicable
1756 @type dm_upload_allowed: bool
1757 @param dm_upload_allowed: If None, no effect. If True or False, only
1758 return packages with that dm_upload_allowed setting
1760 @type session: Session
1761 @param session: Optional SQL session object (a temporary one will be
1762 generated if not supplied)
1765 @return: list of DBSource objects for the given name (may be empty)
1768 q = session.query(DBSource).filter_by(source=source)
1770 if version is not None:
1771 q = q.filter_by(version=version)
1773 if dm_upload_allowed is not None:
1774 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1778 __all__.append('get_sources_from_name')
1781 def get_source_in_suite(source, suite, session=None):
1783 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1785 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1786 - B{suite} - a suite name, eg. I{unstable}
1788 @type source: string
1789 @param source: source package name
1792 @param suite: the suite name
1795 @return: the version for I{source} in I{suite}
1799 q = session.query(SrcAssociation)
1800 q = q.join('source').filter_by(source=source)
1801 q = q.join('suite').filter_by(suite_name=suite)
1804 return q.one().source
1805 except NoResultFound:
1808 __all__.append('get_source_in_suite')
1810 ################################################################################
1812 class SrcAssociation(object):
1813 def __init__(self, *args, **kwargs):
1817 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1819 __all__.append('SrcAssociation')
1821 ################################################################################
1823 class SrcUploader(object):
1824 def __init__(self, *args, **kwargs):
1828 return '<SrcUploader %s>' % self.uploader_id
1830 __all__.append('SrcUploader')
1832 ################################################################################
1834 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1835 ('SuiteID', 'suite_id'),
1836 ('Version', 'version'),
1837 ('Origin', 'origin'),
1839 ('Description', 'description'),
1840 ('Untouchable', 'untouchable'),
1841 ('Announce', 'announce'),
1842 ('Codename', 'codename'),
1843 ('OverrideCodename', 'overridecodename'),
1844 ('ValidTime', 'validtime'),
1845 ('Priority', 'priority'),
1846 ('NotAutomatic', 'notautomatic'),
1847 ('CopyChanges', 'copychanges'),
1848 ('CopyDotDak', 'copydotdak'),
1849 ('CommentsDir', 'commentsdir'),
1850 ('OverrideSuite', 'overridesuite'),
1851 ('ChangelogBase', 'changelogbase')]
1854 class Suite(object):
1855 def __init__(self, *args, **kwargs):
1859 return '<Suite %s>' % self.suite_name
1861 def __eq__(self, val):
1862 if isinstance(val, str):
1863 return (self.suite_name == val)
1864 # This signals to use the normal comparison operator
1865 return NotImplemented
1867 def __ne__(self, val):
1868 if isinstance(val, str):
1869 return (self.suite_name != val)
1870 # This signals to use the normal comparison operator
1871 return NotImplemented
1875 for disp, field in SUITE_FIELDS:
1876 val = getattr(self, field, None)
1878 ret.append("%s: %s" % (disp, val))
1880 return "\n".join(ret)
1882 __all__.append('Suite')
1885 def get_suite_architecture(suite, architecture, session=None):
1887 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1891 @param suite: Suite name to search for
1893 @type architecture: str
1894 @param architecture: Architecture name to search for
1896 @type session: Session
1897 @param session: Optional SQL session object (a temporary one will be
1898 generated if not supplied)
1900 @rtype: SuiteArchitecture
1901 @return: the SuiteArchitecture object or None
1904 q = session.query(SuiteArchitecture)
1905 q = q.join(Architecture).filter_by(arch_string=architecture)
1906 q = q.join(Suite).filter_by(suite_name=suite)
1910 except NoResultFound:
1913 __all__.append('get_suite_architecture')
1916 def get_suite(suite, session=None):
1918 Returns Suite object for given C{suite name}.
1921 @param suite: The name of the suite
1923 @type session: Session
1924 @param session: Optional SQLA session object (a temporary one will be
1925 generated if not supplied)
1928 @return: Suite object for the requested suite name (None if not present)
1931 q = session.query(Suite).filter_by(suite_name=suite)
1935 except NoResultFound:
1938 __all__.append('get_suite')
1940 ################################################################################
1942 class SuiteArchitecture(object):
1943 def __init__(self, *args, **kwargs):
1947 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1949 __all__.append('SuiteArchitecture')
1952 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1954 Returns list of Architecture objects for given C{suite} name
1957 @param source: Suite name to search for
1959 @type skipsrc: boolean
1960 @param skipsrc: Whether to skip returning the 'source' architecture entry
1963 @type skipall: boolean
1964 @param skipall: Whether to skip returning the 'all' architecture entry
1967 @type session: Session
1968 @param session: Optional SQL session object (a temporary one will be
1969 generated if not supplied)
1972 @return: list of Architecture objects for the given name (may be empty)
1975 q = session.query(Architecture)
1976 q = q.join(SuiteArchitecture)
1977 q = q.join(Suite).filter_by(suite_name=suite)
1980 q = q.filter(Architecture.arch_string != 'source')
1983 q = q.filter(Architecture.arch_string != 'all')
1985 q = q.order_by('arch_string')
1989 __all__.append('get_suite_architectures')
1991 ################################################################################
1994 def __init__(self, *args, **kwargs):
1997 def __eq__(self, val):
1998 if isinstance(val, str):
1999 return (self.uid == val)
2000 # This signals to use the normal comparison operator
2001 return NotImplemented
2003 def __ne__(self, val):
2004 if isinstance(val, str):
2005 return (self.uid != val)
2006 # This signals to use the normal comparison operator
2007 return NotImplemented
2010 return '<Uid %s (%s)>' % (self.uid, self.name)
2012 __all__.append('Uid')
2014 def add_database_user(uidname, session=None):
2016 Adds a database user
2018 @type uidname: string
2019 @param uidname: The uid of the user to add
2021 @type session: SQLAlchemy
2022 @param session: Optional SQL session object (a temporary one will be
2023 generated if not supplied). If not passed, a commit will be performed at
2024 the end of the function, otherwise the caller is responsible for commiting.
2027 @return: the uid object for the given uidname
2030 privatetrans = False
2032 session = DBConn().session()
2035 session.execute("CREATE USER :uid", {'uid': uidname})
2041 __all__.append('add_database_user')
2043 def get_or_set_uid(uidname, session=None):
2045 Returns uid object for given uidname.
2047 If no matching uidname is found, a row is inserted.
2049 @type uidname: string
2050 @param uidname: The uid to add
2052 @type session: SQLAlchemy
2053 @param session: Optional SQL session object (a temporary one will be
2054 generated if not supplied). If not passed, a commit will be performed at
2055 the end of the function, otherwise the caller is responsible for commiting.
2058 @return: the uid object for the given uidname
2061 privatetrans = False
2063 session = DBConn().session()
2066 q = session.query(Uid).filter_by(uid=uidname)
2070 except NoResultFound:
2085 __all__.append('get_or_set_uid')
2088 def get_uid_from_fingerprint(fpr, session=None):
2089 q = session.query(Uid)
2090 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2094 except NoResultFound:
2097 __all__.append('get_uid_from_fingerprint')
2099 ################################################################################
2101 class DBConn(Singleton):
2103 database module init.
2105 def __init__(self, *args, **kwargs):
2106 super(DBConn, self).__init__(*args, **kwargs)
2108 def _startup(self, *args, **kwargs):
2110 if kwargs.has_key('debug'):
2114 def __setuptables(self):
2115 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2116 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2117 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2118 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2119 self.tbl_component = Table('component', self.db_meta, autoload=True)
2120 self.tbl_config = Table('config', self.db_meta, autoload=True)
2121 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2122 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2123 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2124 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2125 self.tbl_files = Table('files', self.db_meta, autoload=True)
2126 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2127 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2128 self.tbl_location = Table('location', self.db_meta, autoload=True)
2129 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2130 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2131 self.tbl_override = Table('override', self.db_meta, autoload=True)
2132 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2133 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2134 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2135 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2136 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2137 self.tbl_section = Table('section', self.db_meta, autoload=True)
2138 self.tbl_source = Table('source', self.db_meta, autoload=True)
2139 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2140 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2141 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2142 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2143 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2145 def __setupmappers(self):
2146 mapper(Architecture, self.tbl_architecture,
2147 properties = dict(arch_id = self.tbl_architecture.c.id))
2149 mapper(Archive, self.tbl_archive,
2150 properties = dict(archive_id = self.tbl_archive.c.id,
2151 archive_name = self.tbl_archive.c.name))
2153 mapper(BinAssociation, self.tbl_bin_associations,
2154 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2155 suite_id = self.tbl_bin_associations.c.suite,
2156 suite = relation(Suite),
2157 binary_id = self.tbl_bin_associations.c.bin,
2158 binary = relation(DBBinary)))
2160 mapper(DBBinary, self.tbl_binaries,
2161 properties = dict(binary_id = self.tbl_binaries.c.id,
2162 package = self.tbl_binaries.c.package,
2163 version = self.tbl_binaries.c.version,
2164 maintainer_id = self.tbl_binaries.c.maintainer,
2165 maintainer = relation(Maintainer),
2166 source_id = self.tbl_binaries.c.source,
2167 source = relation(DBSource),
2168 arch_id = self.tbl_binaries.c.architecture,
2169 architecture = relation(Architecture),
2170 poolfile_id = self.tbl_binaries.c.file,
2171 poolfile = relation(PoolFile),
2172 binarytype = self.tbl_binaries.c.type,
2173 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2174 fingerprint = relation(Fingerprint),
2175 install_date = self.tbl_binaries.c.install_date,
2176 binassociations = relation(BinAssociation,
2177 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2179 mapper(Component, self.tbl_component,
2180 properties = dict(component_id = self.tbl_component.c.id,
2181 component_name = self.tbl_component.c.name))
2183 mapper(DBConfig, self.tbl_config,
2184 properties = dict(config_id = self.tbl_config.c.id))
2186 mapper(ContentAssociation, self.tbl_content_associations,
2187 properties = dict(ca_id = self.tbl_content_associations.c.id,
2188 filename_id = self.tbl_content_associations.c.filename,
2189 filename = relation(ContentFilename),
2190 filepath_id = self.tbl_content_associations.c.filepath,
2191 filepath = relation(ContentFilepath),
2192 binary_id = self.tbl_content_associations.c.binary_pkg,
2193 binary = relation(DBBinary)))
2196 mapper(ContentFilename, self.tbl_content_file_names,
2197 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2198 filename = self.tbl_content_file_names.c.file))
2200 mapper(ContentFilepath, self.tbl_content_file_paths,
2201 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2202 filepath = self.tbl_content_file_paths.c.path))
2204 mapper(DSCFile, self.tbl_dsc_files,
2205 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2206 source_id = self.tbl_dsc_files.c.source,
2207 source = relation(DBSource),
2208 poolfile_id = self.tbl_dsc_files.c.file,
2209 poolfile = relation(PoolFile)))
2211 mapper(PoolFile, self.tbl_files,
2212 properties = dict(file_id = self.tbl_files.c.id,
2213 filesize = self.tbl_files.c.size,
2214 location_id = self.tbl_files.c.location,
2215 location = relation(Location)))
2217 mapper(Fingerprint, self.tbl_fingerprint,
2218 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2219 uid_id = self.tbl_fingerprint.c.uid,
2220 uid = relation(Uid),
2221 keyring_id = self.tbl_fingerprint.c.keyring,
2222 keyring = relation(Keyring)))
2224 mapper(Keyring, self.tbl_keyrings,
2225 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2226 keyring_id = self.tbl_keyrings.c.id))
2228 mapper(Location, self.tbl_location,
2229 properties = dict(location_id = self.tbl_location.c.id,
2230 component_id = self.tbl_location.c.component,
2231 component = relation(Component),
2232 archive_id = self.tbl_location.c.archive,
2233 archive = relation(Archive),
2234 archive_type = self.tbl_location.c.type))
2236 mapper(Maintainer, self.tbl_maintainer,
2237 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2239 mapper(NewComment, self.tbl_new_comments,
2240 properties = dict(comment_id = self.tbl_new_comments.c.id))
2242 mapper(Override, self.tbl_override,
2243 properties = dict(suite_id = self.tbl_override.c.suite,
2244 suite = relation(Suite),
2245 component_id = self.tbl_override.c.component,
2246 component = relation(Component),
2247 priority_id = self.tbl_override.c.priority,
2248 priority = relation(Priority),
2249 section_id = self.tbl_override.c.section,
2250 section = relation(Section),
2251 overridetype_id = self.tbl_override.c.type,
2252 overridetype = relation(OverrideType)))
2254 mapper(OverrideType, self.tbl_override_type,
2255 properties = dict(overridetype = self.tbl_override_type.c.type,
2256 overridetype_id = self.tbl_override_type.c.id))
2258 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2259 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2260 filepath_id = self.tbl_pending_content_associations.c.filepath,
2261 filepath = relation(ContentFilepath),
2262 filename_id = self.tbl_pending_content_associations.c.filename,
2263 filename = relation(ContentFilename)))
2265 mapper(Priority, self.tbl_priority,
2266 properties = dict(priority_id = self.tbl_priority.c.id))
2268 mapper(Queue, self.tbl_queue,
2269 properties = dict(queue_id = self.tbl_queue.c.id))
2271 mapper(QueueBuild, self.tbl_queue_build,
2272 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2273 queue_id = self.tbl_queue_build.c.queue,
2274 queue = relation(Queue, backref='queuebuild')))
2276 mapper(Section, self.tbl_section,
2277 properties = dict(section_id = self.tbl_section.c.id))
2279 mapper(DBSource, self.tbl_source,
2280 properties = dict(source_id = self.tbl_source.c.id,
2281 version = self.tbl_source.c.version,
2282 maintainer_id = self.tbl_source.c.maintainer,
2283 maintainer = relation(Maintainer,
2284 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2285 poolfile_id = self.tbl_source.c.file,
2286 poolfile = relation(PoolFile),
2287 fingerprint_id = self.tbl_source.c.sig_fpr,
2288 fingerprint = relation(Fingerprint),
2289 changedby_id = self.tbl_source.c.changedby,
2290 changedby = relation(Maintainer,
2291 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2292 srcfiles = relation(DSCFile,
2293 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2294 srcassociations = relation(SrcAssociation,
2295 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2297 mapper(SrcAssociation, self.tbl_src_associations,
2298 properties = dict(sa_id = self.tbl_src_associations.c.id,
2299 suite_id = self.tbl_src_associations.c.suite,
2300 suite = relation(Suite),
2301 source_id = self.tbl_src_associations.c.source,
2302 source = relation(DBSource)))
2304 mapper(SrcUploader, self.tbl_src_uploaders,
2305 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2306 source_id = self.tbl_src_uploaders.c.source,
2307 source = relation(DBSource,
2308 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2309 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2310 maintainer = relation(Maintainer,
2311 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2313 mapper(Suite, self.tbl_suite,
2314 properties = dict(suite_id = self.tbl_suite.c.id))
2316 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2317 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2318 suite = relation(Suite, backref='suitearchitectures'),
2319 arch_id = self.tbl_suite_architectures.c.architecture,
2320 architecture = relation(Architecture)))
2322 mapper(Uid, self.tbl_uid,
2323 properties = dict(uid_id = self.tbl_uid.c.id,
2324 fingerprint = relation(Fingerprint)))
2326 ## Connection functions
2327 def __createconn(self):
2328 from config import Config
2332 connstr = "postgres://%s" % cnf["DB::Host"]
2333 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2334 connstr += ":%s" % cnf["DB::Port"]
2335 connstr += "/%s" % cnf["DB::Name"]
2338 connstr = "postgres:///%s" % cnf["DB::Name"]
2339 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2340 connstr += "?port=%s" % cnf["DB::Port"]
2342 self.db_pg = create_engine(connstr, echo=self.debug)
2343 self.db_meta = MetaData()
2344 self.db_meta.bind = self.db_pg
2345 self.db_smaker = sessionmaker(bind=self.db_pg,
2349 self.__setuptables()
2350 self.__setupmappers()
2353 return self.db_smaker()
2355 __all__.append('DBConn')