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 # Get the necessary IDs ...
614 (path, file) = os.path.split(fullpath)
616 filepath_id = get_or_set_contents_path_id(path, session)
617 filename_id = get_or_set_contents_file_id(file, session)
619 pathcache[fullpath] = (filepath_id, filename_id)
621 for fullpath, dat in pathcache.items():
622 ca = ContentAssociation()
623 ca.binary_id = binary_id
624 ca.filepath_id = dat[0]
625 ca.filename_id = dat[1]
628 # Only commit if we set up the session ourself
638 traceback.print_exc()
640 # Only rollback if we set up the session ourself
647 __all__.append('insert_content_paths')
649 ################################################################################
651 class DSCFile(object):
652 def __init__(self, *args, **kwargs):
656 return '<DSCFile %s>' % self.dscfile_id
658 __all__.append('DSCFile')
661 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
663 Returns a list of DSCFiles which may be empty
665 @type dscfile_id: int (optional)
666 @param dscfile_id: the dscfile_id of the DSCFiles to find
668 @type source_id: int (optional)
669 @param source_id: the source id related to the DSCFiles to find
671 @type poolfile_id: int (optional)
672 @param poolfile_id: the poolfile id related to the DSCFiles to find
675 @return: Possibly empty list of DSCFiles
678 q = session.query(DSCFile)
680 if dscfile_id is not None:
681 q = q.filter_by(dscfile_id=dscfile_id)
683 if source_id is not None:
684 q = q.filter_by(source_id=source_id)
686 if poolfile_id is not None:
687 q = q.filter_by(poolfile_id=poolfile_id)
691 __all__.append('get_dscfiles')
693 ################################################################################
695 class PoolFile(object):
696 def __init__(self, *args, **kwargs):
700 return '<PoolFile %s>' % self.filename
702 __all__.append('PoolFile')
705 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
708 (ValidFileFound [boolean or None], PoolFile object or None)
710 @type filename: string
711 @param filename: the filename of the file to check against the DB
714 @param filesize: the size of the file to check against the DB
717 @param md5sum: the md5sum of the file to check against the DB
719 @type location_id: int
720 @param location_id: the id of the location to look in
723 @return: Tuple of length 2.
724 If more than one file found with that name:
726 If valid pool file found: (True, PoolFile object)
727 If valid pool file not found:
728 (False, None) if no file found
729 (False, PoolFile object) if file found with size/md5sum mismatch
732 q = session.query(PoolFile).filter_by(filename=filename)
733 q = q.join(Location).filter_by(location_id=location_id)
743 if obj.md5sum != md5sum or obj.filesize != filesize:
751 __all__.append('check_poolfile')
754 def get_poolfile_by_id(file_id, session=None):
756 Returns a PoolFile objects or None for the given id
759 @param file_id: the id of the file to look for
761 @rtype: PoolFile or None
762 @return: either the PoolFile object or None
765 q = session.query(PoolFile).filter_by(file_id=file_id)
769 except NoResultFound:
772 __all__.append('get_poolfile_by_id')
776 def get_poolfile_by_name(filename, location_id=None, session=None):
778 Returns an array of PoolFile objects for the given filename and
779 (optionally) location_id
781 @type filename: string
782 @param filename: the filename of the file to check against the DB
784 @type location_id: int
785 @param location_id: the id of the location to look in (optional)
788 @return: array of PoolFile objects
791 q = session.query(PoolFile).filter_by(filename=filename)
793 if location_id is not None:
794 q = q.join(Location).filter_by(location_id=location_id)
798 __all__.append('get_poolfile_by_name')
801 def get_poolfile_like_name(filename, session=None):
803 Returns an array of PoolFile objects which are like the given name
805 @type filename: string
806 @param filename: the filename of the file to check against the DB
809 @return: array of PoolFile objects
812 # TODO: There must be a way of properly using bind parameters with %FOO%
813 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
817 __all__.append('get_poolfile_like_name')
819 ################################################################################
821 class Fingerprint(object):
822 def __init__(self, *args, **kwargs):
826 return '<Fingerprint %s>' % self.fingerprint
828 __all__.append('Fingerprint')
831 def get_or_set_fingerprint(fpr, session=None):
833 Returns Fingerprint object for given fpr.
835 If no matching fpr is found, a row is inserted.
838 @param fpr: The fpr to find / add
840 @type session: SQLAlchemy
841 @param session: Optional SQL session object (a temporary one will be
842 generated if not supplied). If not passed, a commit will be performed at
843 the end of the function, otherwise the caller is responsible for commiting.
844 A flush will be performed either way.
847 @return: the Fingerprint object for the given fpr
850 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
854 except NoResultFound:
855 fingerprint = Fingerprint()
856 fingerprint.fingerprint = fpr
857 session.add(fingerprint)
858 session.commit_or_flush()
863 __all__.append('get_or_set_fingerprint')
865 ################################################################################
867 class Keyring(object):
868 def __init__(self, *args, **kwargs):
872 return '<Keyring %s>' % self.keyring_name
874 __all__.append('Keyring')
877 def get_or_set_keyring(keyring, session=None):
879 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
880 and return the new Keyring
881 If C{keyring} already has an entry, simply return the existing Keyring
883 @type keyring: string
884 @param keyring: the keyring name
887 @return: the Keyring object for this keyring
890 q = session.query(Keyring).filter_by(keyring_name=keyring)
894 except NoResultFound:
895 obj = Keyring(keyring_name=keyring)
897 session.commit_or_flush()
900 __all__.append('get_or_set_keyring')
902 ################################################################################
904 class KnownChange(object):
905 def __init__(self, *args, **kwargs):
909 return '<KnownChange %s>' % self.changesname
911 __all__.append('KnownChange')
914 def get_knownchange(filename, session=None):
916 returns knownchange object for given C{filename}.
918 @type archive: string
919 @param archive: the name of the arhive
921 @type session: Session
922 @param session: Optional SQLA session object (a temporary one will be
923 generated if not supplied)
926 @return: Archive object for the given name (None if not present)
929 q = session.query(KnownChange).filter_by(changesname=filename)
933 except NoResultFound:
936 __all__.append('get_knownchange')
938 ################################################################################
939 class Location(object):
940 def __init__(self, *args, **kwargs):
944 return '<Location %s (%s)>' % (self.path, self.location_id)
946 __all__.append('Location')
949 def get_location(location, component=None, archive=None, session=None):
951 Returns Location object for the given combination of location, component
954 @type location: string
955 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
957 @type component: string
958 @param component: the component name (if None, no restriction applied)
960 @type archive: string
961 @param archive_id: the archive name (if None, no restriction applied)
963 @rtype: Location / None
964 @return: Either a Location object or None if one can't be found
967 q = session.query(Location).filter_by(path=location)
969 if archive is not None:
970 q = q.join(Archive).filter_by(archive_name=archive)
972 if component is not None:
973 q = q.join(Component).filter_by(component_name=component)
977 except NoResultFound:
980 __all__.append('get_location')
982 ################################################################################
984 class Maintainer(object):
985 def __init__(self, *args, **kwargs):
989 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
991 def get_split_maintainer(self):
992 if not hasattr(self, 'name') or self.name is None:
993 return ('', '', '', '')
995 return fix_maintainer(self.name.strip())
997 __all__.append('Maintainer')
1000 def get_or_set_maintainer(name, session=None):
1002 Returns Maintainer object for given maintainer name.
1004 If no matching maintainer name is found, a row is inserted.
1007 @param name: The maintainer name to add
1009 @type session: SQLAlchemy
1010 @param session: Optional SQL session object (a temporary one will be
1011 generated if not supplied). If not passed, a commit will be performed at
1012 the end of the function, otherwise the caller is responsible for commiting.
1013 A flush will be performed either way.
1016 @return: the Maintainer object for the given maintainer
1019 q = session.query(Maintainer).filter_by(name=name)
1022 except NoResultFound:
1023 maintainer = Maintainer()
1024 maintainer.name = name
1025 session.add(maintainer)
1026 session.commit_or_flush()
1031 __all__.append('get_or_set_maintainer')
1034 def get_maintainer(maintainer_id, session=None):
1036 Return the name of the maintainer behind C{maintainer_id} or None if that
1037 maintainer_id is invalid.
1039 @type maintainer_id: int
1040 @param maintainer_id: the id of the maintainer
1043 @return: the Maintainer with this C{maintainer_id}
1046 return session.query(Maintainer).get(maintainer_id)
1048 __all__.append('get_maintainer')
1050 ################################################################################
1052 class NewComment(object):
1053 def __init__(self, *args, **kwargs):
1057 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1059 __all__.append('NewComment')
1062 def has_new_comment(package, version, session=None):
1064 Returns true if the given combination of C{package}, C{version} has a comment.
1066 @type package: string
1067 @param package: name of the package
1069 @type version: string
1070 @param version: package version
1072 @type session: Session
1073 @param session: Optional SQLA session object (a temporary one will be
1074 generated if not supplied)
1080 q = session.query(NewComment)
1081 q = q.filter_by(package=package)
1082 q = q.filter_by(version=version)
1084 return bool(q.count() > 0)
1086 __all__.append('has_new_comment')
1089 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1091 Returns (possibly empty) list of NewComment objects for the given
1094 @type package: string (optional)
1095 @param package: name of the package
1097 @type version: string (optional)
1098 @param version: package version
1100 @type comment_id: int (optional)
1101 @param comment_id: An id of a comment
1103 @type session: Session
1104 @param session: Optional SQLA session object (a temporary one will be
1105 generated if not supplied)
1108 @return: A (possibly empty) list of NewComment objects will be returned
1111 q = session.query(NewComment)
1112 if package is not None: q = q.filter_by(package=package)
1113 if version is not None: q = q.filter_by(version=version)
1114 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1118 __all__.append('get_new_comments')
1120 ################################################################################
1122 class Override(object):
1123 def __init__(self, *args, **kwargs):
1127 return '<Override %s (%s)>' % (self.package, self.suite_id)
1129 __all__.append('Override')
1132 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1134 Returns Override object for the given parameters
1136 @type package: string
1137 @param package: The name of the package
1139 @type suite: string, list or None
1140 @param suite: The name of the suite (or suites if a list) to limit to. If
1141 None, don't limit. Defaults to None.
1143 @type component: string, list or None
1144 @param component: The name of the component (or components if a list) to
1145 limit to. If None, don't limit. Defaults to None.
1147 @type overridetype: string, list or None
1148 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1149 limit to. If None, don't limit. Defaults to None.
1151 @type session: Session
1152 @param session: Optional SQLA session object (a temporary one will be
1153 generated if not supplied)
1156 @return: A (possibly empty) list of Override objects will be returned
1159 q = session.query(Override)
1160 q = q.filter_by(package=package)
1162 if suite is not None:
1163 if not isinstance(suite, list): suite = [suite]
1164 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1166 if component is not None:
1167 if not isinstance(component, list): component = [component]
1168 q = q.join(Component).filter(Component.component_name.in_(component))
1170 if overridetype is not None:
1171 if not isinstance(overridetype, list): overridetype = [overridetype]
1172 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1176 __all__.append('get_override')
1179 ################################################################################
1181 class OverrideType(object):
1182 def __init__(self, *args, **kwargs):
1186 return '<OverrideType %s>' % self.overridetype
1188 __all__.append('OverrideType')
1191 def get_override_type(override_type, session=None):
1193 Returns OverrideType object for given C{override type}.
1195 @type override_type: string
1196 @param override_type: The name of the override type
1198 @type session: Session
1199 @param session: Optional SQLA session object (a temporary one will be
1200 generated if not supplied)
1203 @return: the database id for the given override type
1206 q = session.query(OverrideType).filter_by(overridetype=override_type)
1210 except NoResultFound:
1213 __all__.append('get_override_type')
1215 ################################################################################
1217 class PendingContentAssociation(object):
1218 def __init__(self, *args, **kwargs):
1222 return '<PendingContentAssociation %s>' % self.pca_id
1224 __all__.append('PendingContentAssociation')
1226 def insert_pending_content_paths(package, fullpaths, session=None):
1228 Make sure given paths are temporarily associated with given
1232 @param package: the package to associate with should have been read in from the binary control file
1233 @type fullpaths: list
1234 @param fullpaths: the list of paths of the file being associated with the binary
1235 @type session: SQLAlchemy session
1236 @param session: Optional SQLAlchemy session. If this is passed, the caller
1237 is responsible for ensuring a transaction has begun and committing the
1238 results or rolling back based on the result code. If not passed, a commit
1239 will be performed at the end of the function
1241 @return: True upon success, False if there is a problem
1244 privatetrans = False
1247 session = DBConn().session()
1251 arch = get_architecture(package['Architecture'], session)
1252 arch_id = arch.arch_id
1254 # Remove any already existing recorded files for this package
1255 q = session.query(PendingContentAssociation)
1256 q = q.filter_by(package=package['Package'])
1257 q = q.filter_by(version=package['Version'])
1258 q = q.filter_by(architecture=arch_id)
1263 for fullpath in fullpaths:
1264 (path, file) = os.path.split(fullpath)
1266 if path.startswith( "./" ):
1269 filepath_id = get_or_set_contents_path_id(path, session)
1270 filename_id = get_or_set_contents_file_id(file, session)
1272 pathcache[fullpath] = (filepath_id, filename_id)
1274 for fullpath, dat in pathcache.items():
1275 pca = PendingContentAssociation()
1276 pca.package = package['Package']
1277 pca.version = package['Version']
1278 pca.filepath_id = dat[0]
1279 pca.filename_id = dat[1]
1280 pca.architecture = arch_id
1283 # Only commit if we set up the session ourself
1291 except Exception, e:
1292 traceback.print_exc()
1294 # Only rollback if we set up the session ourself
1301 __all__.append('insert_pending_content_paths')
1303 ################################################################################
1305 class Priority(object):
1306 def __init__(self, *args, **kwargs):
1309 def __eq__(self, val):
1310 if isinstance(val, str):
1311 return (self.priority == val)
1312 # This signals to use the normal comparison operator
1313 return NotImplemented
1315 def __ne__(self, val):
1316 if isinstance(val, str):
1317 return (self.priority != val)
1318 # This signals to use the normal comparison operator
1319 return NotImplemented
1322 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1324 __all__.append('Priority')
1327 def get_priority(priority, session=None):
1329 Returns Priority object for given C{priority name}.
1331 @type priority: string
1332 @param priority: The name of the priority
1334 @type session: Session
1335 @param session: Optional SQLA session object (a temporary one will be
1336 generated if not supplied)
1339 @return: Priority object for the given priority
1342 q = session.query(Priority).filter_by(priority=priority)
1346 except NoResultFound:
1349 __all__.append('get_priority')
1352 def get_priorities(session=None):
1354 Returns dictionary of priority names -> id mappings
1356 @type session: Session
1357 @param session: Optional SQL session object (a temporary one will be
1358 generated if not supplied)
1361 @return: dictionary of priority names -> id mappings
1365 q = session.query(Priority)
1367 ret[x.priority] = x.priority_id
1371 __all__.append('get_priorities')
1373 ################################################################################
1375 class Queue(object):
1376 def __init__(self, *args, **kwargs):
1380 return '<Queue %s>' % self.queue_name
1382 def autobuild_upload(self, changes, srcpath, session=None):
1384 Update queue_build database table used for incoming autobuild support.
1386 @type changes: Changes
1387 @param changes: changes object for the upload to process
1389 @type srcpath: string
1390 @param srcpath: path for the queue file entries/link destinations
1392 @type session: SQLAlchemy session
1393 @param session: Optional SQLAlchemy session. If this is passed, the
1394 caller is responsible for ensuring a transaction has begun and
1395 committing the results or rolling back based on the result code. If
1396 not passed, a commit will be performed at the end of the function,
1397 otherwise the caller is responsible for commiting.
1399 @rtype: NoneType or string
1400 @return: None if the operation failed, a string describing the error if not
1403 privatetrans = False
1405 session = DBConn().session()
1408 # TODO: Remove by moving queue config into the database
1411 for suitename in changes.changes["distribution"].keys():
1412 # TODO: Move into database as:
1413 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1414 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1415 # This also gets rid of the SecurityQueueBuild hack below
1416 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1420 s = get_suite(suitename, session)
1422 return "INTERNAL ERROR: Could not find suite %s" % suitename
1424 # TODO: Get from database as above
1425 dest_dir = conf["Dir::QueueBuild"]
1427 # TODO: Move into database as above
1428 if conf.FindB("Dinstall::SecurityQueueBuild"):
1429 dest_dir = os.path.join(dest_dir, suitename)
1431 for file_entry in changes.files.keys():
1432 src = os.path.join(srcpath, file_entry)
1433 dest = os.path.join(dest_dir, file_entry)
1435 # TODO: Move into database as above
1436 if conf.FindB("Dinstall::SecurityQueueBuild"):
1437 # Copy it since the original won't be readable by www-data
1439 utils.copy(src, dest)
1441 # Create a symlink to it
1442 os.symlink(src, dest)
1445 qb.suite_id = s.suite_id
1446 qb.queue_id = self.queue_id
1452 # If the .orig tarballs are in the pool, create a symlink to
1453 # them (if one doesn't already exist)
1454 for dsc_file in changes.dsc_files.keys():
1455 # Skip all files except orig tarballs
1456 from daklib.regexes import re_is_orig_source
1457 if not re_is_orig_source.match(dsc_file):
1459 # Skip orig files not identified in the pool
1460 if not (changes.orig_files.has_key(dsc_file) and
1461 changes.orig_files[dsc_file].has_key("id")):
1463 orig_file_id = changes.orig_files[dsc_file]["id"]
1464 dest = os.path.join(dest_dir, dsc_file)
1466 # If it doesn't exist, create a symlink
1467 if not os.path.exists(dest):
1468 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1469 {'id': orig_file_id})
1472 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1474 src = os.path.join(res[0], res[1])
1475 os.symlink(src, dest)
1477 # Add it to the list of packages for later processing by apt-ftparchive
1479 qb.suite_id = s.suite_id
1480 qb.queue_id = self.queue_id
1485 # If it does, update things to ensure it's not removed prematurely
1487 qb = get_queue_build(dest, s.suite_id, session)
1499 __all__.append('Queue')
1502 def get_or_set_queue(queuename, session=None):
1504 Returns Queue object for given C{queue name}, creating it if it does not
1507 @type queuename: string
1508 @param queuename: The name of the queue
1510 @type session: Session
1511 @param session: Optional SQLA session object (a temporary one will be
1512 generated if not supplied)
1515 @return: Queue object for the given queue
1518 q = session.query(Queue).filter_by(queue_name=queuename)
1522 except NoResultFound:
1524 queue.queue_name = queuename
1526 session.commit_or_flush()
1531 __all__.append('get_or_set_queue')
1533 ################################################################################
1535 class QueueBuild(object):
1536 def __init__(self, *args, **kwargs):
1540 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1542 __all__.append('QueueBuild')
1545 def get_queue_build(filename, suite, session=None):
1547 Returns QueueBuild object for given C{filename} and C{suite}.
1549 @type filename: string
1550 @param filename: The name of the file
1552 @type suiteid: int or str
1553 @param suiteid: Suite name or ID
1555 @type session: Session
1556 @param session: Optional SQLA session object (a temporary one will be
1557 generated if not supplied)
1560 @return: Queue object for the given queue
1563 if isinstance(suite, int):
1564 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1566 q = session.query(QueueBuild).filter_by(filename=filename)
1567 q = q.join(Suite).filter_by(suite_name=suite)
1571 except NoResultFound:
1574 __all__.append('get_queue_build')
1576 ################################################################################
1578 class Section(object):
1579 def __init__(self, *args, **kwargs):
1582 def __eq__(self, val):
1583 if isinstance(val, str):
1584 return (self.section == val)
1585 # This signals to use the normal comparison operator
1586 return NotImplemented
1588 def __ne__(self, val):
1589 if isinstance(val, str):
1590 return (self.section != val)
1591 # This signals to use the normal comparison operator
1592 return NotImplemented
1595 return '<Section %s>' % self.section
1597 __all__.append('Section')
1600 def get_section(section, session=None):
1602 Returns Section object for given C{section name}.
1604 @type section: string
1605 @param section: The name of the section
1607 @type session: Session
1608 @param session: Optional SQLA session object (a temporary one will be
1609 generated if not supplied)
1612 @return: Section object for the given section name
1615 q = session.query(Section).filter_by(section=section)
1619 except NoResultFound:
1622 __all__.append('get_section')
1625 def get_sections(session=None):
1627 Returns dictionary of section names -> id mappings
1629 @type session: Session
1630 @param session: Optional SQL session object (a temporary one will be
1631 generated if not supplied)
1634 @return: dictionary of section names -> id mappings
1638 q = session.query(Section)
1640 ret[x.section] = x.section_id
1644 __all__.append('get_sections')
1646 ################################################################################
1648 class DBSource(object):
1649 def __init__(self, *args, **kwargs):
1653 return '<DBSource %s (%s)>' % (self.source, self.version)
1655 __all__.append('DBSource')
1658 def source_exists(source, source_version, suites = ["any"], session=None):
1660 Ensure that source exists somewhere in the archive for the binary
1661 upload being processed.
1662 1. exact match => 1.0-3
1663 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1665 @type package: string
1666 @param package: package source name
1668 @type source_version: string
1669 @param source_version: expected source version
1672 @param suites: list of suites to check in, default I{any}
1674 @type session: Session
1675 @param session: Optional SQLA session object (a temporary one will be
1676 generated if not supplied)
1679 @return: returns 1 if a source with expected version is found, otherwise 0
1686 for suite in suites:
1687 q = session.query(DBSource).filter_by(source=source)
1689 # source must exist in suite X, or in some other suite that's
1690 # mapped to X, recursively... silent-maps are counted too,
1691 # unreleased-maps aren't.
1692 maps = cnf.ValueList("SuiteMappings")[:]
1694 maps = [ m.split() for m in maps ]
1695 maps = [ (x[1], x[2]) for x in maps
1696 if x[0] == "map" or x[0] == "silent-map" ]
1699 if x[1] in s and x[0] not in s:
1702 q = q.join(SrcAssociation).join(Suite)
1703 q = q.filter(Suite.suite_name.in_(s))
1705 # Reduce the query results to a list of version numbers
1706 ql = [ j.version for j in q.all() ]
1709 if source_version in ql:
1713 from daklib.regexes import re_bin_only_nmu
1714 orig_source_version = re_bin_only_nmu.sub('', source_version)
1715 if orig_source_version in ql:
1718 # No source found so return not ok
1723 __all__.append('source_exists')
1726 def get_suites_source_in(source, session=None):
1728 Returns list of Suite objects which given C{source} name is in
1731 @param source: DBSource package name to search for
1734 @return: list of Suite objects for the given source
1737 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1739 __all__.append('get_suites_source_in')
1742 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1744 Returns list of DBSource objects for given C{source} name and other parameters
1747 @param source: DBSource package name to search for
1749 @type source: str or None
1750 @param source: DBSource version name to search for or None if not applicable
1752 @type dm_upload_allowed: bool
1753 @param dm_upload_allowed: If None, no effect. If True or False, only
1754 return packages with that dm_upload_allowed setting
1756 @type session: Session
1757 @param session: Optional SQL session object (a temporary one will be
1758 generated if not supplied)
1761 @return: list of DBSource objects for the given name (may be empty)
1764 q = session.query(DBSource).filter_by(source=source)
1766 if version is not None:
1767 q = q.filter_by(version=version)
1769 if dm_upload_allowed is not None:
1770 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1774 __all__.append('get_sources_from_name')
1777 def get_source_in_suite(source, suite, session=None):
1779 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1781 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1782 - B{suite} - a suite name, eg. I{unstable}
1784 @type source: string
1785 @param source: source package name
1788 @param suite: the suite name
1791 @return: the version for I{source} in I{suite}
1795 q = session.query(SrcAssociation)
1796 q = q.join('source').filter_by(source=source)
1797 q = q.join('suite').filter_by(suite_name=suite)
1800 return q.one().source
1801 except NoResultFound:
1804 __all__.append('get_source_in_suite')
1806 ################################################################################
1808 class SrcAssociation(object):
1809 def __init__(self, *args, **kwargs):
1813 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1815 __all__.append('SrcAssociation')
1817 ################################################################################
1819 class SrcFormat(object):
1820 def __init__(self, *args, **kwargs):
1824 return '<SrcFormat %s>' % (self.format_name)
1826 __all__.append('SrcFormat')
1828 ################################################################################
1830 class SrcUploader(object):
1831 def __init__(self, *args, **kwargs):
1835 return '<SrcUploader %s>' % self.uploader_id
1837 __all__.append('SrcUploader')
1839 ################################################################################
1841 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1842 ('SuiteID', 'suite_id'),
1843 ('Version', 'version'),
1844 ('Origin', 'origin'),
1846 ('Description', 'description'),
1847 ('Untouchable', 'untouchable'),
1848 ('Announce', 'announce'),
1849 ('Codename', 'codename'),
1850 ('OverrideCodename', 'overridecodename'),
1851 ('ValidTime', 'validtime'),
1852 ('Priority', 'priority'),
1853 ('NotAutomatic', 'notautomatic'),
1854 ('CopyChanges', 'copychanges'),
1855 ('CopyDotDak', 'copydotdak'),
1856 ('CommentsDir', 'commentsdir'),
1857 ('OverrideSuite', 'overridesuite'),
1858 ('ChangelogBase', 'changelogbase')]
1861 class Suite(object):
1862 def __init__(self, *args, **kwargs):
1866 return '<Suite %s>' % self.suite_name
1868 def __eq__(self, val):
1869 if isinstance(val, str):
1870 return (self.suite_name == val)
1871 # This signals to use the normal comparison operator
1872 return NotImplemented
1874 def __ne__(self, val):
1875 if isinstance(val, str):
1876 return (self.suite_name != val)
1877 # This signals to use the normal comparison operator
1878 return NotImplemented
1882 for disp, field in SUITE_FIELDS:
1883 val = getattr(self, field, None)
1885 ret.append("%s: %s" % (disp, val))
1887 return "\n".join(ret)
1889 __all__.append('Suite')
1892 def get_suite_architecture(suite, architecture, session=None):
1894 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1898 @param suite: Suite name to search for
1900 @type architecture: str
1901 @param architecture: Architecture name to search for
1903 @type session: Session
1904 @param session: Optional SQL session object (a temporary one will be
1905 generated if not supplied)
1907 @rtype: SuiteArchitecture
1908 @return: the SuiteArchitecture object or None
1911 q = session.query(SuiteArchitecture)
1912 q = q.join(Architecture).filter_by(arch_string=architecture)
1913 q = q.join(Suite).filter_by(suite_name=suite)
1917 except NoResultFound:
1920 __all__.append('get_suite_architecture')
1923 def get_suite(suite, session=None):
1925 Returns Suite object for given C{suite name}.
1928 @param suite: The name of the suite
1930 @type session: Session
1931 @param session: Optional SQLA session object (a temporary one will be
1932 generated if not supplied)
1935 @return: Suite object for the requested suite name (None if not present)
1938 q = session.query(Suite).filter_by(suite_name=suite)
1942 except NoResultFound:
1945 __all__.append('get_suite')
1947 ################################################################################
1949 class SuiteArchitecture(object):
1950 def __init__(self, *args, **kwargs):
1954 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1956 __all__.append('SuiteArchitecture')
1959 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1961 Returns list of Architecture objects for given C{suite} name
1964 @param source: Suite name to search for
1966 @type skipsrc: boolean
1967 @param skipsrc: Whether to skip returning the 'source' architecture entry
1970 @type skipall: boolean
1971 @param skipall: Whether to skip returning the 'all' architecture entry
1974 @type session: Session
1975 @param session: Optional SQL session object (a temporary one will be
1976 generated if not supplied)
1979 @return: list of Architecture objects for the given name (may be empty)
1982 q = session.query(Architecture)
1983 q = q.join(SuiteArchitecture)
1984 q = q.join(Suite).filter_by(suite_name=suite)
1987 q = q.filter(Architecture.arch_string != 'source')
1990 q = q.filter(Architecture.arch_string != 'all')
1992 q = q.order_by('arch_string')
1996 __all__.append('get_suite_architectures')
1998 ################################################################################
2000 class SuiteSrcFormat(object):
2001 def __init__(self, *args, **kwargs):
2005 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2007 __all__.append('SuiteSrcFormat')
2010 def get_suite_src_formats(suite, session=None):
2012 Returns list of allowed SrcFormat for C{suite}.
2015 @param suite: Suite name to search for
2017 @type session: Session
2018 @param session: Optional SQL session object (a temporary one will be
2019 generated if not supplied)
2022 @return: the list of allowed source formats for I{suite}
2025 q = session.query(SrcFormat)
2026 q = q.join(SuiteSrcFormat)
2027 q = q.join(Suite).filter_by(suite_name=suite)
2028 q = q.order_by('format_name')
2032 __all__.append('get_suite_src_formats')
2034 ################################################################################
2037 def __init__(self, *args, **kwargs):
2040 def __eq__(self, val):
2041 if isinstance(val, str):
2042 return (self.uid == val)
2043 # This signals to use the normal comparison operator
2044 return NotImplemented
2046 def __ne__(self, val):
2047 if isinstance(val, str):
2048 return (self.uid != val)
2049 # This signals to use the normal comparison operator
2050 return NotImplemented
2053 return '<Uid %s (%s)>' % (self.uid, self.name)
2055 __all__.append('Uid')
2058 def add_database_user(uidname, session=None):
2060 Adds a database user
2062 @type uidname: string
2063 @param uidname: The uid of the user to add
2065 @type session: SQLAlchemy
2066 @param session: Optional SQL session object (a temporary one will be
2067 generated if not supplied). If not passed, a commit will be performed at
2068 the end of the function, otherwise the caller is responsible for commiting.
2071 @return: the uid object for the given uidname
2074 session.execute("CREATE USER :uid", {'uid': uidname})
2075 session.commit_or_flush()
2077 __all__.append('add_database_user')
2080 def get_or_set_uid(uidname, session=None):
2082 Returns uid object for given uidname.
2084 If no matching uidname is found, a row is inserted.
2086 @type uidname: string
2087 @param uidname: The uid to add
2089 @type session: SQLAlchemy
2090 @param session: Optional SQL session object (a temporary one will be
2091 generated if not supplied). If not passed, a commit will be performed at
2092 the end of the function, otherwise the caller is responsible for commiting.
2095 @return: the uid object for the given uidname
2098 q = session.query(Uid).filter_by(uid=uidname)
2102 except NoResultFound:
2106 session.commit_or_flush()
2111 __all__.append('get_or_set_uid')
2114 def get_uid_from_fingerprint(fpr, session=None):
2115 q = session.query(Uid)
2116 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2120 except NoResultFound:
2123 __all__.append('get_uid_from_fingerprint')
2125 ################################################################################
2127 class DBConn(Singleton):
2129 database module init.
2131 def __init__(self, *args, **kwargs):
2132 super(DBConn, self).__init__(*args, **kwargs)
2134 def _startup(self, *args, **kwargs):
2136 if kwargs.has_key('debug'):
2140 def __setuptables(self):
2141 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2142 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2143 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2144 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2145 self.tbl_component = Table('component', self.db_meta, autoload=True)
2146 self.tbl_config = Table('config', self.db_meta, autoload=True)
2147 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2148 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2149 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2150 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2151 self.tbl_files = Table('files', self.db_meta, autoload=True)
2152 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2153 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2154 self.tbl_known_changes = Table('known_changes', self.db_meta, autoload=True)
2155 self.tbl_location = Table('location', self.db_meta, autoload=True)
2156 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2157 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2158 self.tbl_override = Table('override', self.db_meta, autoload=True)
2159 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2160 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2161 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2162 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2163 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2164 self.tbl_section = Table('section', self.db_meta, autoload=True)
2165 self.tbl_source = Table('source', self.db_meta, autoload=True)
2166 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2167 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2168 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2169 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2170 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2171 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2172 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2174 def __setupmappers(self):
2175 mapper(Architecture, self.tbl_architecture,
2176 properties = dict(arch_id = self.tbl_architecture.c.id))
2178 mapper(Archive, self.tbl_archive,
2179 properties = dict(archive_id = self.tbl_archive.c.id,
2180 archive_name = self.tbl_archive.c.name))
2182 mapper(BinAssociation, self.tbl_bin_associations,
2183 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2184 suite_id = self.tbl_bin_associations.c.suite,
2185 suite = relation(Suite),
2186 binary_id = self.tbl_bin_associations.c.bin,
2187 binary = relation(DBBinary)))
2189 mapper(DBBinary, self.tbl_binaries,
2190 properties = dict(binary_id = self.tbl_binaries.c.id,
2191 package = self.tbl_binaries.c.package,
2192 version = self.tbl_binaries.c.version,
2193 maintainer_id = self.tbl_binaries.c.maintainer,
2194 maintainer = relation(Maintainer),
2195 source_id = self.tbl_binaries.c.source,
2196 source = relation(DBSource),
2197 arch_id = self.tbl_binaries.c.architecture,
2198 architecture = relation(Architecture),
2199 poolfile_id = self.tbl_binaries.c.file,
2200 poolfile = relation(PoolFile),
2201 binarytype = self.tbl_binaries.c.type,
2202 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2203 fingerprint = relation(Fingerprint),
2204 install_date = self.tbl_binaries.c.install_date,
2205 binassociations = relation(BinAssociation,
2206 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2208 mapper(Component, self.tbl_component,
2209 properties = dict(component_id = self.tbl_component.c.id,
2210 component_name = self.tbl_component.c.name))
2212 mapper(DBConfig, self.tbl_config,
2213 properties = dict(config_id = self.tbl_config.c.id))
2215 mapper(ContentAssociation, self.tbl_content_associations,
2216 properties = dict(ca_id = self.tbl_content_associations.c.id,
2217 filename_id = self.tbl_content_associations.c.filename,
2218 filename = relation(ContentFilename),
2219 filepath_id = self.tbl_content_associations.c.filepath,
2220 filepath = relation(ContentFilepath),
2221 binary_id = self.tbl_content_associations.c.binary_pkg,
2222 binary = relation(DBBinary)))
2225 mapper(ContentFilename, self.tbl_content_file_names,
2226 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2227 filename = self.tbl_content_file_names.c.file))
2229 mapper(ContentFilepath, self.tbl_content_file_paths,
2230 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2231 filepath = self.tbl_content_file_paths.c.path))
2233 mapper(DSCFile, self.tbl_dsc_files,
2234 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2235 source_id = self.tbl_dsc_files.c.source,
2236 source = relation(DBSource),
2237 poolfile_id = self.tbl_dsc_files.c.file,
2238 poolfile = relation(PoolFile)))
2240 mapper(PoolFile, self.tbl_files,
2241 properties = dict(file_id = self.tbl_files.c.id,
2242 filesize = self.tbl_files.c.size,
2243 location_id = self.tbl_files.c.location,
2244 location = relation(Location)))
2246 mapper(Fingerprint, self.tbl_fingerprint,
2247 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2248 uid_id = self.tbl_fingerprint.c.uid,
2249 uid = relation(Uid),
2250 keyring_id = self.tbl_fingerprint.c.keyring,
2251 keyring = relation(Keyring)))
2253 mapper(Keyring, self.tbl_keyrings,
2254 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2255 keyring_id = self.tbl_keyrings.c.id))
2257 mapper(KnownChange, self.tbl_known_changes,
2258 properties = dict(known_change_id = self.tbl_known_changes.c.id))
2260 mapper(Location, self.tbl_location,
2261 properties = dict(location_id = self.tbl_location.c.id,
2262 component_id = self.tbl_location.c.component,
2263 component = relation(Component),
2264 archive_id = self.tbl_location.c.archive,
2265 archive = relation(Archive),
2266 archive_type = self.tbl_location.c.type))
2268 mapper(Maintainer, self.tbl_maintainer,
2269 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2271 mapper(NewComment, self.tbl_new_comments,
2272 properties = dict(comment_id = self.tbl_new_comments.c.id))
2274 mapper(Override, self.tbl_override,
2275 properties = dict(suite_id = self.tbl_override.c.suite,
2276 suite = relation(Suite),
2277 component_id = self.tbl_override.c.component,
2278 component = relation(Component),
2279 priority_id = self.tbl_override.c.priority,
2280 priority = relation(Priority),
2281 section_id = self.tbl_override.c.section,
2282 section = relation(Section),
2283 overridetype_id = self.tbl_override.c.type,
2284 overridetype = relation(OverrideType)))
2286 mapper(OverrideType, self.tbl_override_type,
2287 properties = dict(overridetype = self.tbl_override_type.c.type,
2288 overridetype_id = self.tbl_override_type.c.id))
2290 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2291 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2292 filepath_id = self.tbl_pending_content_associations.c.filepath,
2293 filepath = relation(ContentFilepath),
2294 filename_id = self.tbl_pending_content_associations.c.filename,
2295 filename = relation(ContentFilename)))
2297 mapper(Priority, self.tbl_priority,
2298 properties = dict(priority_id = self.tbl_priority.c.id))
2300 mapper(Queue, self.tbl_queue,
2301 properties = dict(queue_id = self.tbl_queue.c.id))
2303 mapper(QueueBuild, self.tbl_queue_build,
2304 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2305 queue_id = self.tbl_queue_build.c.queue,
2306 queue = relation(Queue, backref='queuebuild')))
2308 mapper(Section, self.tbl_section,
2309 properties = dict(section_id = self.tbl_section.c.id))
2311 mapper(DBSource, self.tbl_source,
2312 properties = dict(source_id = self.tbl_source.c.id,
2313 version = self.tbl_source.c.version,
2314 maintainer_id = self.tbl_source.c.maintainer,
2315 maintainer = relation(Maintainer,
2316 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2317 poolfile_id = self.tbl_source.c.file,
2318 poolfile = relation(PoolFile),
2319 fingerprint_id = self.tbl_source.c.sig_fpr,
2320 fingerprint = relation(Fingerprint),
2321 changedby_id = self.tbl_source.c.changedby,
2322 changedby = relation(Maintainer,
2323 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2324 srcfiles = relation(DSCFile,
2325 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2326 srcassociations = relation(SrcAssociation,
2327 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2329 mapper(SrcAssociation, self.tbl_src_associations,
2330 properties = dict(sa_id = self.tbl_src_associations.c.id,
2331 suite_id = self.tbl_src_associations.c.suite,
2332 suite = relation(Suite),
2333 source_id = self.tbl_src_associations.c.source,
2334 source = relation(DBSource)))
2336 mapper(SrcFormat, self.tbl_src_format,
2337 properties = dict(src_format_id = self.tbl_src_format.c.id,
2338 format_name = self.tbl_src_format.c.format_name))
2340 mapper(SrcUploader, self.tbl_src_uploaders,
2341 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2342 source_id = self.tbl_src_uploaders.c.source,
2343 source = relation(DBSource,
2344 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2345 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2346 maintainer = relation(Maintainer,
2347 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2349 mapper(Suite, self.tbl_suite,
2350 properties = dict(suite_id = self.tbl_suite.c.id))
2352 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2353 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2354 suite = relation(Suite, backref='suitearchitectures'),
2355 arch_id = self.tbl_suite_architectures.c.architecture,
2356 architecture = relation(Architecture)))
2358 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2359 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2360 suite = relation(Suite, backref='suitesrcformats'),
2361 src_format_id = self.tbl_suite_src_formats.c.src_format,
2362 src_format = relation(SrcFormat)))
2364 mapper(Uid, self.tbl_uid,
2365 properties = dict(uid_id = self.tbl_uid.c.id,
2366 fingerprint = relation(Fingerprint)))
2368 ## Connection functions
2369 def __createconn(self):
2370 from config import Config
2374 connstr = "postgres://%s" % cnf["DB::Host"]
2375 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2376 connstr += ":%s" % cnf["DB::Port"]
2377 connstr += "/%s" % cnf["DB::Name"]
2380 connstr = "postgres:///%s" % cnf["DB::Name"]
2381 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2382 connstr += "?port=%s" % cnf["DB::Port"]
2384 self.db_pg = create_engine(connstr, echo=self.debug)
2385 self.db_meta = MetaData()
2386 self.db_meta.bind = self.db_pg
2387 self.db_smaker = sessionmaker(bind=self.db_pg,
2391 self.__setuptables()
2392 self.__setupmappers()
2395 return self.db_smaker()
2397 __all__.append('DBConn')