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 DBBinary(object):
234 def __init__(self, *args, **kwargs):
238 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
240 __all__.append('DBBinary')
243 def get_suites_binary_in(package, session=None):
245 Returns list of Suite objects which given C{package} name is in
248 @param source: DBBinary package name to search for
251 @return: list of Suite objects for the given package
254 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
256 __all__.append('get_suites_binary_in')
259 def get_binary_from_id(id, session=None):
261 Returns DBBinary object for given C{id}
264 @param id: Id of the required binary
266 @type session: Session
267 @param session: Optional SQLA session object (a temporary one will be
268 generated if not supplied)
271 @return: DBBinary object for the given binary (None if not present)
274 q = session.query(DBBinary).filter_by(binary_id=id)
278 except NoResultFound:
281 __all__.append('get_binary_from_id')
284 def get_binaries_from_name(package, version=None, architecture=None, session=None):
286 Returns list of DBBinary objects for given C{package} name
289 @param package: DBBinary package name to search for
291 @type version: str or None
292 @param version: Version to search for (or None)
294 @type package: str, list or None
295 @param package: Architectures to limit to (or None if no limit)
297 @type session: Session
298 @param session: Optional SQL session object (a temporary one will be
299 generated if not supplied)
302 @return: list of DBBinary objects for the given name (may be empty)
305 q = session.query(DBBinary).filter_by(package=package)
307 if version is not None:
308 q = q.filter_by(version=version)
310 if architecture is not None:
311 if not isinstance(architecture, list):
312 architecture = [architecture]
313 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
319 __all__.append('get_binaries_from_name')
322 def get_binaries_from_source_id(source_id, session=None):
324 Returns list of DBBinary objects for given C{source_id}
327 @param source_id: source_id to search for
329 @type session: Session
330 @param session: Optional SQL session object (a temporary one will be
331 generated if not supplied)
334 @return: list of DBBinary objects for the given name (may be empty)
337 return session.query(DBBinary).filter_by(source_id=source_id).all()
339 __all__.append('get_binaries_from_source_id')
342 def get_binary_from_name_suite(package, suitename, session=None):
343 ### For dak examine-package
344 ### XXX: Doesn't use object API yet
346 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
347 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
348 WHERE b.package=:package
350 AND fi.location = l.id
351 AND l.component = c.id
354 AND su.suite_name=:suitename
355 ORDER BY b.version DESC"""
357 return session.execute(sql, {'package': package, 'suitename': suitename})
359 __all__.append('get_binary_from_name_suite')
362 def get_binary_components(package, suitename, arch, session=None):
363 # Check for packages that have moved from one component to another
364 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
365 WHERE b.package=:package AND s.suite_name=:suitename
366 AND (a.arch_string = :arch OR a.arch_string = 'all')
367 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
368 AND f.location = l.id
369 AND l.component = c.id
372 vals = {'package': package, 'suitename': suitename, 'arch': arch}
374 return session.execute(query, vals)
376 __all__.append('get_binary_components')
378 ################################################################################
380 class Component(object):
381 def __init__(self, *args, **kwargs):
384 def __eq__(self, val):
385 if isinstance(val, str):
386 return (self.component_name == val)
387 # This signals to use the normal comparison operator
388 return NotImplemented
390 def __ne__(self, val):
391 if isinstance(val, str):
392 return (self.component_name != val)
393 # This signals to use the normal comparison operator
394 return NotImplemented
397 return '<Component %s>' % self.component_name
400 __all__.append('Component')
403 def get_component(component, session=None):
405 Returns database id for given C{component}.
407 @type component: string
408 @param component: The name of the override type
411 @return: the database id for the given component
414 component = component.lower()
416 q = session.query(Component).filter_by(component_name=component)
420 except NoResultFound:
423 __all__.append('get_component')
425 ################################################################################
427 class DBConfig(object):
428 def __init__(self, *args, **kwargs):
432 return '<DBConfig %s>' % self.name
434 __all__.append('DBConfig')
436 ################################################################################
438 class ContentFilename(object):
439 def __init__(self, *args, **kwargs):
443 return '<ContentFilename %s>' % self.filename
445 __all__.append('ContentFilename')
448 def get_or_set_contents_file_id(filename, session=None):
450 Returns database id for given filename.
452 If no matching file is found, a row is inserted.
454 @type filename: string
455 @param filename: The filename
456 @type session: SQLAlchemy
457 @param session: Optional SQL session object (a temporary one will be
458 generated if not supplied). If not passed, a commit will be performed at
459 the end of the function, otherwise the caller is responsible for commiting.
462 @return: the database id for the given component
465 q = session.query(ContentFilename).filter_by(filename=filename)
468 ret = q.one().cafilename_id
469 except NoResultFound:
470 cf = ContentFilename()
471 cf.filename = filename
473 session.commit_or_flush()
474 ret = cf.cafilename_id
478 __all__.append('get_or_set_contents_file_id')
481 def get_contents(suite, overridetype, section=None, session=None):
483 Returns contents for a suite / overridetype combination, limiting
484 to a section if not None.
487 @param suite: Suite object
489 @type overridetype: OverrideType
490 @param overridetype: OverrideType object
492 @type section: Section
493 @param section: Optional section object to limit results to
495 @type session: SQLAlchemy
496 @param session: Optional SQL session object (a temporary one will be
497 generated if not supplied)
500 @return: ResultsProxy object set up to return tuples of (filename, section,
504 # find me all of the contents for a given suite
505 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
509 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
510 JOIN content_file_names n ON (c.filename=n.id)
511 JOIN binaries b ON (b.id=c.binary_pkg)
512 JOIN override o ON (o.package=b.package)
513 JOIN section s ON (s.id=o.section)
514 WHERE o.suite = :suiteid AND o.type = :overridetypeid
515 AND b.type=:overridetypename"""
517 vals = {'suiteid': suite.suite_id,
518 'overridetypeid': overridetype.overridetype_id,
519 'overridetypename': overridetype.overridetype}
521 if section is not None:
522 contents_q += " AND s.id = :sectionid"
523 vals['sectionid'] = section.section_id
525 contents_q += " ORDER BY fn"
527 return session.execute(contents_q, vals)
529 __all__.append('get_contents')
531 ################################################################################
533 class ContentFilepath(object):
534 def __init__(self, *args, **kwargs):
538 return '<ContentFilepath %s>' % self.filepath
540 __all__.append('ContentFilepath')
543 def get_or_set_contents_path_id(filepath, session=None):
545 Returns database id for given path.
547 If no matching file is found, a row is inserted.
549 @type filename: string
550 @param filename: The filepath
551 @type session: SQLAlchemy
552 @param session: Optional SQL session object (a temporary one will be
553 generated if not supplied). If not passed, a commit will be performed at
554 the end of the function, otherwise the caller is responsible for commiting.
557 @return: the database id for the given path
560 q = session.query(ContentFilepath).filter_by(filepath=filepath)
563 ret = q.one().cafilepath_id
564 except NoResultFound:
565 cf = ContentFilepath()
566 cf.filepath = filepath
568 session.commit_or_flush()
569 ret = cf.cafilepath_id
573 __all__.append('get_or_set_contents_path_id')
575 ################################################################################
577 class ContentAssociation(object):
578 def __init__(self, *args, **kwargs):
582 return '<ContentAssociation %s>' % self.ca_id
584 __all__.append('ContentAssociation')
586 def insert_content_paths(binary_id, fullpaths, session=None):
588 Make sure given path is associated with given binary id
591 @param binary_id: the id of the binary
592 @type fullpaths: list
593 @param fullpaths: the list of paths of the file being associated with the binary
594 @type session: SQLAlchemy session
595 @param session: Optional SQLAlchemy session. If this is passed, the caller
596 is responsible for ensuring a transaction has begun and committing the
597 results or rolling back based on the result code. If not passed, a commit
598 will be performed at the end of the function, otherwise the caller is
599 responsible for commiting.
601 @return: True upon success
606 session = DBConn().session()
612 for fullpath in fullpaths:
613 if fullpath.startswith( './' ):
614 fullpath = fullpath[2:]
616 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )", { 'filename': fullpath, 'id': binary_id} )
624 traceback.print_exc()
626 # Only rollback if we set up the session ourself
633 __all__.append('insert_content_paths')
635 ################################################################################
637 class DSCFile(object):
638 def __init__(self, *args, **kwargs):
642 return '<DSCFile %s>' % self.dscfile_id
644 __all__.append('DSCFile')
647 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
649 Returns a list of DSCFiles which may be empty
651 @type dscfile_id: int (optional)
652 @param dscfile_id: the dscfile_id of the DSCFiles to find
654 @type source_id: int (optional)
655 @param source_id: the source id related to the DSCFiles to find
657 @type poolfile_id: int (optional)
658 @param poolfile_id: the poolfile id related to the DSCFiles to find
661 @return: Possibly empty list of DSCFiles
664 q = session.query(DSCFile)
666 if dscfile_id is not None:
667 q = q.filter_by(dscfile_id=dscfile_id)
669 if source_id is not None:
670 q = q.filter_by(source_id=source_id)
672 if poolfile_id is not None:
673 q = q.filter_by(poolfile_id=poolfile_id)
677 __all__.append('get_dscfiles')
679 ################################################################################
681 class PoolFile(object):
682 def __init__(self, *args, **kwargs):
686 return '<PoolFile %s>' % self.filename
688 __all__.append('PoolFile')
691 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
694 (ValidFileFound [boolean or None], PoolFile object or None)
696 @type filename: string
697 @param filename: the filename of the file to check against the DB
700 @param filesize: the size of the file to check against the DB
703 @param md5sum: the md5sum of the file to check against the DB
705 @type location_id: int
706 @param location_id: the id of the location to look in
709 @return: Tuple of length 2.
710 If more than one file found with that name:
712 If valid pool file found: (True, PoolFile object)
713 If valid pool file not found:
714 (False, None) if no file found
715 (False, PoolFile object) if file found with size/md5sum mismatch
718 q = session.query(PoolFile).filter_by(filename=filename)
719 q = q.join(Location).filter_by(location_id=location_id)
729 if obj.md5sum != md5sum or obj.filesize != filesize:
737 __all__.append('check_poolfile')
740 def get_poolfile_by_id(file_id, session=None):
742 Returns a PoolFile objects or None for the given id
745 @param file_id: the id of the file to look for
747 @rtype: PoolFile or None
748 @return: either the PoolFile object or None
751 q = session.query(PoolFile).filter_by(file_id=file_id)
755 except NoResultFound:
758 __all__.append('get_poolfile_by_id')
762 def get_poolfile_by_name(filename, location_id=None, session=None):
764 Returns an array of PoolFile objects for the given filename and
765 (optionally) location_id
767 @type filename: string
768 @param filename: the filename of the file to check against the DB
770 @type location_id: int
771 @param location_id: the id of the location to look in (optional)
774 @return: array of PoolFile objects
777 q = session.query(PoolFile).filter_by(filename=filename)
779 if location_id is not None:
780 q = q.join(Location).filter_by(location_id=location_id)
784 __all__.append('get_poolfile_by_name')
787 def get_poolfile_like_name(filename, session=None):
789 Returns an array of PoolFile objects which are like the given name
791 @type filename: string
792 @param filename: the filename of the file to check against the DB
795 @return: array of PoolFile objects
798 # TODO: There must be a way of properly using bind parameters with %FOO%
799 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
803 __all__.append('get_poolfile_like_name')
805 ################################################################################
807 class Fingerprint(object):
808 def __init__(self, *args, **kwargs):
812 return '<Fingerprint %s>' % self.fingerprint
814 __all__.append('Fingerprint')
817 def get_or_set_fingerprint(fpr, session=None):
819 Returns Fingerprint object for given fpr.
821 If no matching fpr is found, a row is inserted.
824 @param fpr: The fpr to find / add
826 @type session: SQLAlchemy
827 @param session: Optional SQL session object (a temporary one will be
828 generated if not supplied). If not passed, a commit will be performed at
829 the end of the function, otherwise the caller is responsible for commiting.
830 A flush will be performed either way.
833 @return: the Fingerprint object for the given fpr
836 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
840 except NoResultFound:
841 fingerprint = Fingerprint()
842 fingerprint.fingerprint = fpr
843 session.add(fingerprint)
844 session.commit_or_flush()
849 __all__.append('get_or_set_fingerprint')
851 ################################################################################
853 class Keyring(object):
854 def __init__(self, *args, **kwargs):
858 return '<Keyring %s>' % self.keyring_name
860 __all__.append('Keyring')
863 def get_or_set_keyring(keyring, session=None):
865 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
866 and return the new Keyring
867 If C{keyring} already has an entry, simply return the existing Keyring
869 @type keyring: string
870 @param keyring: the keyring name
873 @return: the Keyring object for this keyring
876 q = session.query(Keyring).filter_by(keyring_name=keyring)
880 except NoResultFound:
881 obj = Keyring(keyring_name=keyring)
883 session.commit_or_flush()
886 __all__.append('get_or_set_keyring')
888 ################################################################################
890 class Location(object):
891 def __init__(self, *args, **kwargs):
895 return '<Location %s (%s)>' % (self.path, self.location_id)
897 __all__.append('Location')
900 def get_location(location, component=None, archive=None, session=None):
902 Returns Location object for the given combination of location, component
905 @type location: string
906 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
908 @type component: string
909 @param component: the component name (if None, no restriction applied)
911 @type archive: string
912 @param archive_id: the archive name (if None, no restriction applied)
914 @rtype: Location / None
915 @return: Either a Location object or None if one can't be found
918 q = session.query(Location).filter_by(path=location)
920 if archive is not None:
921 q = q.join(Archive).filter_by(archive_name=archive)
923 if component is not None:
924 q = q.join(Component).filter_by(component_name=component)
928 except NoResultFound:
931 __all__.append('get_location')
933 ################################################################################
935 class Maintainer(object):
936 def __init__(self, *args, **kwargs):
940 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
942 def get_split_maintainer(self):
943 if not hasattr(self, 'name') or self.name is None:
944 return ('', '', '', '')
946 return fix_maintainer(self.name.strip())
948 __all__.append('Maintainer')
951 def get_or_set_maintainer(name, session=None):
953 Returns Maintainer object for given maintainer name.
955 If no matching maintainer name is found, a row is inserted.
958 @param name: The maintainer name to add
960 @type session: SQLAlchemy
961 @param session: Optional SQL session object (a temporary one will be
962 generated if not supplied). If not passed, a commit will be performed at
963 the end of the function, otherwise the caller is responsible for commiting.
964 A flush will be performed either way.
967 @return: the Maintainer object for the given maintainer
970 q = session.query(Maintainer).filter_by(name=name)
973 except NoResultFound:
974 maintainer = Maintainer()
975 maintainer.name = name
976 session.add(maintainer)
977 session.commit_or_flush()
982 __all__.append('get_or_set_maintainer')
985 def get_maintainer(maintainer_id, session=None):
987 Return the name of the maintainer behind C{maintainer_id} or None if that
988 maintainer_id is invalid.
990 @type maintainer_id: int
991 @param maintainer_id: the id of the maintainer
994 @return: the Maintainer with this C{maintainer_id}
997 return session.query(Maintainer).get(maintainer_id)
999 __all__.append('get_maintainer')
1001 ################################################################################
1003 class NewComment(object):
1004 def __init__(self, *args, **kwargs):
1008 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1010 __all__.append('NewComment')
1013 def has_new_comment(package, version, session=None):
1015 Returns true if the given combination of C{package}, C{version} has a comment.
1017 @type package: string
1018 @param package: name of the package
1020 @type version: string
1021 @param version: package version
1023 @type session: Session
1024 @param session: Optional SQLA session object (a temporary one will be
1025 generated if not supplied)
1031 q = session.query(NewComment)
1032 q = q.filter_by(package=package)
1033 q = q.filter_by(version=version)
1035 return bool(q.count() > 0)
1037 __all__.append('has_new_comment')
1040 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1042 Returns (possibly empty) list of NewComment objects for the given
1045 @type package: string (optional)
1046 @param package: name of the package
1048 @type version: string (optional)
1049 @param version: package version
1051 @type comment_id: int (optional)
1052 @param comment_id: An id of a comment
1054 @type session: Session
1055 @param session: Optional SQLA session object (a temporary one will be
1056 generated if not supplied)
1059 @return: A (possibly empty) list of NewComment objects will be returned
1062 q = session.query(NewComment)
1063 if package is not None: q = q.filter_by(package=package)
1064 if version is not None: q = q.filter_by(version=version)
1065 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1069 __all__.append('get_new_comments')
1071 ################################################################################
1073 class Override(object):
1074 def __init__(self, *args, **kwargs):
1078 return '<Override %s (%s)>' % (self.package, self.suite_id)
1080 __all__.append('Override')
1083 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1085 Returns Override object for the given parameters
1087 @type package: string
1088 @param package: The name of the package
1090 @type suite: string, list or None
1091 @param suite: The name of the suite (or suites if a list) to limit to. If
1092 None, don't limit. Defaults to None.
1094 @type component: string, list or None
1095 @param component: The name of the component (or components if a list) to
1096 limit to. If None, don't limit. Defaults to None.
1098 @type overridetype: string, list or None
1099 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1100 limit to. If None, don't limit. Defaults to None.
1102 @type session: Session
1103 @param session: Optional SQLA session object (a temporary one will be
1104 generated if not supplied)
1107 @return: A (possibly empty) list of Override objects will be returned
1110 q = session.query(Override)
1111 q = q.filter_by(package=package)
1113 if suite is not None:
1114 if not isinstance(suite, list): suite = [suite]
1115 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1117 if component is not None:
1118 if not isinstance(component, list): component = [component]
1119 q = q.join(Component).filter(Component.component_name.in_(component))
1121 if overridetype is not None:
1122 if not isinstance(overridetype, list): overridetype = [overridetype]
1123 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1127 __all__.append('get_override')
1130 ################################################################################
1132 class OverrideType(object):
1133 def __init__(self, *args, **kwargs):
1137 return '<OverrideType %s>' % self.overridetype
1139 __all__.append('OverrideType')
1142 def get_override_type(override_type, session=None):
1144 Returns OverrideType object for given C{override type}.
1146 @type override_type: string
1147 @param override_type: The name of the override type
1149 @type session: Session
1150 @param session: Optional SQLA session object (a temporary one will be
1151 generated if not supplied)
1154 @return: the database id for the given override type
1157 q = session.query(OverrideType).filter_by(overridetype=override_type)
1161 except NoResultFound:
1164 __all__.append('get_override_type')
1166 ################################################################################
1168 class PendingContentAssociation(object):
1169 def __init__(self, *args, **kwargs):
1173 return '<PendingContentAssociation %s>' % self.pca_id
1175 __all__.append('PendingContentAssociation')
1177 def insert_pending_content_paths(package, fullpaths, session=None):
1179 Make sure given paths are temporarily associated with given
1183 @param package: the package to associate with should have been read in from the binary control file
1184 @type fullpaths: list
1185 @param fullpaths: the list of paths of the file being associated with the binary
1186 @type session: SQLAlchemy session
1187 @param session: Optional SQLAlchemy session. If this is passed, the caller
1188 is responsible for ensuring a transaction has begun and committing the
1189 results or rolling back based on the result code. If not passed, a commit
1190 will be performed at the end of the function
1192 @return: True upon success, False if there is a problem
1195 privatetrans = False
1198 session = DBConn().session()
1202 arch = get_architecture(package['Architecture'], session)
1203 arch_id = arch.arch_id
1205 # Remove any already existing recorded files for this package
1206 q = session.query(PendingContentAssociation)
1207 q = q.filter_by(package=package['Package'])
1208 q = q.filter_by(version=package['Version'])
1209 q = q.filter_by(architecture=arch_id)
1214 for fullpath in fullpaths:
1215 (path, file) = os.path.split(fullpath)
1217 if path.startswith( "./" ):
1220 filepath_id = get_or_set_contents_path_id(path, session)
1221 filename_id = get_or_set_contents_file_id(file, session)
1223 pathcache[fullpath] = (filepath_id, filename_id)
1225 for fullpath, dat in pathcache.items():
1226 pca = PendingContentAssociation()
1227 pca.package = package['Package']
1228 pca.version = package['Version']
1229 pca.filepath_id = dat[0]
1230 pca.filename_id = dat[1]
1231 pca.architecture = arch_id
1234 # Only commit if we set up the session ourself
1242 except Exception, e:
1243 traceback.print_exc()
1245 # Only rollback if we set up the session ourself
1252 __all__.append('insert_pending_content_paths')
1254 ################################################################################
1256 class Priority(object):
1257 def __init__(self, *args, **kwargs):
1260 def __eq__(self, val):
1261 if isinstance(val, str):
1262 return (self.priority == val)
1263 # This signals to use the normal comparison operator
1264 return NotImplemented
1266 def __ne__(self, val):
1267 if isinstance(val, str):
1268 return (self.priority != val)
1269 # This signals to use the normal comparison operator
1270 return NotImplemented
1273 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1275 __all__.append('Priority')
1278 def get_priority(priority, session=None):
1280 Returns Priority object for given C{priority name}.
1282 @type priority: string
1283 @param priority: The name of the priority
1285 @type session: Session
1286 @param session: Optional SQLA session object (a temporary one will be
1287 generated if not supplied)
1290 @return: Priority object for the given priority
1293 q = session.query(Priority).filter_by(priority=priority)
1297 except NoResultFound:
1300 __all__.append('get_priority')
1303 def get_priorities(session=None):
1305 Returns dictionary of priority names -> id mappings
1307 @type session: Session
1308 @param session: Optional SQL session object (a temporary one will be
1309 generated if not supplied)
1312 @return: dictionary of priority names -> id mappings
1316 q = session.query(Priority)
1318 ret[x.priority] = x.priority_id
1322 __all__.append('get_priorities')
1324 ################################################################################
1326 class Queue(object):
1327 def __init__(self, *args, **kwargs):
1331 return '<Queue %s>' % self.queue_name
1333 def autobuild_upload(self, changes, srcpath, session=None):
1335 Update queue_build database table used for incoming autobuild support.
1337 @type changes: Changes
1338 @param changes: changes object for the upload to process
1340 @type srcpath: string
1341 @param srcpath: path for the queue file entries/link destinations
1343 @type session: SQLAlchemy session
1344 @param session: Optional SQLAlchemy session. If this is passed, the
1345 caller is responsible for ensuring a transaction has begun and
1346 committing the results or rolling back based on the result code. If
1347 not passed, a commit will be performed at the end of the function,
1348 otherwise the caller is responsible for commiting.
1350 @rtype: NoneType or string
1351 @return: None if the operation failed, a string describing the error if not
1354 privatetrans = False
1356 session = DBConn().session()
1359 # TODO: Remove by moving queue config into the database
1362 for suitename in changes.changes["distribution"].keys():
1363 # TODO: Move into database as:
1364 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1365 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1366 # This also gets rid of the SecurityQueueBuild hack below
1367 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1371 s = get_suite(suitename, session)
1373 return "INTERNAL ERROR: Could not find suite %s" % suitename
1375 # TODO: Get from database as above
1376 dest_dir = conf["Dir::QueueBuild"]
1378 # TODO: Move into database as above
1379 if conf.FindB("Dinstall::SecurityQueueBuild"):
1380 dest_dir = os.path.join(dest_dir, suitename)
1382 for file_entry in changes.files.keys():
1383 src = os.path.join(srcpath, file_entry)
1384 dest = os.path.join(dest_dir, file_entry)
1386 # TODO: Move into database as above
1387 if conf.FindB("Dinstall::SecurityQueueBuild"):
1388 # Copy it since the original won't be readable by www-data
1390 utils.copy(src, dest)
1392 # Create a symlink to it
1393 os.symlink(src, dest)
1396 qb.suite_id = s.suite_id
1397 qb.queue_id = self.queue_id
1403 exists, symlinked = utils.ensure_orig_files(changes, dest, session)
1405 # Add symlinked files to the list of packages for later processing
1407 for filename in symlinked:
1409 qb.suite_id = s.suite_id
1410 qb.queue_id = self.queue_id
1411 qb.filename = filename
1415 # Update files to ensure they are not removed prematurely
1416 for filename in exists:
1417 qb = get_queue_build(filename, s.suite_id, session)
1429 __all__.append('Queue')
1432 def get_or_set_queue(queuename, session=None):
1434 Returns Queue object for given C{queue name}, creating it if it does not
1437 @type queuename: string
1438 @param queuename: The name of the queue
1440 @type session: Session
1441 @param session: Optional SQLA session object (a temporary one will be
1442 generated if not supplied)
1445 @return: Queue object for the given queue
1448 q = session.query(Queue).filter_by(queue_name=queuename)
1452 except NoResultFound:
1454 queue.queue_name = queuename
1456 session.commit_or_flush()
1461 __all__.append('get_or_set_queue')
1463 ################################################################################
1465 class QueueBuild(object):
1466 def __init__(self, *args, **kwargs):
1470 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1472 __all__.append('QueueBuild')
1475 def get_queue_build(filename, suite, session=None):
1477 Returns QueueBuild object for given C{filename} and C{suite}.
1479 @type filename: string
1480 @param filename: The name of the file
1482 @type suiteid: int or str
1483 @param suiteid: Suite name or ID
1485 @type session: Session
1486 @param session: Optional SQLA session object (a temporary one will be
1487 generated if not supplied)
1490 @return: Queue object for the given queue
1493 if isinstance(suite, int):
1494 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1496 q = session.query(QueueBuild).filter_by(filename=filename)
1497 q = q.join(Suite).filter_by(suite_name=suite)
1501 except NoResultFound:
1504 __all__.append('get_queue_build')
1506 ################################################################################
1508 class Section(object):
1509 def __init__(self, *args, **kwargs):
1512 def __eq__(self, val):
1513 if isinstance(val, str):
1514 return (self.section == val)
1515 # This signals to use the normal comparison operator
1516 return NotImplemented
1518 def __ne__(self, val):
1519 if isinstance(val, str):
1520 return (self.section != val)
1521 # This signals to use the normal comparison operator
1522 return NotImplemented
1525 return '<Section %s>' % self.section
1527 __all__.append('Section')
1530 def get_section(section, session=None):
1532 Returns Section object for given C{section name}.
1534 @type section: string
1535 @param section: The name of the section
1537 @type session: Session
1538 @param session: Optional SQLA session object (a temporary one will be
1539 generated if not supplied)
1542 @return: Section object for the given section name
1545 q = session.query(Section).filter_by(section=section)
1549 except NoResultFound:
1552 __all__.append('get_section')
1555 def get_sections(session=None):
1557 Returns dictionary of section names -> id mappings
1559 @type session: Session
1560 @param session: Optional SQL session object (a temporary one will be
1561 generated if not supplied)
1564 @return: dictionary of section names -> id mappings
1568 q = session.query(Section)
1570 ret[x.section] = x.section_id
1574 __all__.append('get_sections')
1576 ################################################################################
1578 class DBSource(object):
1579 def __init__(self, *args, **kwargs):
1583 return '<DBSource %s (%s)>' % (self.source, self.version)
1585 __all__.append('DBSource')
1588 def source_exists(source, source_version, suites = ["any"], session=None):
1590 Ensure that source exists somewhere in the archive for the binary
1591 upload being processed.
1592 1. exact match => 1.0-3
1593 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1595 @type package: string
1596 @param package: package source name
1598 @type source_version: string
1599 @param source_version: expected source version
1602 @param suites: list of suites to check in, default I{any}
1604 @type session: Session
1605 @param session: Optional SQLA session object (a temporary one will be
1606 generated if not supplied)
1609 @return: returns 1 if a source with expected version is found, otherwise 0
1616 for suite in suites:
1617 q = session.query(DBSource).filter_by(source=source)
1619 # source must exist in suite X, or in some other suite that's
1620 # mapped to X, recursively... silent-maps are counted too,
1621 # unreleased-maps aren't.
1622 maps = cnf.ValueList("SuiteMappings")[:]
1624 maps = [ m.split() for m in maps ]
1625 maps = [ (x[1], x[2]) for x in maps
1626 if x[0] == "map" or x[0] == "silent-map" ]
1629 if x[1] in s and x[0] not in s:
1632 q = q.join(SrcAssociation).join(Suite)
1633 q = q.filter(Suite.suite_name.in_(s))
1635 # Reduce the query results to a list of version numbers
1636 ql = [ j.version for j in q.all() ]
1639 if source_version in ql:
1643 from daklib.regexes import re_bin_only_nmu
1644 orig_source_version = re_bin_only_nmu.sub('', source_version)
1645 if orig_source_version in ql:
1648 # No source found so return not ok
1653 __all__.append('source_exists')
1656 def get_suites_source_in(source, session=None):
1658 Returns list of Suite objects which given C{source} name is in
1661 @param source: DBSource package name to search for
1664 @return: list of Suite objects for the given source
1667 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1669 __all__.append('get_suites_source_in')
1672 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1674 Returns list of DBSource objects for given C{source} name and other parameters
1677 @param source: DBSource package name to search for
1679 @type source: str or None
1680 @param source: DBSource version name to search for or None if not applicable
1682 @type dm_upload_allowed: bool
1683 @param dm_upload_allowed: If None, no effect. If True or False, only
1684 return packages with that dm_upload_allowed setting
1686 @type session: Session
1687 @param session: Optional SQL session object (a temporary one will be
1688 generated if not supplied)
1691 @return: list of DBSource objects for the given name (may be empty)
1694 q = session.query(DBSource).filter_by(source=source)
1696 if version is not None:
1697 q = q.filter_by(version=version)
1699 if dm_upload_allowed is not None:
1700 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1704 __all__.append('get_sources_from_name')
1707 def get_source_in_suite(source, suite, session=None):
1709 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1711 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1712 - B{suite} - a suite name, eg. I{unstable}
1714 @type source: string
1715 @param source: source package name
1718 @param suite: the suite name
1721 @return: the version for I{source} in I{suite}
1725 q = session.query(SrcAssociation)
1726 q = q.join('source').filter_by(source=source)
1727 q = q.join('suite').filter_by(suite_name=suite)
1730 return q.one().source
1731 except NoResultFound:
1734 __all__.append('get_source_in_suite')
1736 ################################################################################
1738 class SrcAssociation(object):
1739 def __init__(self, *args, **kwargs):
1743 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1745 __all__.append('SrcAssociation')
1747 ################################################################################
1749 class SrcFormat(object):
1750 def __init__(self, *args, **kwargs):
1754 return '<SrcFormat %s>' % (self.format_name)
1756 __all__.append('SrcFormat')
1758 ################################################################################
1760 class SrcUploader(object):
1761 def __init__(self, *args, **kwargs):
1765 return '<SrcUploader %s>' % self.uploader_id
1767 __all__.append('SrcUploader')
1769 ################################################################################
1771 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1772 ('SuiteID', 'suite_id'),
1773 ('Version', 'version'),
1774 ('Origin', 'origin'),
1776 ('Description', 'description'),
1777 ('Untouchable', 'untouchable'),
1778 ('Announce', 'announce'),
1779 ('Codename', 'codename'),
1780 ('OverrideCodename', 'overridecodename'),
1781 ('ValidTime', 'validtime'),
1782 ('Priority', 'priority'),
1783 ('NotAutomatic', 'notautomatic'),
1784 ('CopyChanges', 'copychanges'),
1785 ('CopyDotDak', 'copydotdak'),
1786 ('CommentsDir', 'commentsdir'),
1787 ('OverrideSuite', 'overridesuite'),
1788 ('ChangelogBase', 'changelogbase')]
1791 class Suite(object):
1792 def __init__(self, *args, **kwargs):
1796 return '<Suite %s>' % self.suite_name
1798 def __eq__(self, val):
1799 if isinstance(val, str):
1800 return (self.suite_name == val)
1801 # This signals to use the normal comparison operator
1802 return NotImplemented
1804 def __ne__(self, val):
1805 if isinstance(val, str):
1806 return (self.suite_name != val)
1807 # This signals to use the normal comparison operator
1808 return NotImplemented
1812 for disp, field in SUITE_FIELDS:
1813 val = getattr(self, field, None)
1815 ret.append("%s: %s" % (disp, val))
1817 return "\n".join(ret)
1819 __all__.append('Suite')
1822 def get_suite_architecture(suite, architecture, session=None):
1824 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1828 @param suite: Suite name to search for
1830 @type architecture: str
1831 @param architecture: Architecture name to search for
1833 @type session: Session
1834 @param session: Optional SQL session object (a temporary one will be
1835 generated if not supplied)
1837 @rtype: SuiteArchitecture
1838 @return: the SuiteArchitecture object or None
1841 q = session.query(SuiteArchitecture)
1842 q = q.join(Architecture).filter_by(arch_string=architecture)
1843 q = q.join(Suite).filter_by(suite_name=suite)
1847 except NoResultFound:
1850 __all__.append('get_suite_architecture')
1853 def get_suite(suite, session=None):
1855 Returns Suite object for given C{suite name}.
1858 @param suite: The name of the suite
1860 @type session: Session
1861 @param session: Optional SQLA session object (a temporary one will be
1862 generated if not supplied)
1865 @return: Suite object for the requested suite name (None if not present)
1868 q = session.query(Suite).filter_by(suite_name=suite)
1872 except NoResultFound:
1875 __all__.append('get_suite')
1877 ################################################################################
1879 class SuiteArchitecture(object):
1880 def __init__(self, *args, **kwargs):
1884 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1886 __all__.append('SuiteArchitecture')
1889 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1891 Returns list of Architecture objects for given C{suite} name
1894 @param source: Suite name to search for
1896 @type skipsrc: boolean
1897 @param skipsrc: Whether to skip returning the 'source' architecture entry
1900 @type skipall: boolean
1901 @param skipall: Whether to skip returning the 'all' architecture entry
1904 @type session: Session
1905 @param session: Optional SQL session object (a temporary one will be
1906 generated if not supplied)
1909 @return: list of Architecture objects for the given name (may be empty)
1912 q = session.query(Architecture)
1913 q = q.join(SuiteArchitecture)
1914 q = q.join(Suite).filter_by(suite_name=suite)
1917 q = q.filter(Architecture.arch_string != 'source')
1920 q = q.filter(Architecture.arch_string != 'all')
1922 q = q.order_by('arch_string')
1926 __all__.append('get_suite_architectures')
1928 ################################################################################
1930 class SuiteSrcFormat(object):
1931 def __init__(self, *args, **kwargs):
1935 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1937 __all__.append('SuiteSrcFormat')
1940 def get_suite_src_formats(suite, session=None):
1942 Returns list of allowed SrcFormat for C{suite}.
1945 @param suite: Suite name to search for
1947 @type session: Session
1948 @param session: Optional SQL session object (a temporary one will be
1949 generated if not supplied)
1952 @return: the list of allowed source formats for I{suite}
1955 q = session.query(SrcFormat)
1956 q = q.join(SuiteSrcFormat)
1957 q = q.join(Suite).filter_by(suite_name=suite)
1958 q = q.order_by('format_name')
1962 __all__.append('get_suite_src_formats')
1964 ################################################################################
1967 def __init__(self, *args, **kwargs):
1970 def __eq__(self, val):
1971 if isinstance(val, str):
1972 return (self.uid == val)
1973 # This signals to use the normal comparison operator
1974 return NotImplemented
1976 def __ne__(self, val):
1977 if isinstance(val, str):
1978 return (self.uid != val)
1979 # This signals to use the normal comparison operator
1980 return NotImplemented
1983 return '<Uid %s (%s)>' % (self.uid, self.name)
1985 __all__.append('Uid')
1988 def add_database_user(uidname, session=None):
1990 Adds a database user
1992 @type uidname: string
1993 @param uidname: The uid of the user to add
1995 @type session: SQLAlchemy
1996 @param session: Optional SQL session object (a temporary one will be
1997 generated if not supplied). If not passed, a commit will be performed at
1998 the end of the function, otherwise the caller is responsible for commiting.
2001 @return: the uid object for the given uidname
2004 session.execute("CREATE USER :uid", {'uid': uidname})
2005 session.commit_or_flush()
2007 __all__.append('add_database_user')
2010 def get_or_set_uid(uidname, session=None):
2012 Returns uid object for given uidname.
2014 If no matching uidname is found, a row is inserted.
2016 @type uidname: string
2017 @param uidname: The uid 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 q = session.query(Uid).filter_by(uid=uidname)
2032 except NoResultFound:
2036 session.commit_or_flush()
2041 __all__.append('get_or_set_uid')
2044 def get_uid_from_fingerprint(fpr, session=None):
2045 q = session.query(Uid)
2046 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2050 except NoResultFound:
2053 __all__.append('get_uid_from_fingerprint')
2055 ################################################################################
2057 class DBConn(Singleton):
2059 database module init.
2061 def __init__(self, *args, **kwargs):
2062 super(DBConn, self).__init__(*args, **kwargs)
2064 def _startup(self, *args, **kwargs):
2066 if kwargs.has_key('debug'):
2070 def __setuptables(self):
2071 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2072 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2073 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2074 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2075 self.tbl_component = Table('component', self.db_meta, autoload=True)
2076 self.tbl_config = Table('config', self.db_meta, autoload=True)
2077 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2078 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2079 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2080 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2081 self.tbl_files = Table('files', self.db_meta, autoload=True)
2082 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2083 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2084 self.tbl_location = Table('location', self.db_meta, autoload=True)
2085 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2086 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2087 self.tbl_override = Table('override', self.db_meta, autoload=True)
2088 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2089 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2090 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2091 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2092 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2093 self.tbl_section = Table('section', self.db_meta, autoload=True)
2094 self.tbl_source = Table('source', self.db_meta, autoload=True)
2095 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2096 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2097 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2098 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2099 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2100 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2101 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2103 def __setupmappers(self):
2104 mapper(Architecture, self.tbl_architecture,
2105 properties = dict(arch_id = self.tbl_architecture.c.id))
2107 mapper(Archive, self.tbl_archive,
2108 properties = dict(archive_id = self.tbl_archive.c.id,
2109 archive_name = self.tbl_archive.c.name))
2111 mapper(BinAssociation, self.tbl_bin_associations,
2112 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2113 suite_id = self.tbl_bin_associations.c.suite,
2114 suite = relation(Suite),
2115 binary_id = self.tbl_bin_associations.c.bin,
2116 binary = relation(DBBinary)))
2118 mapper(DBBinary, self.tbl_binaries,
2119 properties = dict(binary_id = self.tbl_binaries.c.id,
2120 package = self.tbl_binaries.c.package,
2121 version = self.tbl_binaries.c.version,
2122 maintainer_id = self.tbl_binaries.c.maintainer,
2123 maintainer = relation(Maintainer),
2124 source_id = self.tbl_binaries.c.source,
2125 source = relation(DBSource),
2126 arch_id = self.tbl_binaries.c.architecture,
2127 architecture = relation(Architecture),
2128 poolfile_id = self.tbl_binaries.c.file,
2129 poolfile = relation(PoolFile),
2130 binarytype = self.tbl_binaries.c.type,
2131 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2132 fingerprint = relation(Fingerprint),
2133 install_date = self.tbl_binaries.c.install_date,
2134 binassociations = relation(BinAssociation,
2135 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2137 mapper(Component, self.tbl_component,
2138 properties = dict(component_id = self.tbl_component.c.id,
2139 component_name = self.tbl_component.c.name))
2141 mapper(DBConfig, self.tbl_config,
2142 properties = dict(config_id = self.tbl_config.c.id))
2144 mapper(ContentAssociation, self.tbl_content_associations,
2145 properties = dict(ca_id = self.tbl_content_associations.c.id,
2146 filename_id = self.tbl_content_associations.c.filename,
2147 filename = relation(ContentFilename),
2148 filepath_id = self.tbl_content_associations.c.filepath,
2149 filepath = relation(ContentFilepath),
2150 binary_id = self.tbl_content_associations.c.binary_pkg,
2151 binary = relation(DBBinary)))
2154 mapper(ContentFilename, self.tbl_content_file_names,
2155 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2156 filename = self.tbl_content_file_names.c.file))
2158 mapper(ContentFilepath, self.tbl_content_file_paths,
2159 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2160 filepath = self.tbl_content_file_paths.c.path))
2162 mapper(DSCFile, self.tbl_dsc_files,
2163 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2164 source_id = self.tbl_dsc_files.c.source,
2165 source = relation(DBSource),
2166 poolfile_id = self.tbl_dsc_files.c.file,
2167 poolfile = relation(PoolFile)))
2169 mapper(PoolFile, self.tbl_files,
2170 properties = dict(file_id = self.tbl_files.c.id,
2171 filesize = self.tbl_files.c.size,
2172 location_id = self.tbl_files.c.location,
2173 location = relation(Location)))
2175 mapper(Fingerprint, self.tbl_fingerprint,
2176 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2177 uid_id = self.tbl_fingerprint.c.uid,
2178 uid = relation(Uid),
2179 keyring_id = self.tbl_fingerprint.c.keyring,
2180 keyring = relation(Keyring)))
2182 mapper(Keyring, self.tbl_keyrings,
2183 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2184 keyring_id = self.tbl_keyrings.c.id))
2186 mapper(Location, self.tbl_location,
2187 properties = dict(location_id = self.tbl_location.c.id,
2188 component_id = self.tbl_location.c.component,
2189 component = relation(Component),
2190 archive_id = self.tbl_location.c.archive,
2191 archive = relation(Archive),
2192 archive_type = self.tbl_location.c.type))
2194 mapper(Maintainer, self.tbl_maintainer,
2195 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2197 mapper(NewComment, self.tbl_new_comments,
2198 properties = dict(comment_id = self.tbl_new_comments.c.id))
2200 mapper(Override, self.tbl_override,
2201 properties = dict(suite_id = self.tbl_override.c.suite,
2202 suite = relation(Suite),
2203 component_id = self.tbl_override.c.component,
2204 component = relation(Component),
2205 priority_id = self.tbl_override.c.priority,
2206 priority = relation(Priority),
2207 section_id = self.tbl_override.c.section,
2208 section = relation(Section),
2209 overridetype_id = self.tbl_override.c.type,
2210 overridetype = relation(OverrideType)))
2212 mapper(OverrideType, self.tbl_override_type,
2213 properties = dict(overridetype = self.tbl_override_type.c.type,
2214 overridetype_id = self.tbl_override_type.c.id))
2216 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2217 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2218 filepath_id = self.tbl_pending_content_associations.c.filepath,
2219 filepath = relation(ContentFilepath),
2220 filename_id = self.tbl_pending_content_associations.c.filename,
2221 filename = relation(ContentFilename)))
2223 mapper(Priority, self.tbl_priority,
2224 properties = dict(priority_id = self.tbl_priority.c.id))
2226 mapper(Queue, self.tbl_queue,
2227 properties = dict(queue_id = self.tbl_queue.c.id))
2229 mapper(QueueBuild, self.tbl_queue_build,
2230 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2231 queue_id = self.tbl_queue_build.c.queue,
2232 queue = relation(Queue, backref='queuebuild')))
2234 mapper(Section, self.tbl_section,
2235 properties = dict(section_id = self.tbl_section.c.id))
2237 mapper(DBSource, self.tbl_source,
2238 properties = dict(source_id = self.tbl_source.c.id,
2239 version = self.tbl_source.c.version,
2240 maintainer_id = self.tbl_source.c.maintainer,
2241 maintainer = relation(Maintainer,
2242 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2243 poolfile_id = self.tbl_source.c.file,
2244 poolfile = relation(PoolFile),
2245 fingerprint_id = self.tbl_source.c.sig_fpr,
2246 fingerprint = relation(Fingerprint),
2247 changedby_id = self.tbl_source.c.changedby,
2248 changedby = relation(Maintainer,
2249 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2250 srcfiles = relation(DSCFile,
2251 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2252 srcassociations = relation(SrcAssociation,
2253 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2255 mapper(SrcAssociation, self.tbl_src_associations,
2256 properties = dict(sa_id = self.tbl_src_associations.c.id,
2257 suite_id = self.tbl_src_associations.c.suite,
2258 suite = relation(Suite),
2259 source_id = self.tbl_src_associations.c.source,
2260 source = relation(DBSource)))
2262 mapper(SrcFormat, self.tbl_src_format,
2263 properties = dict(src_format_id = self.tbl_src_format.c.id,
2264 format_name = self.tbl_src_format.c.format_name))
2266 mapper(SrcUploader, self.tbl_src_uploaders,
2267 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2268 source_id = self.tbl_src_uploaders.c.source,
2269 source = relation(DBSource,
2270 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2271 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2272 maintainer = relation(Maintainer,
2273 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2275 mapper(Suite, self.tbl_suite,
2276 properties = dict(suite_id = self.tbl_suite.c.id))
2278 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2279 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2280 suite = relation(Suite, backref='suitearchitectures'),
2281 arch_id = self.tbl_suite_architectures.c.architecture,
2282 architecture = relation(Architecture)))
2284 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2285 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2286 suite = relation(Suite, backref='suitesrcformats'),
2287 src_format_id = self.tbl_suite_src_formats.c.src_format,
2288 src_format = relation(SrcFormat)))
2290 mapper(Uid, self.tbl_uid,
2291 properties = dict(uid_id = self.tbl_uid.c.id,
2292 fingerprint = relation(Fingerprint)))
2294 ## Connection functions
2295 def __createconn(self):
2296 from config import Config
2300 connstr = "postgres://%s" % cnf["DB::Host"]
2301 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2302 connstr += ":%s" % cnf["DB::Port"]
2303 connstr += "/%s" % cnf["DB::Name"]
2306 connstr = "postgres:///%s" % cnf["DB::Name"]
2307 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2308 connstr += "?port=%s" % cnf["DB::Port"]
2310 self.db_pg = create_engine(connstr, echo=self.debug)
2311 self.db_meta = MetaData()
2312 self.db_meta.bind = self.db_pg
2313 self.db_smaker = sessionmaker(bind=self.db_pg,
2317 self.__setuptables()
2318 self.__setupmappers()
2321 return self.db_smaker()
2323 __all__.append('DBConn')