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 BinaryACL(object):
381 def __init__(self, *args, **kwargs):
385 return '<BinaryACL %s>' % self.binary_acl_id
387 __all__.append('BinaryACL')
389 ################################################################################
391 class BinaryACLMap(object):
392 def __init__(self, *args, **kwargs):
396 return '<BinaryACLMap %s>' % self.binary_acl_map_id
398 __all__.append('BinaryACLMap')
400 ################################################################################
402 class Component(object):
403 def __init__(self, *args, **kwargs):
406 def __eq__(self, val):
407 if isinstance(val, str):
408 return (self.component_name == val)
409 # This signals to use the normal comparison operator
410 return NotImplemented
412 def __ne__(self, val):
413 if isinstance(val, str):
414 return (self.component_name != val)
415 # This signals to use the normal comparison operator
416 return NotImplemented
419 return '<Component %s>' % self.component_name
422 __all__.append('Component')
425 def get_component(component, session=None):
427 Returns database id for given C{component}.
429 @type component: string
430 @param component: The name of the override type
433 @return: the database id for the given component
436 component = component.lower()
438 q = session.query(Component).filter_by(component_name=component)
442 except NoResultFound:
445 __all__.append('get_component')
447 ################################################################################
449 class DBConfig(object):
450 def __init__(self, *args, **kwargs):
454 return '<DBConfig %s>' % self.name
456 __all__.append('DBConfig')
458 ################################################################################
460 class ContentFilename(object):
461 def __init__(self, *args, **kwargs):
465 return '<ContentFilename %s>' % self.filename
467 __all__.append('ContentFilename')
470 def get_or_set_contents_file_id(filename, session=None):
472 Returns database id for given filename.
474 If no matching file is found, a row is inserted.
476 @type filename: string
477 @param filename: The filename
478 @type session: SQLAlchemy
479 @param session: Optional SQL session object (a temporary one will be
480 generated if not supplied). If not passed, a commit will be performed at
481 the end of the function, otherwise the caller is responsible for commiting.
484 @return: the database id for the given component
487 q = session.query(ContentFilename).filter_by(filename=filename)
490 ret = q.one().cafilename_id
491 except NoResultFound:
492 cf = ContentFilename()
493 cf.filename = filename
495 session.commit_or_flush()
496 ret = cf.cafilename_id
500 __all__.append('get_or_set_contents_file_id')
503 def get_contents(suite, overridetype, section=None, session=None):
505 Returns contents for a suite / overridetype combination, limiting
506 to a section if not None.
509 @param suite: Suite object
511 @type overridetype: OverrideType
512 @param overridetype: OverrideType object
514 @type section: Section
515 @param section: Optional section object to limit results to
517 @type session: SQLAlchemy
518 @param session: Optional SQL session object (a temporary one will be
519 generated if not supplied)
522 @return: ResultsProxy object set up to return tuples of (filename, section,
526 # find me all of the contents for a given suite
527 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
531 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
532 JOIN content_file_names n ON (c.filename=n.id)
533 JOIN binaries b ON (b.id=c.binary_pkg)
534 JOIN override o ON (o.package=b.package)
535 JOIN section s ON (s.id=o.section)
536 WHERE o.suite = :suiteid AND o.type = :overridetypeid
537 AND b.type=:overridetypename"""
539 vals = {'suiteid': suite.suite_id,
540 'overridetypeid': overridetype.overridetype_id,
541 'overridetypename': overridetype.overridetype}
543 if section is not None:
544 contents_q += " AND s.id = :sectionid"
545 vals['sectionid'] = section.section_id
547 contents_q += " ORDER BY fn"
549 return session.execute(contents_q, vals)
551 __all__.append('get_contents')
553 ################################################################################
555 class ContentFilepath(object):
556 def __init__(self, *args, **kwargs):
560 return '<ContentFilepath %s>' % self.filepath
562 __all__.append('ContentFilepath')
565 def get_or_set_contents_path_id(filepath, session=None):
567 Returns database id for given path.
569 If no matching file is found, a row is inserted.
571 @type filename: string
572 @param filename: The filepath
573 @type session: SQLAlchemy
574 @param session: Optional SQL session object (a temporary one will be
575 generated if not supplied). If not passed, a commit will be performed at
576 the end of the function, otherwise the caller is responsible for commiting.
579 @return: the database id for the given path
582 q = session.query(ContentFilepath).filter_by(filepath=filepath)
585 ret = q.one().cafilepath_id
586 except NoResultFound:
587 cf = ContentFilepath()
588 cf.filepath = filepath
590 session.commit_or_flush()
591 ret = cf.cafilepath_id
595 __all__.append('get_or_set_contents_path_id')
597 ################################################################################
599 class ContentAssociation(object):
600 def __init__(self, *args, **kwargs):
604 return '<ContentAssociation %s>' % self.ca_id
606 __all__.append('ContentAssociation')
608 def insert_content_paths(binary_id, fullpaths, session=None):
610 Make sure given path is associated with given binary id
613 @param binary_id: the id of the binary
614 @type fullpaths: list
615 @param fullpaths: the list of paths of the file being associated with the binary
616 @type session: SQLAlchemy session
617 @param session: Optional SQLAlchemy session. If this is passed, the caller
618 is responsible for ensuring a transaction has begun and committing the
619 results or rolling back based on the result code. If not passed, a commit
620 will be performed at the end of the function, otherwise the caller is
621 responsible for commiting.
623 @return: True upon success
628 session = DBConn().session()
634 for fullpath in fullpaths:
635 # Get the necessary IDs ...
636 (path, file) = os.path.split(fullpath)
638 filepath_id = get_or_set_contents_path_id(path, session)
639 filename_id = get_or_set_contents_file_id(file, session)
641 pathcache[fullpath] = (filepath_id, filename_id)
643 for fullpath, dat in pathcache.items():
644 ca = ContentAssociation()
645 ca.binary_id = binary_id
646 ca.filepath_id = dat[0]
647 ca.filename_id = dat[1]
650 # Only commit if we set up the session ourself
660 traceback.print_exc()
662 # Only rollback if we set up the session ourself
669 __all__.append('insert_content_paths')
671 ################################################################################
673 class DSCFile(object):
674 def __init__(self, *args, **kwargs):
678 return '<DSCFile %s>' % self.dscfile_id
680 __all__.append('DSCFile')
683 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
685 Returns a list of DSCFiles which may be empty
687 @type dscfile_id: int (optional)
688 @param dscfile_id: the dscfile_id of the DSCFiles to find
690 @type source_id: int (optional)
691 @param source_id: the source id related to the DSCFiles to find
693 @type poolfile_id: int (optional)
694 @param poolfile_id: the poolfile id related to the DSCFiles to find
697 @return: Possibly empty list of DSCFiles
700 q = session.query(DSCFile)
702 if dscfile_id is not None:
703 q = q.filter_by(dscfile_id=dscfile_id)
705 if source_id is not None:
706 q = q.filter_by(source_id=source_id)
708 if poolfile_id is not None:
709 q = q.filter_by(poolfile_id=poolfile_id)
713 __all__.append('get_dscfiles')
715 ################################################################################
717 class PoolFile(object):
718 def __init__(self, *args, **kwargs):
722 return '<PoolFile %s>' % self.filename
724 __all__.append('PoolFile')
727 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
730 (ValidFileFound [boolean or None], PoolFile object or None)
732 @type filename: string
733 @param filename: the filename of the file to check against the DB
736 @param filesize: the size of the file to check against the DB
739 @param md5sum: the md5sum of the file to check against the DB
741 @type location_id: int
742 @param location_id: the id of the location to look in
745 @return: Tuple of length 2.
746 If more than one file found with that name:
748 If valid pool file found: (True, PoolFile object)
749 If valid pool file not found:
750 (False, None) if no file found
751 (False, PoolFile object) if file found with size/md5sum mismatch
754 q = session.query(PoolFile).filter_by(filename=filename)
755 q = q.join(Location).filter_by(location_id=location_id)
765 if obj.md5sum != md5sum or obj.filesize != filesize:
773 __all__.append('check_poolfile')
776 def get_poolfile_by_id(file_id, session=None):
778 Returns a PoolFile objects or None for the given id
781 @param file_id: the id of the file to look for
783 @rtype: PoolFile or None
784 @return: either the PoolFile object or None
787 q = session.query(PoolFile).filter_by(file_id=file_id)
791 except NoResultFound:
794 __all__.append('get_poolfile_by_id')
798 def get_poolfile_by_name(filename, location_id=None, session=None):
800 Returns an array of PoolFile objects for the given filename and
801 (optionally) location_id
803 @type filename: string
804 @param filename: the filename of the file to check against the DB
806 @type location_id: int
807 @param location_id: the id of the location to look in (optional)
810 @return: array of PoolFile objects
813 q = session.query(PoolFile).filter_by(filename=filename)
815 if location_id is not None:
816 q = q.join(Location).filter_by(location_id=location_id)
820 __all__.append('get_poolfile_by_name')
823 def get_poolfile_like_name(filename, session=None):
825 Returns an array of PoolFile objects which are like the given name
827 @type filename: string
828 @param filename: the filename of the file to check against the DB
831 @return: array of PoolFile objects
834 # TODO: There must be a way of properly using bind parameters with %FOO%
835 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
839 __all__.append('get_poolfile_like_name')
841 ################################################################################
843 class Fingerprint(object):
844 def __init__(self, *args, **kwargs):
848 return '<Fingerprint %s>' % self.fingerprint
850 __all__.append('Fingerprint')
853 def get_or_set_fingerprint(fpr, session=None):
855 Returns Fingerprint object for given fpr.
857 If no matching fpr is found, a row is inserted.
860 @param fpr: The fpr to find / add
862 @type session: SQLAlchemy
863 @param session: Optional SQL session object (a temporary one will be
864 generated if not supplied). If not passed, a commit will be performed at
865 the end of the function, otherwise the caller is responsible for commiting.
866 A flush will be performed either way.
869 @return: the Fingerprint object for the given fpr
872 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
876 except NoResultFound:
877 fingerprint = Fingerprint()
878 fingerprint.fingerprint = fpr
879 session.add(fingerprint)
880 session.commit_or_flush()
885 __all__.append('get_or_set_fingerprint')
887 ################################################################################
889 class Keyring(object):
890 def __init__(self, *args, **kwargs):
894 return '<Keyring %s>' % self.keyring_name
896 __all__.append('Keyring')
899 def get_or_set_keyring(keyring, session=None):
901 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
902 and return the new Keyring
903 If C{keyring} already has an entry, simply return the existing Keyring
905 @type keyring: string
906 @param keyring: the keyring name
909 @return: the Keyring object for this keyring
912 q = session.query(Keyring).filter_by(keyring_name=keyring)
916 except NoResultFound:
917 obj = Keyring(keyring_name=keyring)
919 session.commit_or_flush()
922 __all__.append('get_or_set_keyring')
924 ################################################################################
926 class Location(object):
927 def __init__(self, *args, **kwargs):
931 return '<Location %s (%s)>' % (self.path, self.location_id)
933 __all__.append('Location')
936 def get_location(location, component=None, archive=None, session=None):
938 Returns Location object for the given combination of location, component
941 @type location: string
942 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
944 @type component: string
945 @param component: the component name (if None, no restriction applied)
947 @type archive: string
948 @param archive_id: the archive name (if None, no restriction applied)
950 @rtype: Location / None
951 @return: Either a Location object or None if one can't be found
954 q = session.query(Location).filter_by(path=location)
956 if archive is not None:
957 q = q.join(Archive).filter_by(archive_name=archive)
959 if component is not None:
960 q = q.join(Component).filter_by(component_name=component)
964 except NoResultFound:
967 __all__.append('get_location')
969 ################################################################################
971 class Maintainer(object):
972 def __init__(self, *args, **kwargs):
976 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
978 def get_split_maintainer(self):
979 if not hasattr(self, 'name') or self.name is None:
980 return ('', '', '', '')
982 return fix_maintainer(self.name.strip())
984 __all__.append('Maintainer')
987 def get_or_set_maintainer(name, session=None):
989 Returns Maintainer object for given maintainer name.
991 If no matching maintainer name is found, a row is inserted.
994 @param name: The maintainer name to add
996 @type session: SQLAlchemy
997 @param session: Optional SQL session object (a temporary one will be
998 generated if not supplied). If not passed, a commit will be performed at
999 the end of the function, otherwise the caller is responsible for commiting.
1000 A flush will be performed either way.
1003 @return: the Maintainer object for the given maintainer
1006 q = session.query(Maintainer).filter_by(name=name)
1009 except NoResultFound:
1010 maintainer = Maintainer()
1011 maintainer.name = name
1012 session.add(maintainer)
1013 session.commit_or_flush()
1018 __all__.append('get_or_set_maintainer')
1021 def get_maintainer(maintainer_id, session=None):
1023 Return the name of the maintainer behind C{maintainer_id} or None if that
1024 maintainer_id is invalid.
1026 @type maintainer_id: int
1027 @param maintainer_id: the id of the maintainer
1030 @return: the Maintainer with this C{maintainer_id}
1033 return session.query(Maintainer).get(maintainer_id)
1035 __all__.append('get_maintainer')
1037 ################################################################################
1039 class NewComment(object):
1040 def __init__(self, *args, **kwargs):
1044 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1046 __all__.append('NewComment')
1049 def has_new_comment(package, version, session=None):
1051 Returns true if the given combination of C{package}, C{version} has a comment.
1053 @type package: string
1054 @param package: name of the package
1056 @type version: string
1057 @param version: package version
1059 @type session: Session
1060 @param session: Optional SQLA session object (a temporary one will be
1061 generated if not supplied)
1067 q = session.query(NewComment)
1068 q = q.filter_by(package=package)
1069 q = q.filter_by(version=version)
1071 return bool(q.count() > 0)
1073 __all__.append('has_new_comment')
1076 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1078 Returns (possibly empty) list of NewComment objects for the given
1081 @type package: string (optional)
1082 @param package: name of the package
1084 @type version: string (optional)
1085 @param version: package version
1087 @type comment_id: int (optional)
1088 @param comment_id: An id of a comment
1090 @type session: Session
1091 @param session: Optional SQLA session object (a temporary one will be
1092 generated if not supplied)
1095 @return: A (possibly empty) list of NewComment objects will be returned
1098 q = session.query(NewComment)
1099 if package is not None: q = q.filter_by(package=package)
1100 if version is not None: q = q.filter_by(version=version)
1101 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1105 __all__.append('get_new_comments')
1107 ################################################################################
1109 class Override(object):
1110 def __init__(self, *args, **kwargs):
1114 return '<Override %s (%s)>' % (self.package, self.suite_id)
1116 __all__.append('Override')
1119 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1121 Returns Override object for the given parameters
1123 @type package: string
1124 @param package: The name of the package
1126 @type suite: string, list or None
1127 @param suite: The name of the suite (or suites if a list) to limit to. If
1128 None, don't limit. Defaults to None.
1130 @type component: string, list or None
1131 @param component: The name of the component (or components if a list) to
1132 limit to. If None, don't limit. Defaults to None.
1134 @type overridetype: string, list or None
1135 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1136 limit to. If None, don't limit. Defaults to None.
1138 @type session: Session
1139 @param session: Optional SQLA session object (a temporary one will be
1140 generated if not supplied)
1143 @return: A (possibly empty) list of Override objects will be returned
1146 q = session.query(Override)
1147 q = q.filter_by(package=package)
1149 if suite is not None:
1150 if not isinstance(suite, list): suite = [suite]
1151 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1153 if component is not None:
1154 if not isinstance(component, list): component = [component]
1155 q = q.join(Component).filter(Component.component_name.in_(component))
1157 if overridetype is not None:
1158 if not isinstance(overridetype, list): overridetype = [overridetype]
1159 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1163 __all__.append('get_override')
1166 ################################################################################
1168 class OverrideType(object):
1169 def __init__(self, *args, **kwargs):
1173 return '<OverrideType %s>' % self.overridetype
1175 __all__.append('OverrideType')
1178 def get_override_type(override_type, session=None):
1180 Returns OverrideType object for given C{override type}.
1182 @type override_type: string
1183 @param override_type: The name of the override type
1185 @type session: Session
1186 @param session: Optional SQLA session object (a temporary one will be
1187 generated if not supplied)
1190 @return: the database id for the given override type
1193 q = session.query(OverrideType).filter_by(overridetype=override_type)
1197 except NoResultFound:
1200 __all__.append('get_override_type')
1202 ################################################################################
1204 class PendingContentAssociation(object):
1205 def __init__(self, *args, **kwargs):
1209 return '<PendingContentAssociation %s>' % self.pca_id
1211 __all__.append('PendingContentAssociation')
1213 def insert_pending_content_paths(package, fullpaths, session=None):
1215 Make sure given paths are temporarily associated with given
1219 @param package: the package to associate with should have been read in from the binary control file
1220 @type fullpaths: list
1221 @param fullpaths: the list of paths of the file being associated with the binary
1222 @type session: SQLAlchemy session
1223 @param session: Optional SQLAlchemy session. If this is passed, the caller
1224 is responsible for ensuring a transaction has begun and committing the
1225 results or rolling back based on the result code. If not passed, a commit
1226 will be performed at the end of the function
1228 @return: True upon success, False if there is a problem
1231 privatetrans = False
1234 session = DBConn().session()
1238 arch = get_architecture(package['Architecture'], session)
1239 arch_id = arch.arch_id
1241 # Remove any already existing recorded files for this package
1242 q = session.query(PendingContentAssociation)
1243 q = q.filter_by(package=package['Package'])
1244 q = q.filter_by(version=package['Version'])
1245 q = q.filter_by(architecture=arch_id)
1250 for fullpath in fullpaths:
1251 (path, file) = os.path.split(fullpath)
1253 if path.startswith( "./" ):
1256 filepath_id = get_or_set_contents_path_id(path, session)
1257 filename_id = get_or_set_contents_file_id(file, session)
1259 pathcache[fullpath] = (filepath_id, filename_id)
1261 for fullpath, dat in pathcache.items():
1262 pca = PendingContentAssociation()
1263 pca.package = package['Package']
1264 pca.version = package['Version']
1265 pca.filepath_id = dat[0]
1266 pca.filename_id = dat[1]
1267 pca.architecture = arch_id
1270 # Only commit if we set up the session ourself
1278 except Exception, e:
1279 traceback.print_exc()
1281 # Only rollback if we set up the session ourself
1288 __all__.append('insert_pending_content_paths')
1290 ################################################################################
1292 class Priority(object):
1293 def __init__(self, *args, **kwargs):
1296 def __eq__(self, val):
1297 if isinstance(val, str):
1298 return (self.priority == val)
1299 # This signals to use the normal comparison operator
1300 return NotImplemented
1302 def __ne__(self, val):
1303 if isinstance(val, str):
1304 return (self.priority != val)
1305 # This signals to use the normal comparison operator
1306 return NotImplemented
1309 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1311 __all__.append('Priority')
1314 def get_priority(priority, session=None):
1316 Returns Priority object for given C{priority name}.
1318 @type priority: string
1319 @param priority: The name of the priority
1321 @type session: Session
1322 @param session: Optional SQLA session object (a temporary one will be
1323 generated if not supplied)
1326 @return: Priority object for the given priority
1329 q = session.query(Priority).filter_by(priority=priority)
1333 except NoResultFound:
1336 __all__.append('get_priority')
1339 def get_priorities(session=None):
1341 Returns dictionary of priority names -> id mappings
1343 @type session: Session
1344 @param session: Optional SQL session object (a temporary one will be
1345 generated if not supplied)
1348 @return: dictionary of priority names -> id mappings
1352 q = session.query(Priority)
1354 ret[x.priority] = x.priority_id
1358 __all__.append('get_priorities')
1360 ################################################################################
1362 class Queue(object):
1363 def __init__(self, *args, **kwargs):
1367 return '<Queue %s>' % self.queue_name
1369 def autobuild_upload(self, changes, srcpath, session=None):
1371 Update queue_build database table used for incoming autobuild support.
1373 @type changes: Changes
1374 @param changes: changes object for the upload to process
1376 @type srcpath: string
1377 @param srcpath: path for the queue file entries/link destinations
1379 @type session: SQLAlchemy session
1380 @param session: Optional SQLAlchemy session. If this is passed, the
1381 caller is responsible for ensuring a transaction has begun and
1382 committing the results or rolling back based on the result code. If
1383 not passed, a commit will be performed at the end of the function,
1384 otherwise the caller is responsible for commiting.
1386 @rtype: NoneType or string
1387 @return: None if the operation failed, a string describing the error if not
1390 privatetrans = False
1392 session = DBConn().session()
1395 # TODO: Remove by moving queue config into the database
1398 for suitename in changes.changes["distribution"].keys():
1399 # TODO: Move into database as:
1400 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1401 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1402 # This also gets rid of the SecurityQueueBuild hack below
1403 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1407 s = get_suite(suitename, session)
1409 return "INTERNAL ERROR: Could not find suite %s" % suitename
1411 # TODO: Get from database as above
1412 dest_dir = conf["Dir::QueueBuild"]
1414 # TODO: Move into database as above
1415 if conf.FindB("Dinstall::SecurityQueueBuild"):
1416 dest_dir = os.path.join(dest_dir, suitename)
1418 for file_entry in changes.files.keys():
1419 src = os.path.join(srcpath, file_entry)
1420 dest = os.path.join(dest_dir, file_entry)
1422 # TODO: Move into database as above
1423 if conf.FindB("Dinstall::SecurityQueueBuild"):
1424 # Copy it since the original won't be readable by www-data
1426 utils.copy(src, dest)
1428 # Create a symlink to it
1429 os.symlink(src, dest)
1432 qb.suite_id = s.suite_id
1433 qb.queue_id = self.queue_id
1439 # If the .orig tarballs are in the pool, create a symlink to
1440 # them (if one doesn't already exist)
1441 for dsc_file in changes.dsc_files.keys():
1442 # Skip all files except orig tarballs
1443 from daklib.regexes import re_is_orig_source
1444 if not re_is_orig_source.match(dsc_file):
1446 # Skip orig files not identified in the pool
1447 if not (changes.orig_files.has_key(dsc_file) and
1448 changes.orig_files[dsc_file].has_key("id")):
1450 orig_file_id = changes.orig_files[dsc_file]["id"]
1451 dest = os.path.join(dest_dir, dsc_file)
1453 # If it doesn't exist, create a symlink
1454 if not os.path.exists(dest):
1455 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1456 {'id': orig_file_id})
1459 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1461 src = os.path.join(res[0], res[1])
1462 os.symlink(src, dest)
1464 # Add it to the list of packages for later processing by apt-ftparchive
1466 qb.suite_id = s.suite_id
1467 qb.queue_id = self.queue_id
1472 # If it does, update things to ensure it's not removed prematurely
1474 qb = get_queue_build(dest, s.suite_id, session)
1486 __all__.append('Queue')
1489 def get_or_set_queue(queuename, session=None):
1491 Returns Queue object for given C{queue name}, creating it if it does not
1494 @type queuename: string
1495 @param queuename: The name of the queue
1497 @type session: Session
1498 @param session: Optional SQLA session object (a temporary one will be
1499 generated if not supplied)
1502 @return: Queue object for the given queue
1505 q = session.query(Queue).filter_by(queue_name=queuename)
1509 except NoResultFound:
1511 queue.queue_name = queuename
1513 session.commit_or_flush()
1518 __all__.append('get_or_set_queue')
1520 ################################################################################
1522 class QueueBuild(object):
1523 def __init__(self, *args, **kwargs):
1527 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1529 __all__.append('QueueBuild')
1532 def get_queue_build(filename, suite, session=None):
1534 Returns QueueBuild object for given C{filename} and C{suite}.
1536 @type filename: string
1537 @param filename: The name of the file
1539 @type suiteid: int or str
1540 @param suiteid: Suite name or ID
1542 @type session: Session
1543 @param session: Optional SQLA session object (a temporary one will be
1544 generated if not supplied)
1547 @return: Queue object for the given queue
1550 if isinstance(suite, int):
1551 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1553 q = session.query(QueueBuild).filter_by(filename=filename)
1554 q = q.join(Suite).filter_by(suite_name=suite)
1558 except NoResultFound:
1561 __all__.append('get_queue_build')
1563 ################################################################################
1565 class Section(object):
1566 def __init__(self, *args, **kwargs):
1569 def __eq__(self, val):
1570 if isinstance(val, str):
1571 return (self.section == val)
1572 # This signals to use the normal comparison operator
1573 return NotImplemented
1575 def __ne__(self, val):
1576 if isinstance(val, str):
1577 return (self.section != val)
1578 # This signals to use the normal comparison operator
1579 return NotImplemented
1582 return '<Section %s>' % self.section
1584 __all__.append('Section')
1587 def get_section(section, session=None):
1589 Returns Section object for given C{section name}.
1591 @type section: string
1592 @param section: The name of the section
1594 @type session: Session
1595 @param session: Optional SQLA session object (a temporary one will be
1596 generated if not supplied)
1599 @return: Section object for the given section name
1602 q = session.query(Section).filter_by(section=section)
1606 except NoResultFound:
1609 __all__.append('get_section')
1612 def get_sections(session=None):
1614 Returns dictionary of section names -> id mappings
1616 @type session: Session
1617 @param session: Optional SQL session object (a temporary one will be
1618 generated if not supplied)
1621 @return: dictionary of section names -> id mappings
1625 q = session.query(Section)
1627 ret[x.section] = x.section_id
1631 __all__.append('get_sections')
1633 ################################################################################
1635 class DBSource(object):
1636 def __init__(self, *args, **kwargs):
1640 return '<DBSource %s (%s)>' % (self.source, self.version)
1642 __all__.append('DBSource')
1645 def source_exists(source, source_version, suites = ["any"], session=None):
1647 Ensure that source exists somewhere in the archive for the binary
1648 upload being processed.
1649 1. exact match => 1.0-3
1650 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1652 @type package: string
1653 @param package: package source name
1655 @type source_version: string
1656 @param source_version: expected source version
1659 @param suites: list of suites to check in, default I{any}
1661 @type session: Session
1662 @param session: Optional SQLA session object (a temporary one will be
1663 generated if not supplied)
1666 @return: returns 1 if a source with expected version is found, otherwise 0
1673 for suite in suites:
1674 q = session.query(DBSource).filter_by(source=source)
1676 # source must exist in suite X, or in some other suite that's
1677 # mapped to X, recursively... silent-maps are counted too,
1678 # unreleased-maps aren't.
1679 maps = cnf.ValueList("SuiteMappings")[:]
1681 maps = [ m.split() for m in maps ]
1682 maps = [ (x[1], x[2]) for x in maps
1683 if x[0] == "map" or x[0] == "silent-map" ]
1686 if x[1] in s and x[0] not in s:
1689 q = q.join(SrcAssociation).join(Suite)
1690 q = q.filter(Suite.suite_name.in_(s))
1692 # Reduce the query results to a list of version numbers
1693 ql = [ j.version for j in q.all() ]
1696 if source_version in ql:
1700 from daklib.regexes import re_bin_only_nmu
1701 orig_source_version = re_bin_only_nmu.sub('', source_version)
1702 if orig_source_version in ql:
1705 # No source found so return not ok
1710 __all__.append('source_exists')
1713 def get_suites_source_in(source, session=None):
1715 Returns list of Suite objects which given C{source} name is in
1718 @param source: DBSource package name to search for
1721 @return: list of Suite objects for the given source
1724 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1726 __all__.append('get_suites_source_in')
1729 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1731 Returns list of DBSource objects for given C{source} name and other parameters
1734 @param source: DBSource package name to search for
1736 @type source: str or None
1737 @param source: DBSource version name to search for or None if not applicable
1739 @type dm_upload_allowed: bool
1740 @param dm_upload_allowed: If None, no effect. If True or False, only
1741 return packages with that dm_upload_allowed setting
1743 @type session: Session
1744 @param session: Optional SQL session object (a temporary one will be
1745 generated if not supplied)
1748 @return: list of DBSource objects for the given name (may be empty)
1751 q = session.query(DBSource).filter_by(source=source)
1753 if version is not None:
1754 q = q.filter_by(version=version)
1756 if dm_upload_allowed is not None:
1757 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1761 __all__.append('get_sources_from_name')
1764 def get_source_in_suite(source, suite, session=None):
1766 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1768 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1769 - B{suite} - a suite name, eg. I{unstable}
1771 @type source: string
1772 @param source: source package name
1775 @param suite: the suite name
1778 @return: the version for I{source} in I{suite}
1782 q = session.query(SrcAssociation)
1783 q = q.join('source').filter_by(source=source)
1784 q = q.join('suite').filter_by(suite_name=suite)
1787 return q.one().source
1788 except NoResultFound:
1791 __all__.append('get_source_in_suite')
1793 ################################################################################
1795 class SourceACL(object):
1796 def __init__(self, *args, **kwargs):
1800 return '<SourceACL %s>' % self.source_acl_id
1802 __all__.append('SourceACL')
1804 ################################################################################
1806 class SrcAssociation(object):
1807 def __init__(self, *args, **kwargs):
1811 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1813 __all__.append('SrcAssociation')
1815 ################################################################################
1817 class SrcFormat(object):
1818 def __init__(self, *args, **kwargs):
1822 return '<SrcFormat %s>' % (self.format_name)
1824 __all__.append('SrcFormat')
1826 ################################################################################
1828 class SrcUploader(object):
1829 def __init__(self, *args, **kwargs):
1833 return '<SrcUploader %s>' % self.uploader_id
1835 __all__.append('SrcUploader')
1837 ################################################################################
1839 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1840 ('SuiteID', 'suite_id'),
1841 ('Version', 'version'),
1842 ('Origin', 'origin'),
1844 ('Description', 'description'),
1845 ('Untouchable', 'untouchable'),
1846 ('Announce', 'announce'),
1847 ('Codename', 'codename'),
1848 ('OverrideCodename', 'overridecodename'),
1849 ('ValidTime', 'validtime'),
1850 ('Priority', 'priority'),
1851 ('NotAutomatic', 'notautomatic'),
1852 ('CopyChanges', 'copychanges'),
1853 ('CopyDotDak', 'copydotdak'),
1854 ('CommentsDir', 'commentsdir'),
1855 ('OverrideSuite', 'overridesuite'),
1856 ('ChangelogBase', 'changelogbase')]
1859 class Suite(object):
1860 def __init__(self, *args, **kwargs):
1864 return '<Suite %s>' % self.suite_name
1866 def __eq__(self, val):
1867 if isinstance(val, str):
1868 return (self.suite_name == val)
1869 # This signals to use the normal comparison operator
1870 return NotImplemented
1872 def __ne__(self, val):
1873 if isinstance(val, str):
1874 return (self.suite_name != val)
1875 # This signals to use the normal comparison operator
1876 return NotImplemented
1880 for disp, field in SUITE_FIELDS:
1881 val = getattr(self, field, None)
1883 ret.append("%s: %s" % (disp, val))
1885 return "\n".join(ret)
1887 __all__.append('Suite')
1890 def get_suite_architecture(suite, architecture, session=None):
1892 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1896 @param suite: Suite name to search for
1898 @type architecture: str
1899 @param architecture: Architecture name to search for
1901 @type session: Session
1902 @param session: Optional SQL session object (a temporary one will be
1903 generated if not supplied)
1905 @rtype: SuiteArchitecture
1906 @return: the SuiteArchitecture object or None
1909 q = session.query(SuiteArchitecture)
1910 q = q.join(Architecture).filter_by(arch_string=architecture)
1911 q = q.join(Suite).filter_by(suite_name=suite)
1915 except NoResultFound:
1918 __all__.append('get_suite_architecture')
1921 def get_suite(suite, session=None):
1923 Returns Suite object for given C{suite name}.
1926 @param suite: The name of the suite
1928 @type session: Session
1929 @param session: Optional SQLA session object (a temporary one will be
1930 generated if not supplied)
1933 @return: Suite object for the requested suite name (None if not present)
1936 q = session.query(Suite).filter_by(suite_name=suite)
1940 except NoResultFound:
1943 __all__.append('get_suite')
1945 ################################################################################
1947 class SuiteArchitecture(object):
1948 def __init__(self, *args, **kwargs):
1952 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1954 __all__.append('SuiteArchitecture')
1957 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1959 Returns list of Architecture objects for given C{suite} name
1962 @param source: Suite name to search for
1964 @type skipsrc: boolean
1965 @param skipsrc: Whether to skip returning the 'source' architecture entry
1968 @type skipall: boolean
1969 @param skipall: Whether to skip returning the 'all' architecture entry
1972 @type session: Session
1973 @param session: Optional SQL session object (a temporary one will be
1974 generated if not supplied)
1977 @return: list of Architecture objects for the given name (may be empty)
1980 q = session.query(Architecture)
1981 q = q.join(SuiteArchitecture)
1982 q = q.join(Suite).filter_by(suite_name=suite)
1985 q = q.filter(Architecture.arch_string != 'source')
1988 q = q.filter(Architecture.arch_string != 'all')
1990 q = q.order_by('arch_string')
1994 __all__.append('get_suite_architectures')
1996 ################################################################################
1998 class SuiteSrcFormat(object):
1999 def __init__(self, *args, **kwargs):
2003 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2005 __all__.append('SuiteSrcFormat')
2008 def get_suite_src_formats(suite, session=None):
2010 Returns list of allowed SrcFormat for C{suite}.
2013 @param suite: Suite name to search for
2015 @type session: Session
2016 @param session: Optional SQL session object (a temporary one will be
2017 generated if not supplied)
2020 @return: the list of allowed source formats for I{suite}
2023 q = session.query(SrcFormat)
2024 q = q.join(SuiteSrcFormat)
2025 q = q.join(Suite).filter_by(suite_name=suite)
2026 q = q.order_by('format_name')
2030 __all__.append('get_suite_src_formats')
2032 ################################################################################
2035 def __init__(self, *args, **kwargs):
2038 def __eq__(self, val):
2039 if isinstance(val, str):
2040 return (self.uid == val)
2041 # This signals to use the normal comparison operator
2042 return NotImplemented
2044 def __ne__(self, val):
2045 if isinstance(val, str):
2046 return (self.uid != val)
2047 # This signals to use the normal comparison operator
2048 return NotImplemented
2051 return '<Uid %s (%s)>' % (self.uid, self.name)
2053 __all__.append('Uid')
2056 def add_database_user(uidname, session=None):
2058 Adds a database user
2060 @type uidname: string
2061 @param uidname: The uid of the user to add
2063 @type session: SQLAlchemy
2064 @param session: Optional SQL session object (a temporary one will be
2065 generated if not supplied). If not passed, a commit will be performed at
2066 the end of the function, otherwise the caller is responsible for commiting.
2069 @return: the uid object for the given uidname
2072 session.execute("CREATE USER :uid", {'uid': uidname})
2073 session.commit_or_flush()
2075 __all__.append('add_database_user')
2078 def get_or_set_uid(uidname, session=None):
2080 Returns uid object for given uidname.
2082 If no matching uidname is found, a row is inserted.
2084 @type uidname: string
2085 @param uidname: The uid to add
2087 @type session: SQLAlchemy
2088 @param session: Optional SQL session object (a temporary one will be
2089 generated if not supplied). If not passed, a commit will be performed at
2090 the end of the function, otherwise the caller is responsible for commiting.
2093 @return: the uid object for the given uidname
2096 q = session.query(Uid).filter_by(uid=uidname)
2100 except NoResultFound:
2104 session.commit_or_flush()
2109 __all__.append('get_or_set_uid')
2112 def get_uid_from_fingerprint(fpr, session=None):
2113 q = session.query(Uid)
2114 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2118 except NoResultFound:
2121 __all__.append('get_uid_from_fingerprint')
2123 ################################################################################
2125 class UploadBlock(object):
2126 def __init__(self, *args, **kwargs):
2130 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2132 __all__.append('UploadBlock')
2134 ################################################################################
2136 class DBConn(Singleton):
2138 database module init.
2140 def __init__(self, *args, **kwargs):
2141 super(DBConn, self).__init__(*args, **kwargs)
2143 def _startup(self, *args, **kwargs):
2145 if kwargs.has_key('debug'):
2149 def __setuptables(self):
2150 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2151 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2152 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2153 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2154 self.tbl_binary_acl = Table('binary_acl', self.db_meta, autoload=True)
2155 self.tbl_binary_acl_map = Table('binary_acl_map', self.db_meta, autoload=True)
2156 self.tbl_component = Table('component', self.db_meta, autoload=True)
2157 self.tbl_config = Table('config', self.db_meta, autoload=True)
2158 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2159 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2160 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2161 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2162 self.tbl_files = Table('files', self.db_meta, autoload=True)
2163 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2164 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2165 self.tbl_location = Table('location', self.db_meta, autoload=True)
2166 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2167 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2168 self.tbl_override = Table('override', self.db_meta, autoload=True)
2169 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2170 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2171 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2172 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2173 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2174 self.tbl_section = Table('section', self.db_meta, autoload=True)
2175 self.tbl_source = Table('source', self.db_meta, autoload=True)
2176 self.tbl_source_acl = Table('source_acl', self.db_meta, autoload=True)
2177 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2178 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2179 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2180 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2181 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2182 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2183 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2184 self.tbl_upload_blocks = Table('upload_blocks', self.db_meta, autoload=True)
2186 def __setupmappers(self):
2187 mapper(Architecture, self.tbl_architecture,
2188 properties = dict(arch_id = self.tbl_architecture.c.id))
2190 mapper(Archive, self.tbl_archive,
2191 properties = dict(archive_id = self.tbl_archive.c.id,
2192 archive_name = self.tbl_archive.c.name))
2194 mapper(BinAssociation, self.tbl_bin_associations,
2195 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2196 suite_id = self.tbl_bin_associations.c.suite,
2197 suite = relation(Suite),
2198 binary_id = self.tbl_bin_associations.c.bin,
2199 binary = relation(DBBinary)))
2201 mapper(DBBinary, self.tbl_binaries,
2202 properties = dict(binary_id = self.tbl_binaries.c.id,
2203 package = self.tbl_binaries.c.package,
2204 version = self.tbl_binaries.c.version,
2205 maintainer_id = self.tbl_binaries.c.maintainer,
2206 maintainer = relation(Maintainer),
2207 source_id = self.tbl_binaries.c.source,
2208 source = relation(DBSource),
2209 arch_id = self.tbl_binaries.c.architecture,
2210 architecture = relation(Architecture),
2211 poolfile_id = self.tbl_binaries.c.file,
2212 poolfile = relation(PoolFile),
2213 binarytype = self.tbl_binaries.c.type,
2214 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2215 fingerprint = relation(Fingerprint),
2216 install_date = self.tbl_binaries.c.install_date,
2217 binassociations = relation(BinAssociation,
2218 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2220 mapper(BinaryACL, self.tbl_binary_acl,
2221 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2223 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2224 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2225 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2226 architecture = relation(Architecture)))
2228 mapper(Component, self.tbl_component,
2229 properties = dict(component_id = self.tbl_component.c.id,
2230 component_name = self.tbl_component.c.name))
2232 mapper(DBConfig, self.tbl_config,
2233 properties = dict(config_id = self.tbl_config.c.id))
2235 mapper(ContentAssociation, self.tbl_content_associations,
2236 properties = dict(ca_id = self.tbl_content_associations.c.id,
2237 filename_id = self.tbl_content_associations.c.filename,
2238 filename = relation(ContentFilename),
2239 filepath_id = self.tbl_content_associations.c.filepath,
2240 filepath = relation(ContentFilepath),
2241 binary_id = self.tbl_content_associations.c.binary_pkg,
2242 binary = relation(DBBinary)))
2245 mapper(ContentFilename, self.tbl_content_file_names,
2246 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2247 filename = self.tbl_content_file_names.c.file))
2249 mapper(ContentFilepath, self.tbl_content_file_paths,
2250 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2251 filepath = self.tbl_content_file_paths.c.path))
2253 mapper(DSCFile, self.tbl_dsc_files,
2254 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2255 source_id = self.tbl_dsc_files.c.source,
2256 source = relation(DBSource),
2257 poolfile_id = self.tbl_dsc_files.c.file,
2258 poolfile = relation(PoolFile)))
2260 mapper(PoolFile, self.tbl_files,
2261 properties = dict(file_id = self.tbl_files.c.id,
2262 filesize = self.tbl_files.c.size,
2263 location_id = self.tbl_files.c.location,
2264 location = relation(Location)))
2266 mapper(Fingerprint, self.tbl_fingerprint,
2267 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2268 uid_id = self.tbl_fingerprint.c.uid,
2269 uid = relation(Uid),
2270 keyring_id = self.tbl_fingerprint.c.keyring,
2271 keyring = relation(Keyring),
2272 source_acl = relation(SourceACL),
2273 binary_acl = relation(BinaryACL)))
2275 mapper(Keyring, self.tbl_keyrings,
2276 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2277 keyring_id = self.tbl_keyrings.c.id))
2279 mapper(Location, self.tbl_location,
2280 properties = dict(location_id = self.tbl_location.c.id,
2281 component_id = self.tbl_location.c.component,
2282 component = relation(Component),
2283 archive_id = self.tbl_location.c.archive,
2284 archive = relation(Archive),
2285 archive_type = self.tbl_location.c.type))
2287 mapper(Maintainer, self.tbl_maintainer,
2288 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2290 mapper(NewComment, self.tbl_new_comments,
2291 properties = dict(comment_id = self.tbl_new_comments.c.id))
2293 mapper(Override, self.tbl_override,
2294 properties = dict(suite_id = self.tbl_override.c.suite,
2295 suite = relation(Suite),
2296 component_id = self.tbl_override.c.component,
2297 component = relation(Component),
2298 priority_id = self.tbl_override.c.priority,
2299 priority = relation(Priority),
2300 section_id = self.tbl_override.c.section,
2301 section = relation(Section),
2302 overridetype_id = self.tbl_override.c.type,
2303 overridetype = relation(OverrideType)))
2305 mapper(OverrideType, self.tbl_override_type,
2306 properties = dict(overridetype = self.tbl_override_type.c.type,
2307 overridetype_id = self.tbl_override_type.c.id))
2309 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2310 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2311 filepath_id = self.tbl_pending_content_associations.c.filepath,
2312 filepath = relation(ContentFilepath),
2313 filename_id = self.tbl_pending_content_associations.c.filename,
2314 filename = relation(ContentFilename)))
2316 mapper(Priority, self.tbl_priority,
2317 properties = dict(priority_id = self.tbl_priority.c.id))
2319 mapper(Queue, self.tbl_queue,
2320 properties = dict(queue_id = self.tbl_queue.c.id))
2322 mapper(QueueBuild, self.tbl_queue_build,
2323 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2324 queue_id = self.tbl_queue_build.c.queue,
2325 queue = relation(Queue, backref='queuebuild')))
2327 mapper(Section, self.tbl_section,
2328 properties = dict(section_id = self.tbl_section.c.id))
2330 mapper(DBSource, self.tbl_source,
2331 properties = dict(source_id = self.tbl_source.c.id,
2332 version = self.tbl_source.c.version,
2333 maintainer_id = self.tbl_source.c.maintainer,
2334 maintainer = relation(Maintainer,
2335 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2336 poolfile_id = self.tbl_source.c.file,
2337 poolfile = relation(PoolFile),
2338 fingerprint_id = self.tbl_source.c.sig_fpr,
2339 fingerprint = relation(Fingerprint),
2340 changedby_id = self.tbl_source.c.changedby,
2341 changedby = relation(Maintainer,
2342 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2343 srcfiles = relation(DSCFile,
2344 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2345 srcassociations = relation(SrcAssociation,
2346 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2348 mapper(SourceACL, self.tbl_source_acl,
2349 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2351 mapper(SrcAssociation, self.tbl_src_associations,
2352 properties = dict(sa_id = self.tbl_src_associations.c.id,
2353 suite_id = self.tbl_src_associations.c.suite,
2354 suite = relation(Suite),
2355 source_id = self.tbl_src_associations.c.source,
2356 source = relation(DBSource)))
2358 mapper(SrcFormat, self.tbl_src_format,
2359 properties = dict(src_format_id = self.tbl_src_format.c.id,
2360 format_name = self.tbl_src_format.c.format_name))
2362 mapper(SrcUploader, self.tbl_src_uploaders,
2363 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2364 source_id = self.tbl_src_uploaders.c.source,
2365 source = relation(DBSource,
2366 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2367 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2368 maintainer = relation(Maintainer,
2369 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2371 mapper(Suite, self.tbl_suite,
2372 properties = dict(suite_id = self.tbl_suite.c.id))
2374 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2375 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2376 suite = relation(Suite, backref='suitearchitectures'),
2377 arch_id = self.tbl_suite_architectures.c.architecture,
2378 architecture = relation(Architecture)))
2380 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2381 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2382 suite = relation(Suite, backref='suitesrcformats'),
2383 src_format_id = self.tbl_suite_src_formats.c.src_format,
2384 src_format = relation(SrcFormat)))
2386 mapper(Uid, self.tbl_uid,
2387 properties = dict(uid_id = self.tbl_uid.c.id,
2388 fingerprint = relation(Fingerprint)))
2390 mapper(UploadBlock, self.tbl_upload_blocks,
2391 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
2392 fingerprint = relation(Fingerprint, backref="uploadblocks"),
2393 uid = relation(Uid, backref="uploadblocks")))
2395 ## Connection functions
2396 def __createconn(self):
2397 from config import Config
2401 connstr = "postgres://%s" % cnf["DB::Host"]
2402 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2403 connstr += ":%s" % cnf["DB::Port"]
2404 connstr += "/%s" % cnf["DB::Name"]
2407 connstr = "postgres:///%s" % cnf["DB::Name"]
2408 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2409 connstr += "?port=%s" % cnf["DB::Port"]
2411 self.db_pg = create_engine(connstr, echo=self.debug)
2412 self.db_meta = MetaData()
2413 self.db_meta.bind = self.db_pg
2414 self.db_smaker = sessionmaker(bind=self.db_pg,
2418 self.__setuptables()
2419 self.__setupmappers()
2422 return self.db_smaker()
2424 __all__.append('DBConn')