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
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
88 session = args[-1] = DBConn().session()
89 private_transaction = True
91 if private_transaction:
92 session.commit_or_flush = session.commit
94 session.commit_or_flush = session.flush
97 return fn(*args, **kwargs)
99 if private_transaction:
100 # We created a session; close it.
103 wrapped.__doc__ = fn.__doc__
104 wrapped.func_name = fn.func_name
108 ################################################################################
110 class Architecture(object):
111 def __init__(self, *args, **kwargs):
114 def __eq__(self, val):
115 if isinstance(val, str):
116 return (self.arch_string== val)
117 # This signals to use the normal comparison operator
118 return NotImplemented
120 def __ne__(self, val):
121 if isinstance(val, str):
122 return (self.arch_string != val)
123 # This signals to use the normal comparison operator
124 return NotImplemented
127 return '<Architecture %s>' % self.arch_string
129 __all__.append('Architecture')
132 def get_architecture(architecture, session=None):
134 Returns database id for given C{architecture}.
136 @type architecture: string
137 @param architecture: The name of the architecture
139 @type session: Session
140 @param session: Optional SQLA session object (a temporary one will be
141 generated if not supplied)
144 @return: Architecture object for the given arch (None if not present)
147 q = session.query(Architecture).filter_by(arch_string=architecture)
151 except NoResultFound:
154 __all__.append('get_architecture')
157 def get_architecture_suites(architecture, session=None):
159 Returns list of Suite objects for given C{architecture} name
162 @param source: Architecture name to search for
164 @type session: Session
165 @param session: Optional SQL session object (a temporary one will be
166 generated if not supplied)
169 @return: list of Suite objects for the given name (may be empty)
172 q = session.query(Suite)
173 q = q.join(SuiteArchitecture)
174 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
180 __all__.append('get_architecture_suites')
182 ################################################################################
184 class Archive(object):
185 def __init__(self, *args, **kwargs):
189 return '<Archive %s>' % self.archive_name
191 __all__.append('Archive')
194 def get_archive(archive, session=None):
196 returns database id for given C{archive}.
198 @type archive: string
199 @param archive: the name of the arhive
201 @type session: Session
202 @param session: Optional SQLA session object (a temporary one will be
203 generated if not supplied)
206 @return: Archive object for the given name (None if not present)
209 archive = archive.lower()
211 q = session.query(Archive).filter_by(archive_name=archive)
215 except NoResultFound:
218 __all__.append('get_archive')
220 ################################################################################
222 class BinAssociation(object):
223 def __init__(self, *args, **kwargs):
227 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
229 __all__.append('BinAssociation')
231 ################################################################################
233 class BinContents(object):
234 def __init__(self, *args, **kwargs):
238 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
240 __all__.append('BinContents')
242 ################################################################################
244 class DBBinary(object):
245 def __init__(self, *args, **kwargs):
249 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
251 __all__.append('DBBinary')
254 def get_suites_binary_in(package, session=None):
256 Returns list of Suite objects which given C{package} name is in
259 @param source: DBBinary package name to search for
262 @return: list of Suite objects for the given package
265 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
267 __all__.append('get_suites_binary_in')
270 def get_binary_from_id(id, session=None):
272 Returns DBBinary object for given C{id}
275 @param id: Id of the required binary
277 @type session: Session
278 @param session: Optional SQLA session object (a temporary one will be
279 generated if not supplied)
282 @return: DBBinary object for the given binary (None if not present)
285 q = session.query(DBBinary).filter_by(binary_id=id)
289 except NoResultFound:
292 __all__.append('get_binary_from_id')
295 def get_binaries_from_name(package, version=None, architecture=None, session=None):
297 Returns list of DBBinary objects for given C{package} name
300 @param package: DBBinary package name to search for
302 @type version: str or None
303 @param version: Version to search for (or None)
305 @type package: str, list or None
306 @param package: Architectures to limit to (or None if no limit)
308 @type session: Session
309 @param session: Optional SQL session object (a temporary one will be
310 generated if not supplied)
313 @return: list of DBBinary objects for the given name (may be empty)
316 q = session.query(DBBinary).filter_by(package=package)
318 if version is not None:
319 q = q.filter_by(version=version)
321 if architecture is not None:
322 if not isinstance(architecture, list):
323 architecture = [architecture]
324 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
330 __all__.append('get_binaries_from_name')
333 def get_binaries_from_source_id(source_id, session=None):
335 Returns list of DBBinary objects for given C{source_id}
338 @param source_id: source_id to search for
340 @type session: Session
341 @param session: Optional SQL session object (a temporary one will be
342 generated if not supplied)
345 @return: list of DBBinary objects for the given name (may be empty)
348 return session.query(DBBinary).filter_by(source_id=source_id).all()
350 __all__.append('get_binaries_from_source_id')
353 def get_binary_from_name_suite(package, suitename, session=None):
354 ### For dak examine-package
355 ### XXX: Doesn't use object API yet
357 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
358 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
359 WHERE b.package=:package
361 AND fi.location = l.id
362 AND l.component = c.id
365 AND su.suite_name=:suitename
366 ORDER BY b.version DESC"""
368 return session.execute(sql, {'package': package, 'suitename': suitename})
370 __all__.append('get_binary_from_name_suite')
373 def get_binary_components(package, suitename, arch, session=None):
374 # Check for packages that have moved from one component to another
375 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
376 WHERE b.package=:package AND s.suite_name=:suitename
377 AND (a.arch_string = :arch OR a.arch_string = 'all')
378 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
379 AND f.location = l.id
380 AND l.component = c.id
383 vals = {'package': package, 'suitename': suitename, 'arch': arch}
385 return session.execute(query, vals)
387 __all__.append('get_binary_components')
389 ################################################################################
391 class Component(object):
392 def __init__(self, *args, **kwargs):
395 def __eq__(self, val):
396 if isinstance(val, str):
397 return (self.component_name == val)
398 # This signals to use the normal comparison operator
399 return NotImplemented
401 def __ne__(self, val):
402 if isinstance(val, str):
403 return (self.component_name != val)
404 # This signals to use the normal comparison operator
405 return NotImplemented
408 return '<Component %s>' % self.component_name
411 __all__.append('Component')
414 def get_component(component, session=None):
416 Returns database id for given C{component}.
418 @type component: string
419 @param component: The name of the override type
422 @return: the database id for the given component
425 component = component.lower()
427 q = session.query(Component).filter_by(component_name=component)
431 except NoResultFound:
434 __all__.append('get_component')
436 ################################################################################
438 class DBConfig(object):
439 def __init__(self, *args, **kwargs):
443 return '<DBConfig %s>' % self.name
445 __all__.append('DBConfig')
447 ################################################################################
450 def get_or_set_contents_file_id(filename, session=None):
452 Returns database id for given filename.
454 If no matching file is found, a row is inserted.
456 @type filename: string
457 @param filename: The filename
458 @type session: SQLAlchemy
459 @param session: Optional SQL session object (a temporary one will be
460 generated if not supplied). If not passed, a commit will be performed at
461 the end of the function, otherwise the caller is responsible for commiting.
464 @return: the database id for the given component
467 q = session.query(ContentFilename).filter_by(filename=filename)
470 ret = q.one().cafilename_id
471 except NoResultFound:
472 cf = ContentFilename()
473 cf.filename = filename
475 session.commit_or_flush()
476 ret = cf.cafilename_id
480 __all__.append('get_or_set_contents_file_id')
483 def get_contents(suite, overridetype, section=None, session=None):
485 Returns contents for a suite / overridetype combination, limiting
486 to a section if not None.
489 @param suite: Suite object
491 @type overridetype: OverrideType
492 @param overridetype: OverrideType object
494 @type section: Section
495 @param section: Optional section object to limit results to
497 @type session: SQLAlchemy
498 @param session: Optional SQL session object (a temporary one will be
499 generated if not supplied)
502 @return: ResultsProxy object set up to return tuples of (filename, section,
506 # find me all of the contents for a given suite
507 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
511 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
512 JOIN content_file_names n ON (c.filename=n.id)
513 JOIN binaries b ON (b.id=c.binary_pkg)
514 JOIN override o ON (o.package=b.package)
515 JOIN section s ON (s.id=o.section)
516 WHERE o.suite = :suiteid AND o.type = :overridetypeid
517 AND b.type=:overridetypename"""
519 vals = {'suiteid': suite.suite_id,
520 'overridetypeid': overridetype.overridetype_id,
521 'overridetypename': overridetype.overridetype}
523 if section is not None:
524 contents_q += " AND s.id = :sectionid"
525 vals['sectionid'] = section.section_id
527 contents_q += " ORDER BY fn"
529 return session.execute(contents_q, vals)
531 __all__.append('get_contents')
533 ################################################################################
535 class ContentFilepath(object):
536 def __init__(self, *args, **kwargs):
540 return '<ContentFilepath %s>' % self.filepath
542 __all__.append('ContentFilepath')
545 def get_or_set_contents_path_id(filepath, session=None):
547 Returns database id for given path.
549 If no matching file is found, a row is inserted.
551 @type filename: string
552 @param filename: The filepath
553 @type session: SQLAlchemy
554 @param session: Optional SQL session object (a temporary one will be
555 generated if not supplied). If not passed, a commit will be performed at
556 the end of the function, otherwise the caller is responsible for commiting.
559 @return: the database id for the given path
562 q = session.query(ContentFilepath).filter_by(filepath=filepath)
565 ret = q.one().cafilepath_id
566 except NoResultFound:
567 cf = ContentFilepath()
568 cf.filepath = filepath
570 session.commit_or_flush()
571 ret = cf.cafilepath_id
575 __all__.append('get_or_set_contents_path_id')
577 ################################################################################
579 class ContentAssociation(object):
580 def __init__(self, *args, **kwargs):
584 return '<ContentAssociation %s>' % self.ca_id
586 __all__.append('ContentAssociation')
588 def insert_content_paths(binary_id, fullpaths, session=None):
590 Make sure given path is associated with given binary id
593 @param binary_id: the id of the binary
594 @type fullpaths: list
595 @param fullpaths: the list of paths of the file being associated with the binary
596 @type session: SQLAlchemy session
597 @param session: Optional SQLAlchemy session. If this is passed, the caller
598 is responsible for ensuring a transaction has begun and committing the
599 results or rolling back based on the result code. If not passed, a commit
600 will be performed at the end of the function, otherwise the caller is
601 responsible for commiting.
603 @return: True upon success
608 session = DBConn().session()
615 def generate_path_dicts():
616 for fullpath in fullpaths:
617 if fullpath.startswith( './' ):
618 fullpath = fullpath[2:]
620 yield {'fulename':fullpath, 'id': binary_id }
622 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )",
623 generate_path_dicts() )
631 traceback.print_exc()
633 # Only rollback if we set up the session ourself
640 __all__.append('insert_content_paths')
642 ################################################################################
644 class DSCFile(object):
645 def __init__(self, *args, **kwargs):
649 return '<DSCFile %s>' % self.dscfile_id
651 __all__.append('DSCFile')
654 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
656 Returns a list of DSCFiles which may be empty
658 @type dscfile_id: int (optional)
659 @param dscfile_id: the dscfile_id of the DSCFiles to find
661 @type source_id: int (optional)
662 @param source_id: the source id related to the DSCFiles to find
664 @type poolfile_id: int (optional)
665 @param poolfile_id: the poolfile id related to the DSCFiles to find
668 @return: Possibly empty list of DSCFiles
671 q = session.query(DSCFile)
673 if dscfile_id is not None:
674 q = q.filter_by(dscfile_id=dscfile_id)
676 if source_id is not None:
677 q = q.filter_by(source_id=source_id)
679 if poolfile_id is not None:
680 q = q.filter_by(poolfile_id=poolfile_id)
684 __all__.append('get_dscfiles')
686 ################################################################################
688 class PoolFile(object):
689 def __init__(self, *args, **kwargs):
693 return '<PoolFile %s>' % self.filename
695 __all__.append('PoolFile')
698 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
701 (ValidFileFound [boolean or None], PoolFile object or None)
703 @type filename: string
704 @param filename: the filename of the file to check against the DB
707 @param filesize: the size of the file to check against the DB
710 @param md5sum: the md5sum of the file to check against the DB
712 @type location_id: int
713 @param location_id: the id of the location to look in
716 @return: Tuple of length 2.
717 If more than one file found with that name:
719 If valid pool file found: (True, PoolFile object)
720 If valid pool file not found:
721 (False, None) if no file found
722 (False, PoolFile object) if file found with size/md5sum mismatch
725 q = session.query(PoolFile).filter_by(filename=filename)
726 q = q.join(Location).filter_by(location_id=location_id)
736 if obj.md5sum != md5sum or obj.filesize != filesize:
744 __all__.append('check_poolfile')
747 def get_poolfile_by_id(file_id, session=None):
749 Returns a PoolFile objects or None for the given id
752 @param file_id: the id of the file to look for
754 @rtype: PoolFile or None
755 @return: either the PoolFile object or None
758 q = session.query(PoolFile).filter_by(file_id=file_id)
762 except NoResultFound:
765 __all__.append('get_poolfile_by_id')
769 def get_poolfile_by_name(filename, location_id=None, session=None):
771 Returns an array of PoolFile objects for the given filename and
772 (optionally) location_id
774 @type filename: string
775 @param filename: the filename of the file to check against the DB
777 @type location_id: int
778 @param location_id: the id of the location to look in (optional)
781 @return: array of PoolFile objects
784 q = session.query(PoolFile).filter_by(filename=filename)
786 if location_id is not None:
787 q = q.join(Location).filter_by(location_id=location_id)
791 __all__.append('get_poolfile_by_name')
794 def get_poolfile_like_name(filename, session=None):
796 Returns an array of PoolFile objects which are like the given name
798 @type filename: string
799 @param filename: the filename of the file to check against the DB
802 @return: array of PoolFile objects
805 # TODO: There must be a way of properly using bind parameters with %FOO%
806 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
810 __all__.append('get_poolfile_like_name')
812 ################################################################################
814 class Fingerprint(object):
815 def __init__(self, *args, **kwargs):
819 return '<Fingerprint %s>' % self.fingerprint
821 __all__.append('Fingerprint')
824 def get_or_set_fingerprint(fpr, session=None):
826 Returns Fingerprint object for given fpr.
828 If no matching fpr is found, a row is inserted.
831 @param fpr: The fpr to find / add
833 @type session: SQLAlchemy
834 @param session: Optional SQL session object (a temporary one will be
835 generated if not supplied). If not passed, a commit will be performed at
836 the end of the function, otherwise the caller is responsible for commiting.
837 A flush will be performed either way.
840 @return: the Fingerprint object for the given fpr
843 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
847 except NoResultFound:
848 fingerprint = Fingerprint()
849 fingerprint.fingerprint = fpr
850 session.add(fingerprint)
851 session.commit_or_flush()
856 __all__.append('get_or_set_fingerprint')
858 ################################################################################
860 class Keyring(object):
861 def __init__(self, *args, **kwargs):
865 return '<Keyring %s>' % self.keyring_name
867 __all__.append('Keyring')
870 def get_or_set_keyring(keyring, session=None):
872 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
873 and return the new Keyring
874 If C{keyring} already has an entry, simply return the existing Keyring
876 @type keyring: string
877 @param keyring: the keyring name
880 @return: the Keyring object for this keyring
883 q = session.query(Keyring).filter_by(keyring_name=keyring)
887 except NoResultFound:
888 obj = Keyring(keyring_name=keyring)
890 session.commit_or_flush()
893 __all__.append('get_or_set_keyring')
895 ################################################################################
897 class Location(object):
898 def __init__(self, *args, **kwargs):
902 return '<Location %s (%s)>' % (self.path, self.location_id)
904 __all__.append('Location')
907 def get_location(location, component=None, archive=None, session=None):
909 Returns Location object for the given combination of location, component
912 @type location: string
913 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
915 @type component: string
916 @param component: the component name (if None, no restriction applied)
918 @type archive: string
919 @param archive_id: the archive name (if None, no restriction applied)
921 @rtype: Location / None
922 @return: Either a Location object or None if one can't be found
925 q = session.query(Location).filter_by(path=location)
927 if archive is not None:
928 q = q.join(Archive).filter_by(archive_name=archive)
930 if component is not None:
931 q = q.join(Component).filter_by(component_name=component)
935 except NoResultFound:
938 __all__.append('get_location')
940 ################################################################################
942 class Maintainer(object):
943 def __init__(self, *args, **kwargs):
947 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
949 def get_split_maintainer(self):
950 if not hasattr(self, 'name') or self.name is None:
951 return ('', '', '', '')
953 return fix_maintainer(self.name.strip())
955 __all__.append('Maintainer')
958 def get_or_set_maintainer(name, session=None):
960 Returns Maintainer object for given maintainer name.
962 If no matching maintainer name is found, a row is inserted.
965 @param name: The maintainer name to add
967 @type session: SQLAlchemy
968 @param session: Optional SQL session object (a temporary one will be
969 generated if not supplied). If not passed, a commit will be performed at
970 the end of the function, otherwise the caller is responsible for commiting.
971 A flush will be performed either way.
974 @return: the Maintainer object for the given maintainer
977 q = session.query(Maintainer).filter_by(name=name)
980 except NoResultFound:
981 maintainer = Maintainer()
982 maintainer.name = name
983 session.add(maintainer)
984 session.commit_or_flush()
989 __all__.append('get_or_set_maintainer')
992 def get_maintainer(maintainer_id, session=None):
994 Return the name of the maintainer behind C{maintainer_id} or None if that
995 maintainer_id is invalid.
997 @type maintainer_id: int
998 @param maintainer_id: the id of the maintainer
1001 @return: the Maintainer with this C{maintainer_id}
1004 return session.query(Maintainer).get(maintainer_id)
1006 __all__.append('get_maintainer')
1008 ################################################################################
1010 class NewComment(object):
1011 def __init__(self, *args, **kwargs):
1015 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1017 __all__.append('NewComment')
1020 def has_new_comment(package, version, session=None):
1022 Returns true if the given combination of C{package}, C{version} has a comment.
1024 @type package: string
1025 @param package: name of the package
1027 @type version: string
1028 @param version: package version
1030 @type session: Session
1031 @param session: Optional SQLA session object (a temporary one will be
1032 generated if not supplied)
1038 q = session.query(NewComment)
1039 q = q.filter_by(package=package)
1040 q = q.filter_by(version=version)
1042 return bool(q.count() > 0)
1044 __all__.append('has_new_comment')
1047 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1049 Returns (possibly empty) list of NewComment objects for the given
1052 @type package: string (optional)
1053 @param package: name of the package
1055 @type version: string (optional)
1056 @param version: package version
1058 @type comment_id: int (optional)
1059 @param comment_id: An id of a comment
1061 @type session: Session
1062 @param session: Optional SQLA session object (a temporary one will be
1063 generated if not supplied)
1066 @return: A (possibly empty) list of NewComment objects will be returned
1069 q = session.query(NewComment)
1070 if package is not None: q = q.filter_by(package=package)
1071 if version is not None: q = q.filter_by(version=version)
1072 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1076 __all__.append('get_new_comments')
1078 ################################################################################
1080 class Override(object):
1081 def __init__(self, *args, **kwargs):
1085 return '<Override %s (%s)>' % (self.package, self.suite_id)
1087 __all__.append('Override')
1090 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1092 Returns Override object for the given parameters
1094 @type package: string
1095 @param package: The name of the package
1097 @type suite: string, list or None
1098 @param suite: The name of the suite (or suites if a list) to limit to. If
1099 None, don't limit. Defaults to None.
1101 @type component: string, list or None
1102 @param component: The name of the component (or components if a list) to
1103 limit to. If None, don't limit. Defaults to None.
1105 @type overridetype: string, list or None
1106 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1107 limit to. If None, don't limit. Defaults to None.
1109 @type session: Session
1110 @param session: Optional SQLA session object (a temporary one will be
1111 generated if not supplied)
1114 @return: A (possibly empty) list of Override objects will be returned
1117 q = session.query(Override)
1118 q = q.filter_by(package=package)
1120 if suite is not None:
1121 if not isinstance(suite, list): suite = [suite]
1122 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1124 if component is not None:
1125 if not isinstance(component, list): component = [component]
1126 q = q.join(Component).filter(Component.component_name.in_(component))
1128 if overridetype is not None:
1129 if not isinstance(overridetype, list): overridetype = [overridetype]
1130 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1134 __all__.append('get_override')
1137 ################################################################################
1139 class OverrideType(object):
1140 def __init__(self, *args, **kwargs):
1144 return '<OverrideType %s>' % self.overridetype
1146 __all__.append('OverrideType')
1149 def get_override_type(override_type, session=None):
1151 Returns OverrideType object for given C{override type}.
1153 @type override_type: string
1154 @param override_type: The name of the override type
1156 @type session: Session
1157 @param session: Optional SQLA session object (a temporary one will be
1158 generated if not supplied)
1161 @return: the database id for the given override type
1164 q = session.query(OverrideType).filter_by(overridetype=override_type)
1168 except NoResultFound:
1171 __all__.append('get_override_type')
1173 ################################################################################
1175 class DebContents(object):
1176 def __init__(self, *args, **kwargs):
1180 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1182 __all__.append('DebContents')
1185 class UdebContents(object):
1186 def __init__(self, *args, **kwargs):
1190 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1192 __all__.append('UdebContents')
1194 class PendingBinContents(object):
1195 def __init__(self, *args, **kwargs):
1199 return '<PendingBinContents %s>' % self.contents_id
1201 __all__.append('PendingBinContents')
1203 def insert_pending_content_paths(package,
1208 Make sure given paths are temporarily associated with given
1212 @param package: the package to associate with should have been read in from the binary control file
1213 @type fullpaths: list
1214 @param fullpaths: the list of paths of the file being associated with the binary
1215 @type session: SQLAlchemy session
1216 @param session: Optional SQLAlchemy session. If this is passed, the caller
1217 is responsible for ensuring a transaction has begun and committing the
1218 results or rolling back based on the result code. If not passed, a commit
1219 will be performed at the end of the function
1221 @return: True upon success, False if there is a problem
1224 privatetrans = False
1227 session = DBConn().session()
1231 arch = get_architecture(package['Architecture'], session)
1232 arch_id = arch.arch_id
1234 # Remove any already existing recorded files for this package
1235 q = session.query(PendingBinContents)
1236 q = q.filter_by(package=package['Package'])
1237 q = q.filter_by(version=package['Version'])
1238 q = q.filter_by(architecture=arch_id)
1241 for fullpath in fullpaths:
1243 if fullpath.startswith( "./" ):
1244 fullpath = fullpath[2:]
1246 pca = PendingBinContents()
1247 pca.package = package['Package']
1248 pca.version = package['Version']
1250 pca.architecture = arch_id
1253 pca.type = 8 # gross
1255 pca.type = 7 # also gross
1258 # Only commit if we set up the session ourself
1266 except Exception, e:
1267 traceback.print_exc()
1269 # Only rollback if we set up the session ourself
1276 __all__.append('insert_pending_content_paths')
1278 ################################################################################
1280 class Priority(object):
1281 def __init__(self, *args, **kwargs):
1284 def __eq__(self, val):
1285 if isinstance(val, str):
1286 return (self.priority == val)
1287 # This signals to use the normal comparison operator
1288 return NotImplemented
1290 def __ne__(self, val):
1291 if isinstance(val, str):
1292 return (self.priority != val)
1293 # This signals to use the normal comparison operator
1294 return NotImplemented
1297 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1299 __all__.append('Priority')
1302 def get_priority(priority, session=None):
1304 Returns Priority object for given C{priority name}.
1306 @type priority: string
1307 @param priority: The name of the priority
1309 @type session: Session
1310 @param session: Optional SQLA session object (a temporary one will be
1311 generated if not supplied)
1314 @return: Priority object for the given priority
1317 q = session.query(Priority).filter_by(priority=priority)
1321 except NoResultFound:
1324 __all__.append('get_priority')
1327 def get_priorities(session=None):
1329 Returns dictionary of priority names -> id mappings
1331 @type session: Session
1332 @param session: Optional SQL session object (a temporary one will be
1333 generated if not supplied)
1336 @return: dictionary of priority names -> id mappings
1340 q = session.query(Priority)
1342 ret[x.priority] = x.priority_id
1346 __all__.append('get_priorities')
1348 ################################################################################
1350 class Queue(object):
1351 def __init__(self, *args, **kwargs):
1355 return '<Queue %s>' % self.queue_name
1357 def autobuild_upload(self, changes, srcpath, session=None):
1359 Update queue_build database table used for incoming autobuild support.
1361 @type changes: Changes
1362 @param changes: changes object for the upload to process
1364 @type srcpath: string
1365 @param srcpath: path for the queue file entries/link destinations
1367 @type session: SQLAlchemy session
1368 @param session: Optional SQLAlchemy session. If this is passed, the
1369 caller is responsible for ensuring a transaction has begun and
1370 committing the results or rolling back based on the result code. If
1371 not passed, a commit will be performed at the end of the function,
1372 otherwise the caller is responsible for commiting.
1374 @rtype: NoneType or string
1375 @return: None if the operation failed, a string describing the error if not
1378 privatetrans = False
1380 session = DBConn().session()
1383 # TODO: Remove by moving queue config into the database
1386 for suitename in changes.changes["distribution"].keys():
1387 # TODO: Move into database as:
1388 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1389 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1390 # This also gets rid of the SecurityQueueBuild hack below
1391 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1395 s = get_suite(suitename, session)
1397 return "INTERNAL ERROR: Could not find suite %s" % suitename
1399 # TODO: Get from database as above
1400 dest_dir = conf["Dir::QueueBuild"]
1402 # TODO: Move into database as above
1403 if conf.FindB("Dinstall::SecurityQueueBuild"):
1404 dest_dir = os.path.join(dest_dir, suitename)
1406 for file_entry in changes.files.keys():
1407 src = os.path.join(srcpath, file_entry)
1408 dest = os.path.join(dest_dir, file_entry)
1410 # TODO: Move into database as above
1411 if conf.FindB("Dinstall::SecurityQueueBuild"):
1412 # Copy it since the original won't be readable by www-data
1414 utils.copy(src, dest)
1416 # Create a symlink to it
1417 os.symlink(src, dest)
1420 qb.suite_id = s.suite_id
1421 qb.queue_id = self.queue_id
1427 exists, symlinked = utils.ensure_orig_files(changes, dest, session)
1429 # Add symlinked files to the list of packages for later processing
1431 for filename in symlinked:
1433 qb.suite_id = s.suite_id
1434 qb.queue_id = self.queue_id
1435 qb.filename = filename
1439 # Update files to ensure they are not removed prematurely
1440 for filename in exists:
1441 qb = get_queue_build(filename, s.suite_id, session)
1453 __all__.append('Queue')
1456 def get_or_set_queue(queuename, session=None):
1458 Returns Queue object for given C{queue name}, creating it if it does not
1461 @type queuename: string
1462 @param queuename: The name of the queue
1464 @type session: Session
1465 @param session: Optional SQLA session object (a temporary one will be
1466 generated if not supplied)
1469 @return: Queue object for the given queue
1472 q = session.query(Queue).filter_by(queue_name=queuename)
1476 except NoResultFound:
1478 queue.queue_name = queuename
1480 session.commit_or_flush()
1485 __all__.append('get_or_set_queue')
1487 ################################################################################
1489 class QueueBuild(object):
1490 def __init__(self, *args, **kwargs):
1494 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1496 __all__.append('QueueBuild')
1499 def get_queue_build(filename, suite, session=None):
1501 Returns QueueBuild object for given C{filename} and C{suite}.
1503 @type filename: string
1504 @param filename: The name of the file
1506 @type suiteid: int or str
1507 @param suiteid: Suite name or ID
1509 @type session: Session
1510 @param session: Optional SQLA session object (a temporary one will be
1511 generated if not supplied)
1514 @return: Queue object for the given queue
1517 if isinstance(suite, int):
1518 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1520 q = session.query(QueueBuild).filter_by(filename=filename)
1521 q = q.join(Suite).filter_by(suite_name=suite)
1525 except NoResultFound:
1528 __all__.append('get_queue_build')
1530 ################################################################################
1532 class Section(object):
1533 def __init__(self, *args, **kwargs):
1536 def __eq__(self, val):
1537 if isinstance(val, str):
1538 return (self.section == val)
1539 # This signals to use the normal comparison operator
1540 return NotImplemented
1542 def __ne__(self, val):
1543 if isinstance(val, str):
1544 return (self.section != val)
1545 # This signals to use the normal comparison operator
1546 return NotImplemented
1549 return '<Section %s>' % self.section
1551 __all__.append('Section')
1554 def get_section(section, session=None):
1556 Returns Section object for given C{section name}.
1558 @type section: string
1559 @param section: The name of the section
1561 @type session: Session
1562 @param session: Optional SQLA session object (a temporary one will be
1563 generated if not supplied)
1566 @return: Section object for the given section name
1569 q = session.query(Section).filter_by(section=section)
1573 except NoResultFound:
1576 __all__.append('get_section')
1579 def get_sections(session=None):
1581 Returns dictionary of section names -> id mappings
1583 @type session: Session
1584 @param session: Optional SQL session object (a temporary one will be
1585 generated if not supplied)
1588 @return: dictionary of section names -> id mappings
1592 q = session.query(Section)
1594 ret[x.section] = x.section_id
1598 __all__.append('get_sections')
1600 ################################################################################
1602 class DBSource(object):
1603 def __init__(self, *args, **kwargs):
1607 return '<DBSource %s (%s)>' % (self.source, self.version)
1609 __all__.append('DBSource')
1612 def source_exists(source, source_version, suites = ["any"], session=None):
1614 Ensure that source exists somewhere in the archive for the binary
1615 upload being processed.
1616 1. exact match => 1.0-3
1617 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1619 @type package: string
1620 @param package: package source name
1622 @type source_version: string
1623 @param source_version: expected source version
1626 @param suites: list of suites to check in, default I{any}
1628 @type session: Session
1629 @param session: Optional SQLA session object (a temporary one will be
1630 generated if not supplied)
1633 @return: returns 1 if a source with expected version is found, otherwise 0
1640 for suite in suites:
1641 q = session.query(DBSource).filter_by(source=source)
1643 # source must exist in suite X, or in some other suite that's
1644 # mapped to X, recursively... silent-maps are counted too,
1645 # unreleased-maps aren't.
1646 maps = cnf.ValueList("SuiteMappings")[:]
1648 maps = [ m.split() for m in maps ]
1649 maps = [ (x[1], x[2]) for x in maps
1650 if x[0] == "map" or x[0] == "silent-map" ]
1653 if x[1] in s and x[0] not in s:
1656 q = q.join(SrcAssociation).join(Suite)
1657 q = q.filter(Suite.suite_name.in_(s))
1659 # Reduce the query results to a list of version numbers
1660 ql = [ j.version for j in q.all() ]
1663 if source_version in ql:
1667 from daklib.regexes import re_bin_only_nmu
1668 orig_source_version = re_bin_only_nmu.sub('', source_version)
1669 if orig_source_version in ql:
1672 # No source found so return not ok
1677 __all__.append('source_exists')
1680 def get_suites_source_in(source, session=None):
1682 Returns list of Suite objects which given C{source} name is in
1685 @param source: DBSource package name to search for
1688 @return: list of Suite objects for the given source
1691 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1693 __all__.append('get_suites_source_in')
1696 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1698 Returns list of DBSource objects for given C{source} name and other parameters
1701 @param source: DBSource package name to search for
1703 @type source: str or None
1704 @param source: DBSource version name to search for or None if not applicable
1706 @type dm_upload_allowed: bool
1707 @param dm_upload_allowed: If None, no effect. If True or False, only
1708 return packages with that dm_upload_allowed setting
1710 @type session: Session
1711 @param session: Optional SQL session object (a temporary one will be
1712 generated if not supplied)
1715 @return: list of DBSource objects for the given name (may be empty)
1718 q = session.query(DBSource).filter_by(source=source)
1720 if version is not None:
1721 q = q.filter_by(version=version)
1723 if dm_upload_allowed is not None:
1724 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1728 __all__.append('get_sources_from_name')
1731 def get_source_in_suite(source, suite, session=None):
1733 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1735 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1736 - B{suite} - a suite name, eg. I{unstable}
1738 @type source: string
1739 @param source: source package name
1742 @param suite: the suite name
1745 @return: the version for I{source} in I{suite}
1749 q = session.query(SrcAssociation)
1750 q = q.join('source').filter_by(source=source)
1751 q = q.join('suite').filter_by(suite_name=suite)
1754 return q.one().source
1755 except NoResultFound:
1758 __all__.append('get_source_in_suite')
1760 ################################################################################
1762 class SrcAssociation(object):
1763 def __init__(self, *args, **kwargs):
1767 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1769 __all__.append('SrcAssociation')
1771 ################################################################################
1773 class SrcFormat(object):
1774 def __init__(self, *args, **kwargs):
1778 return '<SrcFormat %s>' % (self.format_name)
1780 __all__.append('SrcFormat')
1782 ################################################################################
1784 class SrcUploader(object):
1785 def __init__(self, *args, **kwargs):
1789 return '<SrcUploader %s>' % self.uploader_id
1791 __all__.append('SrcUploader')
1793 ################################################################################
1795 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1796 ('SuiteID', 'suite_id'),
1797 ('Version', 'version'),
1798 ('Origin', 'origin'),
1800 ('Description', 'description'),
1801 ('Untouchable', 'untouchable'),
1802 ('Announce', 'announce'),
1803 ('Codename', 'codename'),
1804 ('OverrideCodename', 'overridecodename'),
1805 ('ValidTime', 'validtime'),
1806 ('Priority', 'priority'),
1807 ('NotAutomatic', 'notautomatic'),
1808 ('CopyChanges', 'copychanges'),
1809 ('CopyDotDak', 'copydotdak'),
1810 ('CommentsDir', 'commentsdir'),
1811 ('OverrideSuite', 'overridesuite'),
1812 ('ChangelogBase', 'changelogbase')]
1815 class Suite(object):
1816 def __init__(self, *args, **kwargs):
1820 return '<Suite %s>' % self.suite_name
1822 def __eq__(self, val):
1823 if isinstance(val, str):
1824 return (self.suite_name == val)
1825 # This signals to use the normal comparison operator
1826 return NotImplemented
1828 def __ne__(self, val):
1829 if isinstance(val, str):
1830 return (self.suite_name != val)
1831 # This signals to use the normal comparison operator
1832 return NotImplemented
1836 for disp, field in SUITE_FIELDS:
1837 val = getattr(self, field, None)
1839 ret.append("%s: %s" % (disp, val))
1841 return "\n".join(ret)
1843 __all__.append('Suite')
1846 def get_suite_architecture(suite, architecture, session=None):
1848 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1852 @param suite: Suite name to search for
1854 @type architecture: str
1855 @param architecture: Architecture name to search for
1857 @type session: Session
1858 @param session: Optional SQL session object (a temporary one will be
1859 generated if not supplied)
1861 @rtype: SuiteArchitecture
1862 @return: the SuiteArchitecture object or None
1865 q = session.query(SuiteArchitecture)
1866 q = q.join(Architecture).filter_by(arch_string=architecture)
1867 q = q.join(Suite).filter_by(suite_name=suite)
1871 except NoResultFound:
1874 __all__.append('get_suite_architecture')
1877 def get_suite(suite, session=None):
1879 Returns Suite object for given C{suite name}.
1882 @param suite: The name of the suite
1884 @type session: Session
1885 @param session: Optional SQLA session object (a temporary one will be
1886 generated if not supplied)
1889 @return: Suite object for the requested suite name (None if not present)
1892 q = session.query(Suite).filter_by(suite_name=suite)
1896 except NoResultFound:
1899 __all__.append('get_suite')
1901 ################################################################################
1903 class SuiteArchitecture(object):
1904 def __init__(self, *args, **kwargs):
1908 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1910 __all__.append('SuiteArchitecture')
1913 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1915 Returns list of Architecture objects for given C{suite} name
1918 @param source: Suite name to search for
1920 @type skipsrc: boolean
1921 @param skipsrc: Whether to skip returning the 'source' architecture entry
1924 @type skipall: boolean
1925 @param skipall: Whether to skip returning the 'all' architecture entry
1928 @type session: Session
1929 @param session: Optional SQL session object (a temporary one will be
1930 generated if not supplied)
1933 @return: list of Architecture objects for the given name (may be empty)
1936 q = session.query(Architecture)
1937 q = q.join(SuiteArchitecture)
1938 q = q.join(Suite).filter_by(suite_name=suite)
1941 q = q.filter(Architecture.arch_string != 'source')
1944 q = q.filter(Architecture.arch_string != 'all')
1946 q = q.order_by('arch_string')
1950 __all__.append('get_suite_architectures')
1952 ################################################################################
1954 class SuiteSrcFormat(object):
1955 def __init__(self, *args, **kwargs):
1959 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1961 __all__.append('SuiteSrcFormat')
1964 def get_suite_src_formats(suite, session=None):
1966 Returns list of allowed SrcFormat for C{suite}.
1969 @param suite: Suite name to search for
1971 @type session: Session
1972 @param session: Optional SQL session object (a temporary one will be
1973 generated if not supplied)
1976 @return: the list of allowed source formats for I{suite}
1979 q = session.query(SrcFormat)
1980 q = q.join(SuiteSrcFormat)
1981 q = q.join(Suite).filter_by(suite_name=suite)
1982 q = q.order_by('format_name')
1986 __all__.append('get_suite_src_formats')
1988 ################################################################################
1991 def __init__(self, *args, **kwargs):
1994 def __eq__(self, val):
1995 if isinstance(val, str):
1996 return (self.uid == val)
1997 # This signals to use the normal comparison operator
1998 return NotImplemented
2000 def __ne__(self, val):
2001 if isinstance(val, str):
2002 return (self.uid != val)
2003 # This signals to use the normal comparison operator
2004 return NotImplemented
2007 return '<Uid %s (%s)>' % (self.uid, self.name)
2009 __all__.append('Uid')
2012 def add_database_user(uidname, session=None):
2014 Adds a database user
2016 @type uidname: string
2017 @param uidname: The uid of the user to add
2019 @type session: SQLAlchemy
2020 @param session: Optional SQL session object (a temporary one will be
2021 generated if not supplied). If not passed, a commit will be performed at
2022 the end of the function, otherwise the caller is responsible for commiting.
2025 @return: the uid object for the given uidname
2028 session.execute("CREATE USER :uid", {'uid': uidname})
2029 session.commit_or_flush()
2031 __all__.append('add_database_user')
2034 def get_or_set_uid(uidname, session=None):
2036 Returns uid object for given uidname.
2038 If no matching uidname is found, a row is inserted.
2040 @type uidname: string
2041 @param uidname: The uid to add
2043 @type session: SQLAlchemy
2044 @param session: Optional SQL session object (a temporary one will be
2045 generated if not supplied). If not passed, a commit will be performed at
2046 the end of the function, otherwise the caller is responsible for commiting.
2049 @return: the uid object for the given uidname
2052 q = session.query(Uid).filter_by(uid=uidname)
2056 except NoResultFound:
2060 session.commit_or_flush()
2065 __all__.append('get_or_set_uid')
2068 def get_uid_from_fingerprint(fpr, session=None):
2069 q = session.query(Uid)
2070 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2074 except NoResultFound:
2077 __all__.append('get_uid_from_fingerprint')
2079 ################################################################################
2081 class DBConn(Singleton):
2083 database module init.
2085 def __init__(self, *args, **kwargs):
2086 super(DBConn, self).__init__(*args, **kwargs)
2088 def _startup(self, *args, **kwargs):
2090 if kwargs.has_key('debug'):
2094 def __setuptables(self):
2095 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2096 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2097 self.tbl_bin_contents = Table('bin_contents', self.db_meta, autoload=True)
2098 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2099 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2100 self.tbl_component = Table('component', self.db_meta, autoload=True)
2101 self.tbl_config = Table('config', self.db_meta, autoload=True)
2102 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2103 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2104 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2105 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2106 self.tbl_deb_contents = Table('deb_contents', self.db_meta, autoload=True)
2107 self.tbl_files = Table('files', self.db_meta, autoload=True)
2108 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2109 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2110 self.tbl_location = Table('location', self.db_meta, autoload=True)
2111 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2112 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2113 self.tbl_override = Table('override', self.db_meta, autoload=True)
2114 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2115 self.tbl_pending_bin_contents = Table('pending_bin_contents', self.db_meta, autoload=True)
2116 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2117 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2118 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2119 self.tbl_section = Table('section', self.db_meta, autoload=True)
2120 self.tbl_source = Table('source', self.db_meta, autoload=True)
2121 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2122 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2123 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2124 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2125 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2126 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2127 self.tbl_udeb_contents = Table('udeb_contents', self.db_meta, autoload=True)
2128 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2130 def __setupmappers(self):
2131 mapper(Architecture, self.tbl_architecture,
2132 properties = dict(arch_id = self.tbl_architecture.c.id))
2134 mapper(Archive, self.tbl_archive,
2135 properties = dict(archive_id = self.tbl_archive.c.id,
2136 archive_name = self.tbl_archive.c.name))
2138 mapper(BinAssociation, self.tbl_bin_associations,
2139 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2140 suite_id = self.tbl_bin_associations.c.suite,
2141 suite = relation(Suite),
2142 binary_id = self.tbl_bin_associations.c.bin,
2143 binary = relation(DBBinary)))
2145 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2146 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2147 filename = self.tbl_pending_bin_contents.c.filename,
2148 package = self.tbl_pending_bin_contents.c.package,
2149 version = self.tbl_pending_bin_contents.c.version,
2150 arch = self.tbl_pending_bin_contents.c.arch,
2151 otype = self.tbl_pending_bin_contents.c.type))
2153 mapper(DebContents, self.tbl_deb_contents,
2154 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2155 package=self.tbl_deb_contents.c.package,
2156 component=self.tbl_deb_contents.c.component,
2157 arch=self.tbl_deb_contents.c.arch,
2158 section=self.tbl_deb_contents.c.section,
2159 filename=self.tbl_deb_contents.c.filename))
2161 mapper(UdebContents, self.tbl_udeb_contents,
2162 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2163 package=self.tbl_udeb_contents.c.package,
2164 component=self.tbl_udeb_contents.c.component,
2165 arch=self.tbl_udeb_contents.c.arch,
2166 section=self.tbl_udeb_contents.c.section,
2167 filename=self.tbl_udeb_contents.c.filename))
2169 mapper(DBBinary, self.tbl_binaries,
2170 properties = dict(binary_id = self.tbl_binaries.c.id,
2171 package = self.tbl_binaries.c.package,
2172 version = self.tbl_binaries.c.version,
2173 maintainer_id = self.tbl_binaries.c.maintainer,
2174 maintainer = relation(Maintainer),
2175 source_id = self.tbl_binaries.c.source,
2176 source = relation(DBSource),
2177 arch_id = self.tbl_binaries.c.architecture,
2178 architecture = relation(Architecture),
2179 poolfile_id = self.tbl_binaries.c.file,
2180 poolfile = relation(PoolFile),
2181 binarytype = self.tbl_binaries.c.type,
2182 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2183 fingerprint = relation(Fingerprint),
2184 install_date = self.tbl_binaries.c.install_date,
2185 binassociations = relation(BinAssociation,
2186 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2188 mapper(Component, self.tbl_component,
2189 properties = dict(component_id = self.tbl_component.c.id,
2190 component_name = self.tbl_component.c.name))
2192 mapper(DBConfig, self.tbl_config,
2193 properties = dict(config_id = self.tbl_config.c.id))
2195 mapper(DSCFile, self.tbl_dsc_files,
2196 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2197 source_id = self.tbl_dsc_files.c.source,
2198 source = relation(DBSource),
2199 poolfile_id = self.tbl_dsc_files.c.file,
2200 poolfile = relation(PoolFile)))
2202 mapper(PoolFile, self.tbl_files,
2203 properties = dict(file_id = self.tbl_files.c.id,
2204 filesize = self.tbl_files.c.size,
2205 location_id = self.tbl_files.c.location,
2206 location = relation(Location)))
2208 mapper(Fingerprint, self.tbl_fingerprint,
2209 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2210 uid_id = self.tbl_fingerprint.c.uid,
2211 uid = relation(Uid),
2212 keyring_id = self.tbl_fingerprint.c.keyring,
2213 keyring = relation(Keyring)))
2215 mapper(Keyring, self.tbl_keyrings,
2216 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2217 keyring_id = self.tbl_keyrings.c.id))
2219 mapper(Location, self.tbl_location,
2220 properties = dict(location_id = self.tbl_location.c.id,
2221 component_id = self.tbl_location.c.component,
2222 component = relation(Component),
2223 archive_id = self.tbl_location.c.archive,
2224 archive = relation(Archive),
2225 archive_type = self.tbl_location.c.type))
2227 mapper(Maintainer, self.tbl_maintainer,
2228 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2230 mapper(NewComment, self.tbl_new_comments,
2231 properties = dict(comment_id = self.tbl_new_comments.c.id))
2233 mapper(Override, self.tbl_override,
2234 properties = dict(suite_id = self.tbl_override.c.suite,
2235 suite = relation(Suite),
2236 component_id = self.tbl_override.c.component,
2237 component = relation(Component),
2238 priority_id = self.tbl_override.c.priority,
2239 priority = relation(Priority),
2240 section_id = self.tbl_override.c.section,
2241 section = relation(Section),
2242 overridetype_id = self.tbl_override.c.type,
2243 overridetype = relation(OverrideType)))
2245 mapper(OverrideType, self.tbl_override_type,
2246 properties = dict(overridetype = self.tbl_override_type.c.type,
2247 overridetype_id = self.tbl_override_type.c.id))
2249 mapper(Priority, self.tbl_priority,
2250 properties = dict(priority_id = self.tbl_priority.c.id))
2252 mapper(Queue, self.tbl_queue,
2253 properties = dict(queue_id = self.tbl_queue.c.id))
2255 mapper(QueueBuild, self.tbl_queue_build,
2256 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2257 queue_id = self.tbl_queue_build.c.queue,
2258 queue = relation(Queue, backref='queuebuild')))
2260 mapper(Section, self.tbl_section,
2261 properties = dict(section_id = self.tbl_section.c.id,
2262 section=self.tbl_section.c.section))
2264 mapper(DBSource, self.tbl_source,
2265 properties = dict(source_id = self.tbl_source.c.id,
2266 version = self.tbl_source.c.version,
2267 maintainer_id = self.tbl_source.c.maintainer,
2268 maintainer = relation(Maintainer,
2269 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2270 poolfile_id = self.tbl_source.c.file,
2271 poolfile = relation(PoolFile),
2272 fingerprint_id = self.tbl_source.c.sig_fpr,
2273 fingerprint = relation(Fingerprint),
2274 changedby_id = self.tbl_source.c.changedby,
2275 changedby = relation(Maintainer,
2276 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2277 srcfiles = relation(DSCFile,
2278 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2279 srcassociations = relation(SrcAssociation,
2280 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2282 mapper(SrcAssociation, self.tbl_src_associations,
2283 properties = dict(sa_id = self.tbl_src_associations.c.id,
2284 suite_id = self.tbl_src_associations.c.suite,
2285 suite = relation(Suite),
2286 source_id = self.tbl_src_associations.c.source,
2287 source = relation(DBSource)))
2289 mapper(SrcFormat, self.tbl_src_format,
2290 properties = dict(src_format_id = self.tbl_src_format.c.id,
2291 format_name = self.tbl_src_format.c.format_name))
2293 mapper(SrcUploader, self.tbl_src_uploaders,
2294 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2295 source_id = self.tbl_src_uploaders.c.source,
2296 source = relation(DBSource,
2297 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2298 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2299 maintainer = relation(Maintainer,
2300 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2302 mapper(Suite, self.tbl_suite,
2303 properties = dict(suite_id = self.tbl_suite.c.id))
2305 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2306 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2307 suite = relation(Suite, backref='suitearchitectures'),
2308 arch_id = self.tbl_suite_architectures.c.architecture,
2309 architecture = relation(Architecture)))
2311 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2312 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2313 suite = relation(Suite, backref='suitesrcformats'),
2314 src_format_id = self.tbl_suite_src_formats.c.src_format,
2315 src_format = relation(SrcFormat)))
2317 mapper(Uid, self.tbl_uid,
2318 properties = dict(uid_id = self.tbl_uid.c.id,
2319 fingerprint = relation(Fingerprint)))
2321 ## Connection functions
2322 def __createconn(self):
2323 from config import Config
2327 connstr = "postgres://%s" % cnf["DB::Host"]
2328 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2329 connstr += ":%s" % cnf["DB::Port"]
2330 connstr += "/%s" % cnf["DB::Name"]
2333 connstr = "postgres:///%s" % cnf["DB::Name"]
2334 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2335 connstr += "?port=%s" % cnf["DB::Port"]
2337 self.db_pg = create_engine(connstr, echo=self.debug)
2338 self.db_meta = MetaData()
2339 self.db_meta.bind = self.db_pg
2340 self.db_smaker = sessionmaker(bind=self.db_pg,
2344 self.__setuptables()
2345 self.__setupmappers()
2348 return self.db_smaker()
2350 __all__.append('DBConn')