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
76 session = kwargs.get('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')
444 def get_or_set_contents_file_id(filename, session=None):
446 Returns database id for given filename.
448 If no matching file is found, a row is inserted.
450 @type filename: string
451 @param filename: The filename
452 @type session: SQLAlchemy
453 @param session: Optional SQL session object (a temporary one will be
454 generated if not supplied). If not passed, a commit will be performed at
455 the end of the function, otherwise the caller is responsible for commiting.
458 @return: the database id for the given component
461 q = session.query(ContentFilename).filter_by(filename=filename)
464 ret = q.one().cafilename_id
465 except NoResultFound:
466 cf = ContentFilename()
467 cf.filename = filename
469 session.commit_or_flush()
470 ret = cf.cafilename_id
474 __all__.append('get_or_set_contents_file_id')
477 def get_contents(suite, overridetype, section=None, session=None):
479 Returns contents for a suite / overridetype combination, limiting
480 to a section if not None.
483 @param suite: Suite object
485 @type overridetype: OverrideType
486 @param overridetype: OverrideType object
488 @type section: Section
489 @param section: Optional section object to limit results to
491 @type session: SQLAlchemy
492 @param session: Optional SQL session object (a temporary one will be
493 generated if not supplied)
496 @return: ResultsProxy object set up to return tuples of (filename, section,
500 # find me all of the contents for a given suite
501 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
505 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
506 JOIN content_file_names n ON (c.filename=n.id)
507 JOIN binaries b ON (b.id=c.binary_pkg)
508 JOIN override o ON (o.package=b.package)
509 JOIN section s ON (s.id=o.section)
510 WHERE o.suite = :suiteid AND o.type = :overridetypeid
511 AND b.type=:overridetypename"""
513 vals = {'suiteid': suite.suite_id,
514 'overridetypeid': overridetype.overridetype_id,
515 'overridetypename': overridetype.overridetype}
517 if section is not None:
518 contents_q += " AND s.id = :sectionid"
519 vals['sectionid'] = section.section_id
521 contents_q += " ORDER BY fn"
523 return session.execute(contents_q, vals)
525 __all__.append('get_contents')
527 ################################################################################
529 class ContentFilepath(object):
530 def __init__(self, *args, **kwargs):
534 return '<ContentFilepath %s>' % self.filepath
536 __all__.append('ContentFilepath')
539 def get_or_set_contents_path_id(filepath, session=None):
541 Returns database id for given path.
543 If no matching file is found, a row is inserted.
545 @type filename: string
546 @param filename: The filepath
547 @type session: SQLAlchemy
548 @param session: Optional SQL session object (a temporary one will be
549 generated if not supplied). If not passed, a commit will be performed at
550 the end of the function, otherwise the caller is responsible for commiting.
553 @return: the database id for the given path
556 q = session.query(ContentFilepath).filter_by(filepath=filepath)
559 ret = q.one().cafilepath_id
560 except NoResultFound:
561 cf = ContentFilepath()
562 cf.filepath = filepath
564 session.commit_or_flush()
565 ret = cf.cafilepath_id
569 __all__.append('get_or_set_contents_path_id')
571 ################################################################################
573 class ContentAssociation(object):
574 def __init__(self, *args, **kwargs):
578 return '<ContentAssociation %s>' % self.ca_id
580 __all__.append('ContentAssociation')
582 def insert_content_paths(binary_id, fullpaths, session=None):
584 Make sure given path is associated with given binary id
587 @param binary_id: the id of the binary
588 @type fullpaths: list
589 @param fullpaths: the list of paths of the file being associated with the binary
590 @type session: SQLAlchemy session
591 @param session: Optional SQLAlchemy session. If this is passed, the caller
592 is responsible for ensuring a transaction has begun and committing the
593 results or rolling back based on the result code. If not passed, a commit
594 will be performed at the end of the function, otherwise the caller is
595 responsible for commiting.
597 @return: True upon success
602 session = DBConn().session()
608 for fullpath in fullpaths:
609 # Get the necessary IDs ...
610 (path, file) = os.path.split(fullpath)
612 filepath_id = get_or_set_contents_path_id(path, session)
613 filename_id = get_or_set_contents_file_id(file, session)
615 pathcache[fullpath] = (filepath_id, filename_id)
617 for fullpath, dat in pathcache.items():
618 ca = ContentAssociation()
619 ca.binary_id = binary_id
620 ca.filepath_id = dat[0]
621 ca.filename_id = dat[1]
624 # Only commit if we set up the session ourself
634 traceback.print_exc()
636 # Only rollback if we set up the session ourself
643 __all__.append('insert_content_paths')
645 ################################################################################
647 class DSCFile(object):
648 def __init__(self, *args, **kwargs):
652 return '<DSCFile %s>' % self.dscfile_id
654 __all__.append('DSCFile')
657 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
659 Returns a list of DSCFiles which may be empty
661 @type dscfile_id: int (optional)
662 @param dscfile_id: the dscfile_id of the DSCFiles to find
664 @type source_id: int (optional)
665 @param source_id: the source id related to the DSCFiles to find
667 @type poolfile_id: int (optional)
668 @param poolfile_id: the poolfile id related to the DSCFiles to find
671 @return: Possibly empty list of DSCFiles
674 q = session.query(DSCFile)
676 if dscfile_id is not None:
677 q = q.filter_by(dscfile_id=dscfile_id)
679 if source_id is not None:
680 q = q.filter_by(source_id=source_id)
682 if poolfile_id is not None:
683 q = q.filter_by(poolfile_id=poolfile_id)
687 __all__.append('get_dscfiles')
689 ################################################################################
691 class PoolFile(object):
692 def __init__(self, *args, **kwargs):
696 return '<PoolFile %s>' % self.filename
698 __all__.append('PoolFile')
701 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
704 (ValidFileFound [boolean or None], PoolFile object or None)
706 @type filename: string
707 @param filename: the filename of the file to check against the DB
710 @param filesize: the size of the file to check against the DB
713 @param md5sum: the md5sum of the file to check against the DB
715 @type location_id: int
716 @param location_id: the id of the location to look in
719 @return: Tuple of length 2.
720 If more than one file found with that name:
722 If valid pool file found: (True, PoolFile object)
723 If valid pool file not found:
724 (False, None) if no file found
725 (False, PoolFile object) if file found with size/md5sum mismatch
728 q = session.query(PoolFile).filter_by(filename=filename)
729 q = q.join(Location).filter_by(location_id=location_id)
739 if obj.md5sum != md5sum or obj.filesize != filesize:
747 __all__.append('check_poolfile')
750 def get_poolfile_by_id(file_id, session=None):
752 Returns a PoolFile objects or None for the given id
755 @param file_id: the id of the file to look for
757 @rtype: PoolFile or None
758 @return: either the PoolFile object or None
761 q = session.query(PoolFile).filter_by(file_id=file_id)
765 except NoResultFound:
768 __all__.append('get_poolfile_by_id')
772 def get_poolfile_by_name(filename, location_id=None, session=None):
774 Returns an array of PoolFile objects for the given filename and
775 (optionally) location_id
777 @type filename: string
778 @param filename: the filename of the file to check against the DB
780 @type location_id: int
781 @param location_id: the id of the location to look in (optional)
784 @return: array of PoolFile objects
787 q = session.query(PoolFile).filter_by(filename=filename)
789 if location_id is not None:
790 q = q.join(Location).filter_by(location_id=location_id)
794 __all__.append('get_poolfile_by_name')
797 def get_poolfile_like_name(filename, session=None):
799 Returns an array of PoolFile objects which are like the given name
801 @type filename: string
802 @param filename: the filename of the file to check against the DB
805 @return: array of PoolFile objects
808 # TODO: There must be a way of properly using bind parameters with %FOO%
809 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
813 __all__.append('get_poolfile_like_name')
815 ################################################################################
817 class Fingerprint(object):
818 def __init__(self, *args, **kwargs):
822 return '<Fingerprint %s>' % self.fingerprint
824 __all__.append('Fingerprint')
827 def get_or_set_fingerprint(fpr, session=None):
829 Returns Fingerprint object for given fpr.
831 If no matching fpr is found, a row is inserted.
834 @param fpr: The fpr to find / add
836 @type session: SQLAlchemy
837 @param session: Optional SQL session object (a temporary one will be
838 generated if not supplied). If not passed, a commit will be performed at
839 the end of the function, otherwise the caller is responsible for commiting.
840 A flush will be performed either way.
843 @return: the Fingerprint object for the given fpr
846 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
850 except NoResultFound:
851 fingerprint = Fingerprint()
852 fingerprint.fingerprint = fpr
853 session.add(fingerprint)
854 session.commit_or_flush()
859 __all__.append('get_or_set_fingerprint')
861 ################################################################################
863 class Keyring(object):
864 def __init__(self, *args, **kwargs):
868 return '<Keyring %s>' % self.keyring_name
870 __all__.append('Keyring')
873 def get_or_set_keyring(keyring, session=None):
875 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
876 and return the new Keyring
877 If C{keyring} already has an entry, simply return the existing Keyring
879 @type keyring: string
880 @param keyring: the keyring name
883 @return: the Keyring object for this keyring
886 q = session.query(Keyring).filter_by(keyring_name=keyring)
890 except NoResultFound:
891 obj = Keyring(keyring_name=keyring)
893 session.commit_or_flush()
896 __all__.append('get_or_set_keyring')
898 ################################################################################
900 class Location(object):
901 def __init__(self, *args, **kwargs):
905 return '<Location %s (%s)>' % (self.path, self.location_id)
907 __all__.append('Location')
910 def get_location(location, component=None, archive=None, session=None):
912 Returns Location object for the given combination of location, component
915 @type location: string
916 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
918 @type component: string
919 @param component: the component name (if None, no restriction applied)
921 @type archive: string
922 @param archive_id: the archive name (if None, no restriction applied)
924 @rtype: Location / None
925 @return: Either a Location object or None if one can't be found
928 q = session.query(Location).filter_by(path=location)
930 if archive is not None:
931 q = q.join(Archive).filter_by(archive_name=archive)
933 if component is not None:
934 q = q.join(Component).filter_by(component_name=component)
938 except NoResultFound:
941 __all__.append('get_location')
943 ################################################################################
945 class Maintainer(object):
946 def __init__(self, *args, **kwargs):
950 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
952 def get_split_maintainer(self):
953 if not hasattr(self, 'name') or self.name is None:
954 return ('', '', '', '')
956 return fix_maintainer(self.name.strip())
958 __all__.append('Maintainer')
961 def get_or_set_maintainer(name, session=None):
963 Returns Maintainer object for given maintainer name.
965 If no matching maintainer name is found, a row is inserted.
968 @param name: The maintainer name to add
970 @type session: SQLAlchemy
971 @param session: Optional SQL session object (a temporary one will be
972 generated if not supplied). If not passed, a commit will be performed at
973 the end of the function, otherwise the caller is responsible for commiting.
974 A flush will be performed either way.
977 @return: the Maintainer object for the given maintainer
980 q = session.query(Maintainer).filter_by(name=name)
983 except NoResultFound:
984 maintainer = Maintainer()
985 maintainer.name = name
986 session.add(maintainer)
987 session.commit_or_flush()
992 __all__.append('get_or_set_maintainer')
995 def get_maintainer(maintainer_id, session=None):
997 Return the name of the maintainer behind C{maintainer_id} or None if that
998 maintainer_id is invalid.
1000 @type maintainer_id: int
1001 @param maintainer_id: the id of the maintainer
1004 @return: the Maintainer with this C{maintainer_id}
1007 return session.query(Maintainer).get(maintainer_id)
1009 __all__.append('get_maintainer')
1011 ################################################################################
1013 class NewComment(object):
1014 def __init__(self, *args, **kwargs):
1018 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1020 __all__.append('NewComment')
1023 def has_new_comment(package, version, session=None):
1025 Returns true if the given combination of C{package}, C{version} has a comment.
1027 @type package: string
1028 @param package: name of the package
1030 @type version: string
1031 @param version: package version
1033 @type session: Session
1034 @param session: Optional SQLA session object (a temporary one will be
1035 generated if not supplied)
1041 q = session.query(NewComment)
1042 q = q.filter_by(package=package)
1043 q = q.filter_by(version=version)
1045 return bool(q.count() > 0)
1047 __all__.append('has_new_comment')
1050 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1052 Returns (possibly empty) list of NewComment objects for the given
1055 @type package: string (optional)
1056 @param package: name of the package
1058 @type version: string (optional)
1059 @param version: package version
1061 @type comment_id: int (optional)
1062 @param comment_id: An id of a comment
1064 @type session: Session
1065 @param session: Optional SQLA session object (a temporary one will be
1066 generated if not supplied)
1069 @return: A (possibly empty) list of NewComment objects will be returned
1072 q = session.query(NewComment)
1073 if package is not None: q = q.filter_by(package=package)
1074 if version is not None: q = q.filter_by(version=version)
1075 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1079 __all__.append('get_new_comments')
1081 ################################################################################
1083 class Override(object):
1084 def __init__(self, *args, **kwargs):
1088 return '<Override %s (%s)>' % (self.package, self.suite_id)
1090 __all__.append('Override')
1093 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1095 Returns Override object for the given parameters
1097 @type package: string
1098 @param package: The name of the package
1100 @type suite: string, list or None
1101 @param suite: The name of the suite (or suites if a list) to limit to. If
1102 None, don't limit. Defaults to None.
1104 @type component: string, list or None
1105 @param component: The name of the component (or components if a list) to
1106 limit to. If None, don't limit. Defaults to None.
1108 @type overridetype: string, list or None
1109 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1110 limit to. If None, don't limit. Defaults to None.
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 Override objects will be returned
1120 q = session.query(Override)
1121 q = q.filter_by(package=package)
1123 if suite is not None:
1124 if not isinstance(suite, list): suite = [suite]
1125 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1127 if component is not None:
1128 if not isinstance(component, list): component = [component]
1129 q = q.join(Component).filter(Component.component_name.in_(component))
1131 if overridetype is not None:
1132 if not isinstance(overridetype, list): overridetype = [overridetype]
1133 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1137 __all__.append('get_override')
1140 ################################################################################
1142 class OverrideType(object):
1143 def __init__(self, *args, **kwargs):
1147 return '<OverrideType %s>' % self.overridetype
1149 __all__.append('OverrideType')
1152 def get_override_type(override_type, session=None):
1154 Returns OverrideType object for given C{override type}.
1156 @type override_type: string
1157 @param override_type: The name of the override type
1159 @type session: Session
1160 @param session: Optional SQLA session object (a temporary one will be
1161 generated if not supplied)
1164 @return: the database id for the given override type
1167 q = session.query(OverrideType).filter_by(overridetype=override_type)
1171 except NoResultFound:
1174 __all__.append('get_override_type')
1176 ################################################################################
1178 class PendingContentAssociation(object):
1179 def __init__(self, *args, **kwargs):
1183 return '<PendingContentAssociation %s>' % self.pca_id
1185 __all__.append('PendingContentAssociation')
1187 def insert_pending_content_paths(package, fullpaths, session=None):
1189 Make sure given paths are temporarily associated with given
1193 @param package: the package to associate with should have been read in from the binary control file
1194 @type fullpaths: list
1195 @param fullpaths: the list of paths of the file being associated with the binary
1196 @type session: SQLAlchemy session
1197 @param session: Optional SQLAlchemy session. If this is passed, the caller
1198 is responsible for ensuring a transaction has begun and committing the
1199 results or rolling back based on the result code. If not passed, a commit
1200 will be performed at the end of the function
1202 @return: True upon success, False if there is a problem
1205 privatetrans = False
1208 session = DBConn().session()
1212 arch = get_architecture(package['Architecture'], session)
1213 arch_id = arch.arch_id
1215 # Remove any already existing recorded files for this package
1216 q = session.query(PendingContentAssociation)
1217 q = q.filter_by(package=package['Package'])
1218 q = q.filter_by(version=package['Version'])
1219 q = q.filter_by(architecture=arch_id)
1224 for fullpath in fullpaths:
1225 (path, file) = os.path.split(fullpath)
1227 if path.startswith( "./" ):
1230 filepath_id = get_or_set_contents_path_id(path, session)
1231 filename_id = get_or_set_contents_file_id(file, session)
1233 pathcache[fullpath] = (filepath_id, filename_id)
1235 for fullpath, dat in pathcache.items():
1236 pca = PendingContentAssociation()
1237 pca.package = package['Package']
1238 pca.version = package['Version']
1239 pca.filepath_id = dat[0]
1240 pca.filename_id = dat[1]
1241 pca.architecture = arch_id
1244 # Only commit if we set up the session ourself
1252 except Exception, e:
1253 traceback.print_exc()
1255 # Only rollback if we set up the session ourself
1262 __all__.append('insert_pending_content_paths')
1264 ################################################################################
1266 class Priority(object):
1267 def __init__(self, *args, **kwargs):
1270 def __eq__(self, val):
1271 if isinstance(val, str):
1272 return (self.priority == val)
1273 # This signals to use the normal comparison operator
1274 return NotImplemented
1276 def __ne__(self, val):
1277 if isinstance(val, str):
1278 return (self.priority != val)
1279 # This signals to use the normal comparison operator
1280 return NotImplemented
1283 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1285 __all__.append('Priority')
1288 def get_priority(priority, session=None):
1290 Returns Priority object for given C{priority name}.
1292 @type priority: string
1293 @param priority: The name of the priority
1295 @type session: Session
1296 @param session: Optional SQLA session object (a temporary one will be
1297 generated if not supplied)
1300 @return: Priority object for the given priority
1303 q = session.query(Priority).filter_by(priority=priority)
1307 except NoResultFound:
1310 __all__.append('get_priority')
1313 def get_priorities(session=None):
1315 Returns dictionary of priority names -> id mappings
1317 @type session: Session
1318 @param session: Optional SQL session object (a temporary one will be
1319 generated if not supplied)
1322 @return: dictionary of priority names -> id mappings
1326 q = session.query(Priority)
1328 ret[x.priority] = x.priority_id
1332 __all__.append('get_priorities')
1334 ################################################################################
1336 class Queue(object):
1337 def __init__(self, *args, **kwargs):
1341 return '<Queue %s>' % self.queue_name
1343 def autobuild_upload(self, changes, srcpath, session=None):
1345 Update queue_build database table used for incoming autobuild support.
1347 @type changes: Changes
1348 @param changes: changes object for the upload to process
1350 @type srcpath: string
1351 @param srcpath: path for the queue file entries/link destinations
1353 @type session: SQLAlchemy session
1354 @param session: Optional SQLAlchemy session. If this is passed, the
1355 caller is responsible for ensuring a transaction has begun and
1356 committing the results or rolling back based on the result code. If
1357 not passed, a commit will be performed at the end of the function,
1358 otherwise the caller is responsible for commiting.
1360 @rtype: NoneType or string
1361 @return: None if the operation failed, a string describing the error if not
1364 privatetrans = False
1366 session = DBConn().session()
1369 # TODO: Remove by moving queue config into the database
1372 for suitename in changes.changes["distribution"].keys():
1373 # TODO: Move into database as:
1374 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1375 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1376 # This also gets rid of the SecurityQueueBuild hack below
1377 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1381 s = get_suite(suitename, session)
1383 return "INTERNAL ERROR: Could not find suite %s" % suitename
1385 # TODO: Get from database as above
1386 dest_dir = conf["Dir::QueueBuild"]
1388 # TODO: Move into database as above
1389 if conf.FindB("Dinstall::SecurityQueueBuild"):
1390 dest_dir = os.path.join(dest_dir, suitename)
1392 for file_entry in changes.files.keys():
1393 src = os.path.join(srcpath, file_entry)
1394 dest = os.path.join(dest_dir, file_entry)
1396 # TODO: Move into database as above
1397 if conf.FindB("Dinstall::SecurityQueueBuild"):
1398 # Copy it since the original won't be readable by www-data
1400 utils.copy(src, dest)
1402 # Create a symlink to it
1403 os.symlink(src, dest)
1406 qb.suite_id = s.suite_id
1407 qb.queue_id = self.queue_id
1413 # If the .orig tarballs are in the pool, create a symlink to
1414 # them (if one doesn't already exist)
1415 for dsc_file in changes.dsc_files.keys():
1416 # Skip all files except orig tarballs
1417 if not re_is_orig_source.match(dsc_file):
1419 # Skip orig files not identified in the pool
1420 if not (changes.orig_files.has_key(dsc_file) and
1421 changes.orig_files[dsc_file].has_key("id")):
1423 orig_file_id = changes.orig_files[dsc_file]["id"]
1424 dest = os.path.join(dest_dir, dsc_file)
1426 # If it doesn't exist, create a symlink
1427 if not os.path.exists(dest):
1428 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1429 {'id': orig_file_id})
1432 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1434 src = os.path.join(res[0], res[1])
1435 os.symlink(src, dest)
1437 # Add it to the list of packages for later processing by apt-ftparchive
1439 qb.suite_id = s.suite_id
1440 qb.queue_id = self.queue_id
1445 # If it does, update things to ensure it's not removed prematurely
1447 qb = get_queue_build(dest, s.suite_id, session)
1459 __all__.append('Queue')
1462 def get_queue(queuename, session=None):
1464 Returns Queue object for given C{queue name}.
1466 @type queuename: string
1467 @param queuename: The name of the queue
1469 @type session: Session
1470 @param session: Optional SQLA session object (a temporary one will be
1471 generated if not supplied)
1474 @return: Queue object for the given queue
1477 q = session.query(Queue).filter_by(queue_name=queuename)
1481 except NoResultFound:
1484 __all__.append('get_queue')
1486 ################################################################################
1488 class QueueBuild(object):
1489 def __init__(self, *args, **kwargs):
1493 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1495 __all__.append('QueueBuild')
1498 def get_queue_build(filename, suite, session=None):
1500 Returns QueueBuild object for given C{filename} and C{suite}.
1502 @type filename: string
1503 @param filename: The name of the file
1505 @type suiteid: int or str
1506 @param suiteid: Suite name or ID
1508 @type session: Session
1509 @param session: Optional SQLA session object (a temporary one will be
1510 generated if not supplied)
1513 @return: Queue object for the given queue
1516 if isinstance(suite, int):
1517 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1519 q = session.query(QueueBuild).filter_by(filename=filename)
1520 q = q.join(Suite).filter_by(suite_name=suite)
1524 except NoResultFound:
1527 __all__.append('get_queue_build')
1529 ################################################################################
1531 class Section(object):
1532 def __init__(self, *args, **kwargs):
1535 def __eq__(self, val):
1536 if isinstance(val, str):
1537 return (self.section == val)
1538 # This signals to use the normal comparison operator
1539 return NotImplemented
1541 def __ne__(self, val):
1542 if isinstance(val, str):
1543 return (self.section != val)
1544 # This signals to use the normal comparison operator
1545 return NotImplemented
1548 return '<Section %s>' % self.section
1550 __all__.append('Section')
1553 def get_section(section, session=None):
1555 Returns Section object for given C{section name}.
1557 @type section: string
1558 @param section: The name of the section
1560 @type session: Session
1561 @param session: Optional SQLA session object (a temporary one will be
1562 generated if not supplied)
1565 @return: Section object for the given section name
1568 q = session.query(Section).filter_by(section=section)
1572 except NoResultFound:
1575 __all__.append('get_section')
1578 def get_sections(session=None):
1580 Returns dictionary of section names -> id mappings
1582 @type session: Session
1583 @param session: Optional SQL session object (a temporary one will be
1584 generated if not supplied)
1587 @return: dictionary of section names -> id mappings
1591 q = session.query(Section)
1593 ret[x.section] = x.section_id
1597 __all__.append('get_sections')
1599 ################################################################################
1601 class DBSource(object):
1602 def __init__(self, *args, **kwargs):
1606 return '<DBSource %s (%s)>' % (self.source, self.version)
1608 __all__.append('DBSource')
1611 def source_exists(source, source_version, suites = ["any"], session=None):
1613 Ensure that source exists somewhere in the archive for the binary
1614 upload being processed.
1615 1. exact match => 1.0-3
1616 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1618 @type package: string
1619 @param package: package source name
1621 @type source_version: string
1622 @param source_version: expected source version
1625 @param suites: list of suites to check in, default I{any}
1627 @type session: Session
1628 @param session: Optional SQLA session object (a temporary one will be
1629 generated if not supplied)
1632 @return: returns 1 if a source with expected version is found, otherwise 0
1639 for suite in suites:
1640 q = session.query(DBSource).filter_by(source=source)
1642 # source must exist in suite X, or in some other suite that's
1643 # mapped to X, recursively... silent-maps are counted too,
1644 # unreleased-maps aren't.
1645 maps = cnf.ValueList("SuiteMappings")[:]
1647 maps = [ m.split() for m in maps ]
1648 maps = [ (x[1], x[2]) for x in maps
1649 if x[0] == "map" or x[0] == "silent-map" ]
1652 if x[1] in s and x[0] not in s:
1655 q = q.join(SrcAssociation).join(Suite)
1656 q = q.filter(Suite.suite_name.in_(s))
1658 # Reduce the query results to a list of version numbers
1659 ql = [ j.version for j in q.all() ]
1662 if source_version in ql:
1666 from daklib.regexes import re_bin_only_nmu
1667 orig_source_version = re_bin_only_nmu.sub('', source_version)
1668 if orig_source_version in ql:
1671 # No source found so return not ok
1676 __all__.append('source_exists')
1679 def get_suites_source_in(source, session=None):
1681 Returns list of Suite objects which given C{source} name is in
1684 @param source: DBSource package name to search for
1687 @return: list of Suite objects for the given source
1690 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1692 __all__.append('get_suites_source_in')
1695 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1697 Returns list of DBSource objects for given C{source} name and other parameters
1700 @param source: DBSource package name to search for
1702 @type source: str or None
1703 @param source: DBSource version name to search for or None if not applicable
1705 @type dm_upload_allowed: bool
1706 @param dm_upload_allowed: If None, no effect. If True or False, only
1707 return packages with that dm_upload_allowed setting
1709 @type session: Session
1710 @param session: Optional SQL session object (a temporary one will be
1711 generated if not supplied)
1714 @return: list of DBSource objects for the given name (may be empty)
1717 q = session.query(DBSource).filter_by(source=source)
1719 if version is not None:
1720 q = q.filter_by(version=version)
1722 if dm_upload_allowed is not None:
1723 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1727 __all__.append('get_sources_from_name')
1730 def get_source_in_suite(source, suite, session=None):
1732 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1734 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1735 - B{suite} - a suite name, eg. I{unstable}
1737 @type source: string
1738 @param source: source package name
1741 @param suite: the suite name
1744 @return: the version for I{source} in I{suite}
1748 q = session.query(SrcAssociation)
1749 q = q.join('source').filter_by(source=source)
1750 q = q.join('suite').filter_by(suite_name=suite)
1753 return q.one().source
1754 except NoResultFound:
1757 __all__.append('get_source_in_suite')
1759 ################################################################################
1761 class SrcAssociation(object):
1762 def __init__(self, *args, **kwargs):
1766 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1768 __all__.append('SrcAssociation')
1770 ################################################################################
1772 class SrcFormat(object):
1773 def __init__(self, *args, **kwargs):
1777 return '<SrcFormat %s>' % (self.format_name)
1779 __all__.append('SrcFormat')
1781 ################################################################################
1783 class SrcUploader(object):
1784 def __init__(self, *args, **kwargs):
1788 return '<SrcUploader %s>' % self.uploader_id
1790 __all__.append('SrcUploader')
1792 ################################################################################
1794 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1795 ('SuiteID', 'suite_id'),
1796 ('Version', 'version'),
1797 ('Origin', 'origin'),
1799 ('Description', 'description'),
1800 ('Untouchable', 'untouchable'),
1801 ('Announce', 'announce'),
1802 ('Codename', 'codename'),
1803 ('OverrideCodename', 'overridecodename'),
1804 ('ValidTime', 'validtime'),
1805 ('Priority', 'priority'),
1806 ('NotAutomatic', 'notautomatic'),
1807 ('CopyChanges', 'copychanges'),
1808 ('CopyDotDak', 'copydotdak'),
1809 ('CommentsDir', 'commentsdir'),
1810 ('OverrideSuite', 'overridesuite'),
1811 ('ChangelogBase', 'changelogbase')]
1814 class Suite(object):
1815 def __init__(self, *args, **kwargs):
1819 return '<Suite %s>' % self.suite_name
1821 def __eq__(self, val):
1822 if isinstance(val, str):
1823 return (self.suite_name == val)
1824 # This signals to use the normal comparison operator
1825 return NotImplemented
1827 def __ne__(self, val):
1828 if isinstance(val, str):
1829 return (self.suite_name != val)
1830 # This signals to use the normal comparison operator
1831 return NotImplemented
1835 for disp, field in SUITE_FIELDS:
1836 val = getattr(self, field, None)
1838 ret.append("%s: %s" % (disp, val))
1840 return "\n".join(ret)
1842 __all__.append('Suite')
1845 def get_suite_architecture(suite, architecture, session=None):
1847 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1851 @param suite: Suite name to search for
1853 @type architecture: str
1854 @param architecture: Architecture name to search for
1856 @type session: Session
1857 @param session: Optional SQL session object (a temporary one will be
1858 generated if not supplied)
1860 @rtype: SuiteArchitecture
1861 @return: the SuiteArchitecture object or None
1864 q = session.query(SuiteArchitecture)
1865 q = q.join(Architecture).filter_by(arch_string=architecture)
1866 q = q.join(Suite).filter_by(suite_name=suite)
1870 except NoResultFound:
1873 __all__.append('get_suite_architecture')
1876 def get_suite(suite, session=None):
1878 Returns Suite object for given C{suite name}.
1881 @param suite: The name of the suite
1883 @type session: Session
1884 @param session: Optional SQLA session object (a temporary one will be
1885 generated if not supplied)
1888 @return: Suite object for the requested suite name (None if not present)
1891 q = session.query(Suite).filter_by(suite_name=suite)
1895 except NoResultFound:
1898 __all__.append('get_suite')
1900 ################################################################################
1902 class SuiteArchitecture(object):
1903 def __init__(self, *args, **kwargs):
1907 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1909 __all__.append('SuiteArchitecture')
1912 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1914 Returns list of Architecture objects for given C{suite} name
1917 @param source: Suite name to search for
1919 @type skipsrc: boolean
1920 @param skipsrc: Whether to skip returning the 'source' architecture entry
1923 @type skipall: boolean
1924 @param skipall: Whether to skip returning the 'all' architecture entry
1927 @type session: Session
1928 @param session: Optional SQL session object (a temporary one will be
1929 generated if not supplied)
1932 @return: list of Architecture objects for the given name (may be empty)
1935 q = session.query(Architecture)
1936 q = q.join(SuiteArchitecture)
1937 q = q.join(Suite).filter_by(suite_name=suite)
1940 q = q.filter(Architecture.arch_string != 'source')
1943 q = q.filter(Architecture.arch_string != 'all')
1945 q = q.order_by('arch_string')
1949 __all__.append('get_suite_architectures')
1951 ################################################################################
1953 class SuiteSrcFormat(object):
1954 def __init__(self, *args, **kwargs):
1958 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1960 __all__.append('SuiteSrcFormat')
1963 def get_suite_src_formats(suite, session=None):
1965 Returns list of allowed SrcFormat for C{suite}.
1968 @param suite: Suite name to search for
1970 @type session: Session
1971 @param session: Optional SQL session object (a temporary one will be
1972 generated if not supplied)
1975 @return: the list of allowed source formats for I{suite}
1978 q = session.query(SrcFormat)
1979 q = q.join(SuiteSrcFormat)
1980 q = q.join(Suite).filter_by(suite_name=suite)
1981 q = q.order_by('format_name')
1985 __all__.append('get_suite_src_formats')
1987 ################################################################################
1990 def __init__(self, *args, **kwargs):
1993 def __eq__(self, val):
1994 if isinstance(val, str):
1995 return (self.uid == val)
1996 # This signals to use the normal comparison operator
1997 return NotImplemented
1999 def __ne__(self, val):
2000 if isinstance(val, str):
2001 return (self.uid != val)
2002 # This signals to use the normal comparison operator
2003 return NotImplemented
2006 return '<Uid %s (%s)>' % (self.uid, self.name)
2008 __all__.append('Uid')
2011 def add_database_user(uidname, session=None):
2013 Adds a database user
2015 @type uidname: string
2016 @param uidname: The uid of the user to add
2018 @type session: SQLAlchemy
2019 @param session: Optional SQL session object (a temporary one will be
2020 generated if not supplied). If not passed, a commit will be performed at
2021 the end of the function, otherwise the caller is responsible for commiting.
2024 @return: the uid object for the given uidname
2027 session.execute("CREATE USER :uid", {'uid': uidname})
2028 session.commit_or_flush()
2030 __all__.append('add_database_user')
2033 def get_or_set_uid(uidname, session=None):
2035 Returns uid object for given uidname.
2037 If no matching uidname is found, a row is inserted.
2039 @type uidname: string
2040 @param uidname: The uid to add
2042 @type session: SQLAlchemy
2043 @param session: Optional SQL session object (a temporary one will be
2044 generated if not supplied). If not passed, a commit will be performed at
2045 the end of the function, otherwise the caller is responsible for commiting.
2048 @return: the uid object for the given uidname
2051 q = session.query(Uid).filter_by(uid=uidname)
2055 except NoResultFound:
2059 session.commit_or_flush()
2064 __all__.append('get_or_set_uid')
2067 def get_uid_from_fingerprint(fpr, session=None):
2068 q = session.query(Uid)
2069 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2073 except NoResultFound:
2076 __all__.append('get_uid_from_fingerprint')
2078 ################################################################################
2080 class DBConn(Singleton):
2082 database module init.
2084 def __init__(self, *args, **kwargs):
2085 super(DBConn, self).__init__(*args, **kwargs)
2087 def _startup(self, *args, **kwargs):
2089 if kwargs.has_key('debug'):
2093 def __setuptables(self):
2094 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2095 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2096 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2097 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2098 self.tbl_component = Table('component', self.db_meta, autoload=True)
2099 self.tbl_config = Table('config', self.db_meta, autoload=True)
2100 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2101 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2102 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2103 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2104 self.tbl_files = Table('files', self.db_meta, autoload=True)
2105 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2106 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2107 self.tbl_location = Table('location', self.db_meta, autoload=True)
2108 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2109 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2110 self.tbl_override = Table('override', self.db_meta, autoload=True)
2111 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2112 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2113 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2114 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2115 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2116 self.tbl_section = Table('section', self.db_meta, autoload=True)
2117 self.tbl_source = Table('source', self.db_meta, autoload=True)
2118 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2119 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2120 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2121 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2122 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2123 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2124 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2126 def __setupmappers(self):
2127 mapper(Architecture, self.tbl_architecture,
2128 properties = dict(arch_id = self.tbl_architecture.c.id))
2130 mapper(Archive, self.tbl_archive,
2131 properties = dict(archive_id = self.tbl_archive.c.id,
2132 archive_name = self.tbl_archive.c.name))
2134 mapper(BinAssociation, self.tbl_bin_associations,
2135 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2136 suite_id = self.tbl_bin_associations.c.suite,
2137 suite = relation(Suite),
2138 binary_id = self.tbl_bin_associations.c.bin,
2139 binary = relation(DBBinary)))
2141 mapper(DBBinary, self.tbl_binaries,
2142 properties = dict(binary_id = self.tbl_binaries.c.id,
2143 package = self.tbl_binaries.c.package,
2144 version = self.tbl_binaries.c.version,
2145 maintainer_id = self.tbl_binaries.c.maintainer,
2146 maintainer = relation(Maintainer),
2147 source_id = self.tbl_binaries.c.source,
2148 source = relation(DBSource),
2149 arch_id = self.tbl_binaries.c.architecture,
2150 architecture = relation(Architecture),
2151 poolfile_id = self.tbl_binaries.c.file,
2152 poolfile = relation(PoolFile),
2153 binarytype = self.tbl_binaries.c.type,
2154 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2155 fingerprint = relation(Fingerprint),
2156 install_date = self.tbl_binaries.c.install_date,
2157 binassociations = relation(BinAssociation,
2158 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2160 mapper(Component, self.tbl_component,
2161 properties = dict(component_id = self.tbl_component.c.id,
2162 component_name = self.tbl_component.c.name))
2164 mapper(DBConfig, self.tbl_config,
2165 properties = dict(config_id = self.tbl_config.c.id))
2167 mapper(ContentAssociation, self.tbl_content_associations,
2168 properties = dict(ca_id = self.tbl_content_associations.c.id,
2169 filename_id = self.tbl_content_associations.c.filename,
2170 filename = relation(ContentFilename),
2171 filepath_id = self.tbl_content_associations.c.filepath,
2172 filepath = relation(ContentFilepath),
2173 binary_id = self.tbl_content_associations.c.binary_pkg,
2174 binary = relation(DBBinary)))
2177 mapper(ContentFilename, self.tbl_content_file_names,
2178 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2179 filename = self.tbl_content_file_names.c.file))
2181 mapper(ContentFilepath, self.tbl_content_file_paths,
2182 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2183 filepath = self.tbl_content_file_paths.c.path))
2185 mapper(DSCFile, self.tbl_dsc_files,
2186 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2187 source_id = self.tbl_dsc_files.c.source,
2188 source = relation(DBSource),
2189 poolfile_id = self.tbl_dsc_files.c.file,
2190 poolfile = relation(PoolFile)))
2192 mapper(PoolFile, self.tbl_files,
2193 properties = dict(file_id = self.tbl_files.c.id,
2194 filesize = self.tbl_files.c.size,
2195 location_id = self.tbl_files.c.location,
2196 location = relation(Location)))
2198 mapper(Fingerprint, self.tbl_fingerprint,
2199 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2200 uid_id = self.tbl_fingerprint.c.uid,
2201 uid = relation(Uid),
2202 keyring_id = self.tbl_fingerprint.c.keyring,
2203 keyring = relation(Keyring)))
2205 mapper(Keyring, self.tbl_keyrings,
2206 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2207 keyring_id = self.tbl_keyrings.c.id))
2209 mapper(Location, self.tbl_location,
2210 properties = dict(location_id = self.tbl_location.c.id,
2211 component_id = self.tbl_location.c.component,
2212 component = relation(Component),
2213 archive_id = self.tbl_location.c.archive,
2214 archive = relation(Archive),
2215 archive_type = self.tbl_location.c.type))
2217 mapper(Maintainer, self.tbl_maintainer,
2218 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2220 mapper(NewComment, self.tbl_new_comments,
2221 properties = dict(comment_id = self.tbl_new_comments.c.id))
2223 mapper(Override, self.tbl_override,
2224 properties = dict(suite_id = self.tbl_override.c.suite,
2225 suite = relation(Suite),
2226 component_id = self.tbl_override.c.component,
2227 component = relation(Component),
2228 priority_id = self.tbl_override.c.priority,
2229 priority = relation(Priority),
2230 section_id = self.tbl_override.c.section,
2231 section = relation(Section),
2232 overridetype_id = self.tbl_override.c.type,
2233 overridetype = relation(OverrideType)))
2235 mapper(OverrideType, self.tbl_override_type,
2236 properties = dict(overridetype = self.tbl_override_type.c.type,
2237 overridetype_id = self.tbl_override_type.c.id))
2239 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2240 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2241 filepath_id = self.tbl_pending_content_associations.c.filepath,
2242 filepath = relation(ContentFilepath),
2243 filename_id = self.tbl_pending_content_associations.c.filename,
2244 filename = relation(ContentFilename)))
2246 mapper(Priority, self.tbl_priority,
2247 properties = dict(priority_id = self.tbl_priority.c.id))
2249 mapper(Queue, self.tbl_queue,
2250 properties = dict(queue_id = self.tbl_queue.c.id))
2252 mapper(QueueBuild, self.tbl_queue_build,
2253 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2254 queue_id = self.tbl_queue_build.c.queue,
2255 queue = relation(Queue, backref='queuebuild')))
2257 mapper(Section, self.tbl_section,
2258 properties = dict(section_id = self.tbl_section.c.id))
2260 mapper(DBSource, self.tbl_source,
2261 properties = dict(source_id = self.tbl_source.c.id,
2262 version = self.tbl_source.c.version,
2263 maintainer_id = self.tbl_source.c.maintainer,
2264 maintainer = relation(Maintainer,
2265 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2266 poolfile_id = self.tbl_source.c.file,
2267 poolfile = relation(PoolFile),
2268 fingerprint_id = self.tbl_source.c.sig_fpr,
2269 fingerprint = relation(Fingerprint),
2270 changedby_id = self.tbl_source.c.changedby,
2271 changedby = relation(Maintainer,
2272 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2273 srcfiles = relation(DSCFile,
2274 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2275 srcassociations = relation(SrcAssociation,
2276 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2278 mapper(SrcAssociation, self.tbl_src_associations,
2279 properties = dict(sa_id = self.tbl_src_associations.c.id,
2280 suite_id = self.tbl_src_associations.c.suite,
2281 suite = relation(Suite),
2282 source_id = self.tbl_src_associations.c.source,
2283 source = relation(DBSource)))
2285 mapper(SrcFormat, self.tbl_src_format,
2286 properties = dict(src_format_id = self.tbl_src_format.c.id,
2287 format_name = self.tbl_src_format.c.format_name))
2289 mapper(SrcUploader, self.tbl_src_uploaders,
2290 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2291 source_id = self.tbl_src_uploaders.c.source,
2292 source = relation(DBSource,
2293 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2294 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2295 maintainer = relation(Maintainer,
2296 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2298 mapper(Suite, self.tbl_suite,
2299 properties = dict(suite_id = self.tbl_suite.c.id))
2301 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2302 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2303 suite = relation(Suite, backref='suitearchitectures'),
2304 arch_id = self.tbl_suite_architectures.c.architecture,
2305 architecture = relation(Architecture)))
2307 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2308 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2309 suite = relation(Suite, backref='suitesrcformats'),
2310 src_format_id = self.tbl_suite_src_formats.c.src_format,
2311 src_format = relation(SrcFormat)))
2313 mapper(Uid, self.tbl_uid,
2314 properties = dict(uid_id = self.tbl_uid.c.id,
2315 fingerprint = relation(Fingerprint)))
2317 ## Connection functions
2318 def __createconn(self):
2319 from config import Config
2323 connstr = "postgres://%s" % cnf["DB::Host"]
2324 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2325 connstr += ":%s" % cnf["DB::Port"]
2326 connstr += "/%s" % cnf["DB::Name"]
2329 connstr = "postgres:///%s" % cnf["DB::Name"]
2330 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2331 connstr += "?port=%s" % cnf["DB::Port"]
2333 self.db_pg = create_engine(connstr, echo=self.debug)
2334 self.db_meta = MetaData()
2335 self.db_meta.bind = self.db_pg
2336 self.db_smaker = sessionmaker(bind=self.db_pg,
2340 self.__setuptables()
2341 self.__setupmappers()
2344 return self.db_smaker()
2346 __all__.append('DBConn')