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_fingerprint(fpr, session=None):
855 Returns Fingerprint object for given fpr.
858 @param fpr: The fpr to find / add
860 @type session: SQLAlchemy
861 @param session: Optional SQL session object (a temporary one will be
862 generated if not supplied).
865 @return: the Fingerprint object for the given fpr or None
868 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
872 except NoResultFound:
877 __all__.append('get_fingerprint')
880 def get_or_set_fingerprint(fpr, session=None):
882 Returns Fingerprint object for given fpr.
884 If no matching fpr is found, a row is inserted.
887 @param fpr: The fpr to find / add
889 @type session: SQLAlchemy
890 @param session: Optional SQL session object (a temporary one will be
891 generated if not supplied). If not passed, a commit will be performed at
892 the end of the function, otherwise the caller is responsible for commiting.
893 A flush will be performed either way.
896 @return: the Fingerprint object for the given fpr
899 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
903 except NoResultFound:
904 fingerprint = Fingerprint()
905 fingerprint.fingerprint = fpr
906 session.add(fingerprint)
907 session.commit_or_flush()
912 __all__.append('get_or_set_fingerprint')
914 ################################################################################
916 class Keyring(object):
917 def __init__(self, *args, **kwargs):
921 return '<Keyring %s>' % self.keyring_name
923 __all__.append('Keyring')
926 def get_or_set_keyring(keyring, session=None):
928 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
929 and return the new Keyring
930 If C{keyring} already has an entry, simply return the existing Keyring
932 @type keyring: string
933 @param keyring: the keyring name
936 @return: the Keyring object for this keyring
939 q = session.query(Keyring).filter_by(keyring_name=keyring)
943 except NoResultFound:
944 obj = Keyring(keyring_name=keyring)
946 session.commit_or_flush()
949 __all__.append('get_or_set_keyring')
951 ################################################################################
953 class KeyringACLMap(object):
954 def __init__(self, *args, **kwargs):
958 return '<KeyringACLMap %s>' % self.keyring_acl_map_id
960 __all__.append('KeyringACLMap')
962 ################################################################################
964 class Location(object):
965 def __init__(self, *args, **kwargs):
969 return '<Location %s (%s)>' % (self.path, self.location_id)
971 __all__.append('Location')
974 def get_location(location, component=None, archive=None, session=None):
976 Returns Location object for the given combination of location, component
979 @type location: string
980 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
982 @type component: string
983 @param component: the component name (if None, no restriction applied)
985 @type archive: string
986 @param archive_id: the archive name (if None, no restriction applied)
988 @rtype: Location / None
989 @return: Either a Location object or None if one can't be found
992 q = session.query(Location).filter_by(path=location)
994 if archive is not None:
995 q = q.join(Archive).filter_by(archive_name=archive)
997 if component is not None:
998 q = q.join(Component).filter_by(component_name=component)
1002 except NoResultFound:
1005 __all__.append('get_location')
1007 ################################################################################
1009 class Maintainer(object):
1010 def __init__(self, *args, **kwargs):
1014 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
1016 def get_split_maintainer(self):
1017 if not hasattr(self, 'name') or self.name is None:
1018 return ('', '', '', '')
1020 return fix_maintainer(self.name.strip())
1022 __all__.append('Maintainer')
1025 def get_or_set_maintainer(name, session=None):
1027 Returns Maintainer object for given maintainer name.
1029 If no matching maintainer name is found, a row is inserted.
1032 @param name: The maintainer name to add
1034 @type session: SQLAlchemy
1035 @param session: Optional SQL session object (a temporary one will be
1036 generated if not supplied). If not passed, a commit will be performed at
1037 the end of the function, otherwise the caller is responsible for commiting.
1038 A flush will be performed either way.
1041 @return: the Maintainer object for the given maintainer
1044 q = session.query(Maintainer).filter_by(name=name)
1047 except NoResultFound:
1048 maintainer = Maintainer()
1049 maintainer.name = name
1050 session.add(maintainer)
1051 session.commit_or_flush()
1056 __all__.append('get_or_set_maintainer')
1059 def get_maintainer(maintainer_id, session=None):
1061 Return the name of the maintainer behind C{maintainer_id} or None if that
1062 maintainer_id is invalid.
1064 @type maintainer_id: int
1065 @param maintainer_id: the id of the maintainer
1068 @return: the Maintainer with this C{maintainer_id}
1071 return session.query(Maintainer).get(maintainer_id)
1073 __all__.append('get_maintainer')
1075 ################################################################################
1077 class NewComment(object):
1078 def __init__(self, *args, **kwargs):
1082 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1084 __all__.append('NewComment')
1087 def has_new_comment(package, version, session=None):
1089 Returns true if the given combination of C{package}, C{version} has a comment.
1091 @type package: string
1092 @param package: name of the package
1094 @type version: string
1095 @param version: package version
1097 @type session: Session
1098 @param session: Optional SQLA session object (a temporary one will be
1099 generated if not supplied)
1105 q = session.query(NewComment)
1106 q = q.filter_by(package=package)
1107 q = q.filter_by(version=version)
1109 return bool(q.count() > 0)
1111 __all__.append('has_new_comment')
1114 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1116 Returns (possibly empty) list of NewComment objects for the given
1119 @type package: string (optional)
1120 @param package: name of the package
1122 @type version: string (optional)
1123 @param version: package version
1125 @type comment_id: int (optional)
1126 @param comment_id: An id of a comment
1128 @type session: Session
1129 @param session: Optional SQLA session object (a temporary one will be
1130 generated if not supplied)
1133 @return: A (possibly empty) list of NewComment objects will be returned
1136 q = session.query(NewComment)
1137 if package is not None: q = q.filter_by(package=package)
1138 if version is not None: q = q.filter_by(version=version)
1139 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1143 __all__.append('get_new_comments')
1145 ################################################################################
1147 class Override(object):
1148 def __init__(self, *args, **kwargs):
1152 return '<Override %s (%s)>' % (self.package, self.suite_id)
1154 __all__.append('Override')
1157 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1159 Returns Override object for the given parameters
1161 @type package: string
1162 @param package: The name of the package
1164 @type suite: string, list or None
1165 @param suite: The name of the suite (or suites if a list) to limit to. If
1166 None, don't limit. Defaults to None.
1168 @type component: string, list or None
1169 @param component: The name of the component (or components if a list) to
1170 limit to. If None, don't limit. Defaults to None.
1172 @type overridetype: string, list or None
1173 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1174 limit to. If None, don't limit. Defaults to None.
1176 @type session: Session
1177 @param session: Optional SQLA session object (a temporary one will be
1178 generated if not supplied)
1181 @return: A (possibly empty) list of Override objects will be returned
1184 q = session.query(Override)
1185 q = q.filter_by(package=package)
1187 if suite is not None:
1188 if not isinstance(suite, list): suite = [suite]
1189 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1191 if component is not None:
1192 if not isinstance(component, list): component = [component]
1193 q = q.join(Component).filter(Component.component_name.in_(component))
1195 if overridetype is not None:
1196 if not isinstance(overridetype, list): overridetype = [overridetype]
1197 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1201 __all__.append('get_override')
1204 ################################################################################
1206 class OverrideType(object):
1207 def __init__(self, *args, **kwargs):
1211 return '<OverrideType %s>' % self.overridetype
1213 __all__.append('OverrideType')
1216 def get_override_type(override_type, session=None):
1218 Returns OverrideType object for given C{override type}.
1220 @type override_type: string
1221 @param override_type: The name of the override type
1223 @type session: Session
1224 @param session: Optional SQLA session object (a temporary one will be
1225 generated if not supplied)
1228 @return: the database id for the given override type
1231 q = session.query(OverrideType).filter_by(overridetype=override_type)
1235 except NoResultFound:
1238 __all__.append('get_override_type')
1240 ################################################################################
1242 class PendingContentAssociation(object):
1243 def __init__(self, *args, **kwargs):
1247 return '<PendingContentAssociation %s>' % self.pca_id
1249 __all__.append('PendingContentAssociation')
1251 def insert_pending_content_paths(package, fullpaths, session=None):
1253 Make sure given paths are temporarily associated with given
1257 @param package: the package to associate with should have been read in from the binary control file
1258 @type fullpaths: list
1259 @param fullpaths: the list of paths of the file being associated with the binary
1260 @type session: SQLAlchemy session
1261 @param session: Optional SQLAlchemy session. If this is passed, the caller
1262 is responsible for ensuring a transaction has begun and committing the
1263 results or rolling back based on the result code. If not passed, a commit
1264 will be performed at the end of the function
1266 @return: True upon success, False if there is a problem
1269 privatetrans = False
1272 session = DBConn().session()
1276 arch = get_architecture(package['Architecture'], session)
1277 arch_id = arch.arch_id
1279 # Remove any already existing recorded files for this package
1280 q = session.query(PendingContentAssociation)
1281 q = q.filter_by(package=package['Package'])
1282 q = q.filter_by(version=package['Version'])
1283 q = q.filter_by(architecture=arch_id)
1288 for fullpath in fullpaths:
1289 (path, file) = os.path.split(fullpath)
1291 if path.startswith( "./" ):
1294 filepath_id = get_or_set_contents_path_id(path, session)
1295 filename_id = get_or_set_contents_file_id(file, session)
1297 pathcache[fullpath] = (filepath_id, filename_id)
1299 for fullpath, dat in pathcache.items():
1300 pca = PendingContentAssociation()
1301 pca.package = package['Package']
1302 pca.version = package['Version']
1303 pca.filepath_id = dat[0]
1304 pca.filename_id = dat[1]
1305 pca.architecture = arch_id
1308 # Only commit if we set up the session ourself
1316 except Exception, e:
1317 traceback.print_exc()
1319 # Only rollback if we set up the session ourself
1326 __all__.append('insert_pending_content_paths')
1328 ################################################################################
1330 class Priority(object):
1331 def __init__(self, *args, **kwargs):
1334 def __eq__(self, val):
1335 if isinstance(val, str):
1336 return (self.priority == val)
1337 # This signals to use the normal comparison operator
1338 return NotImplemented
1340 def __ne__(self, val):
1341 if isinstance(val, str):
1342 return (self.priority != val)
1343 # This signals to use the normal comparison operator
1344 return NotImplemented
1347 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1349 __all__.append('Priority')
1352 def get_priority(priority, session=None):
1354 Returns Priority object for given C{priority name}.
1356 @type priority: string
1357 @param priority: The name of the priority
1359 @type session: Session
1360 @param session: Optional SQLA session object (a temporary one will be
1361 generated if not supplied)
1364 @return: Priority object for the given priority
1367 q = session.query(Priority).filter_by(priority=priority)
1371 except NoResultFound:
1374 __all__.append('get_priority')
1377 def get_priorities(session=None):
1379 Returns dictionary of priority names -> id mappings
1381 @type session: Session
1382 @param session: Optional SQL session object (a temporary one will be
1383 generated if not supplied)
1386 @return: dictionary of priority names -> id mappings
1390 q = session.query(Priority)
1392 ret[x.priority] = x.priority_id
1396 __all__.append('get_priorities')
1398 ################################################################################
1400 class Queue(object):
1401 def __init__(self, *args, **kwargs):
1405 return '<Queue %s>' % self.queue_name
1407 def autobuild_upload(self, changes, srcpath, session=None):
1409 Update queue_build database table used for incoming autobuild support.
1411 @type changes: Changes
1412 @param changes: changes object for the upload to process
1414 @type srcpath: string
1415 @param srcpath: path for the queue file entries/link destinations
1417 @type session: SQLAlchemy session
1418 @param session: Optional SQLAlchemy session. If this is passed, the
1419 caller is responsible for ensuring a transaction has begun and
1420 committing the results or rolling back based on the result code. If
1421 not passed, a commit will be performed at the end of the function,
1422 otherwise the caller is responsible for commiting.
1424 @rtype: NoneType or string
1425 @return: None if the operation failed, a string describing the error if not
1428 privatetrans = False
1430 session = DBConn().session()
1433 # TODO: Remove by moving queue config into the database
1436 for suitename in changes.changes["distribution"].keys():
1437 # TODO: Move into database as:
1438 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1439 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1440 # This also gets rid of the SecurityQueueBuild hack below
1441 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1445 s = get_suite(suitename, session)
1447 return "INTERNAL ERROR: Could not find suite %s" % suitename
1449 # TODO: Get from database as above
1450 dest_dir = conf["Dir::QueueBuild"]
1452 # TODO: Move into database as above
1453 if conf.FindB("Dinstall::SecurityQueueBuild"):
1454 dest_dir = os.path.join(dest_dir, suitename)
1456 for file_entry in changes.files.keys():
1457 src = os.path.join(srcpath, file_entry)
1458 dest = os.path.join(dest_dir, file_entry)
1460 # TODO: Move into database as above
1461 if conf.FindB("Dinstall::SecurityQueueBuild"):
1462 # Copy it since the original won't be readable by www-data
1464 utils.copy(src, dest)
1466 # Create a symlink to it
1467 os.symlink(src, dest)
1470 qb.suite_id = s.suite_id
1471 qb.queue_id = self.queue_id
1477 # If the .orig tarballs are in the pool, create a symlink to
1478 # them (if one doesn't already exist)
1479 for dsc_file in changes.dsc_files.keys():
1480 # Skip all files except orig tarballs
1481 from daklib.regexes import re_is_orig_source
1482 if not re_is_orig_source.match(dsc_file):
1484 # Skip orig files not identified in the pool
1485 if not (changes.orig_files.has_key(dsc_file) and
1486 changes.orig_files[dsc_file].has_key("id")):
1488 orig_file_id = changes.orig_files[dsc_file]["id"]
1489 dest = os.path.join(dest_dir, dsc_file)
1491 # If it doesn't exist, create a symlink
1492 if not os.path.exists(dest):
1493 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1494 {'id': orig_file_id})
1497 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1499 src = os.path.join(res[0], res[1])
1500 os.symlink(src, dest)
1502 # Add it to the list of packages for later processing by apt-ftparchive
1504 qb.suite_id = s.suite_id
1505 qb.queue_id = self.queue_id
1510 # If it does, update things to ensure it's not removed prematurely
1512 qb = get_queue_build(dest, s.suite_id, session)
1524 __all__.append('Queue')
1527 def get_or_set_queue(queuename, session=None):
1529 Returns Queue object for given C{queue name}, creating it if it does not
1532 @type queuename: string
1533 @param queuename: The name of the queue
1535 @type session: Session
1536 @param session: Optional SQLA session object (a temporary one will be
1537 generated if not supplied)
1540 @return: Queue object for the given queue
1543 q = session.query(Queue).filter_by(queue_name=queuename)
1547 except NoResultFound:
1549 queue.queue_name = queuename
1551 session.commit_or_flush()
1556 __all__.append('get_or_set_queue')
1558 ################################################################################
1560 class QueueBuild(object):
1561 def __init__(self, *args, **kwargs):
1565 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1567 __all__.append('QueueBuild')
1570 def get_queue_build(filename, suite, session=None):
1572 Returns QueueBuild object for given C{filename} and C{suite}.
1574 @type filename: string
1575 @param filename: The name of the file
1577 @type suiteid: int or str
1578 @param suiteid: Suite name or ID
1580 @type session: Session
1581 @param session: Optional SQLA session object (a temporary one will be
1582 generated if not supplied)
1585 @return: Queue object for the given queue
1588 if isinstance(suite, int):
1589 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1591 q = session.query(QueueBuild).filter_by(filename=filename)
1592 q = q.join(Suite).filter_by(suite_name=suite)
1596 except NoResultFound:
1599 __all__.append('get_queue_build')
1601 ################################################################################
1603 class Section(object):
1604 def __init__(self, *args, **kwargs):
1607 def __eq__(self, val):
1608 if isinstance(val, str):
1609 return (self.section == val)
1610 # This signals to use the normal comparison operator
1611 return NotImplemented
1613 def __ne__(self, val):
1614 if isinstance(val, str):
1615 return (self.section != val)
1616 # This signals to use the normal comparison operator
1617 return NotImplemented
1620 return '<Section %s>' % self.section
1622 __all__.append('Section')
1625 def get_section(section, session=None):
1627 Returns Section object for given C{section name}.
1629 @type section: string
1630 @param section: The name of the section
1632 @type session: Session
1633 @param session: Optional SQLA session object (a temporary one will be
1634 generated if not supplied)
1637 @return: Section object for the given section name
1640 q = session.query(Section).filter_by(section=section)
1644 except NoResultFound:
1647 __all__.append('get_section')
1650 def get_sections(session=None):
1652 Returns dictionary of section names -> id mappings
1654 @type session: Session
1655 @param session: Optional SQL session object (a temporary one will be
1656 generated if not supplied)
1659 @return: dictionary of section names -> id mappings
1663 q = session.query(Section)
1665 ret[x.section] = x.section_id
1669 __all__.append('get_sections')
1671 ################################################################################
1673 class DBSource(object):
1674 def __init__(self, *args, **kwargs):
1678 return '<DBSource %s (%s)>' % (self.source, self.version)
1680 __all__.append('DBSource')
1683 def source_exists(source, source_version, suites = ["any"], session=None):
1685 Ensure that source exists somewhere in the archive for the binary
1686 upload being processed.
1687 1. exact match => 1.0-3
1688 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1690 @type package: string
1691 @param package: package source name
1693 @type source_version: string
1694 @param source_version: expected source version
1697 @param suites: list of suites to check in, default I{any}
1699 @type session: Session
1700 @param session: Optional SQLA session object (a temporary one will be
1701 generated if not supplied)
1704 @return: returns 1 if a source with expected version is found, otherwise 0
1711 for suite in suites:
1712 q = session.query(DBSource).filter_by(source=source)
1714 # source must exist in suite X, or in some other suite that's
1715 # mapped to X, recursively... silent-maps are counted too,
1716 # unreleased-maps aren't.
1717 maps = cnf.ValueList("SuiteMappings")[:]
1719 maps = [ m.split() for m in maps ]
1720 maps = [ (x[1], x[2]) for x in maps
1721 if x[0] == "map" or x[0] == "silent-map" ]
1724 if x[1] in s and x[0] not in s:
1727 q = q.join(SrcAssociation).join(Suite)
1728 q = q.filter(Suite.suite_name.in_(s))
1730 # Reduce the query results to a list of version numbers
1731 ql = [ j.version for j in q.all() ]
1734 if source_version in ql:
1738 from daklib.regexes import re_bin_only_nmu
1739 orig_source_version = re_bin_only_nmu.sub('', source_version)
1740 if orig_source_version in ql:
1743 # No source found so return not ok
1748 __all__.append('source_exists')
1751 def get_suites_source_in(source, session=None):
1753 Returns list of Suite objects which given C{source} name is in
1756 @param source: DBSource package name to search for
1759 @return: list of Suite objects for the given source
1762 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1764 __all__.append('get_suites_source_in')
1767 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1769 Returns list of DBSource objects for given C{source} name and other parameters
1772 @param source: DBSource package name to search for
1774 @type source: str or None
1775 @param source: DBSource version name to search for or None if not applicable
1777 @type dm_upload_allowed: bool
1778 @param dm_upload_allowed: If None, no effect. If True or False, only
1779 return packages with that dm_upload_allowed setting
1781 @type session: Session
1782 @param session: Optional SQL session object (a temporary one will be
1783 generated if not supplied)
1786 @return: list of DBSource objects for the given name (may be empty)
1789 q = session.query(DBSource).filter_by(source=source)
1791 if version is not None:
1792 q = q.filter_by(version=version)
1794 if dm_upload_allowed is not None:
1795 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1799 __all__.append('get_sources_from_name')
1802 def get_source_in_suite(source, suite, session=None):
1804 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1806 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1807 - B{suite} - a suite name, eg. I{unstable}
1809 @type source: string
1810 @param source: source package name
1813 @param suite: the suite name
1816 @return: the version for I{source} in I{suite}
1820 q = session.query(SrcAssociation)
1821 q = q.join('source').filter_by(source=source)
1822 q = q.join('suite').filter_by(suite_name=suite)
1825 return q.one().source
1826 except NoResultFound:
1829 __all__.append('get_source_in_suite')
1831 ################################################################################
1833 class SourceACL(object):
1834 def __init__(self, *args, **kwargs):
1838 return '<SourceACL %s>' % self.source_acl_id
1840 __all__.append('SourceACL')
1842 ################################################################################
1844 class SrcAssociation(object):
1845 def __init__(self, *args, **kwargs):
1849 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1851 __all__.append('SrcAssociation')
1853 ################################################################################
1855 class SrcFormat(object):
1856 def __init__(self, *args, **kwargs):
1860 return '<SrcFormat %s>' % (self.format_name)
1862 __all__.append('SrcFormat')
1864 ################################################################################
1866 class SrcUploader(object):
1867 def __init__(self, *args, **kwargs):
1871 return '<SrcUploader %s>' % self.uploader_id
1873 __all__.append('SrcUploader')
1875 ################################################################################
1877 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1878 ('SuiteID', 'suite_id'),
1879 ('Version', 'version'),
1880 ('Origin', 'origin'),
1882 ('Description', 'description'),
1883 ('Untouchable', 'untouchable'),
1884 ('Announce', 'announce'),
1885 ('Codename', 'codename'),
1886 ('OverrideCodename', 'overridecodename'),
1887 ('ValidTime', 'validtime'),
1888 ('Priority', 'priority'),
1889 ('NotAutomatic', 'notautomatic'),
1890 ('CopyChanges', 'copychanges'),
1891 ('CopyDotDak', 'copydotdak'),
1892 ('CommentsDir', 'commentsdir'),
1893 ('OverrideSuite', 'overridesuite'),
1894 ('ChangelogBase', 'changelogbase')]
1897 class Suite(object):
1898 def __init__(self, *args, **kwargs):
1902 return '<Suite %s>' % self.suite_name
1904 def __eq__(self, val):
1905 if isinstance(val, str):
1906 return (self.suite_name == val)
1907 # This signals to use the normal comparison operator
1908 return NotImplemented
1910 def __ne__(self, val):
1911 if isinstance(val, str):
1912 return (self.suite_name != val)
1913 # This signals to use the normal comparison operator
1914 return NotImplemented
1918 for disp, field in SUITE_FIELDS:
1919 val = getattr(self, field, None)
1921 ret.append("%s: %s" % (disp, val))
1923 return "\n".join(ret)
1925 __all__.append('Suite')
1928 def get_suite_architecture(suite, architecture, session=None):
1930 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1934 @param suite: Suite name to search for
1936 @type architecture: str
1937 @param architecture: Architecture name to search for
1939 @type session: Session
1940 @param session: Optional SQL session object (a temporary one will be
1941 generated if not supplied)
1943 @rtype: SuiteArchitecture
1944 @return: the SuiteArchitecture object or None
1947 q = session.query(SuiteArchitecture)
1948 q = q.join(Architecture).filter_by(arch_string=architecture)
1949 q = q.join(Suite).filter_by(suite_name=suite)
1953 except NoResultFound:
1956 __all__.append('get_suite_architecture')
1959 def get_suite(suite, session=None):
1961 Returns Suite object for given C{suite name}.
1964 @param suite: The name of the suite
1966 @type session: Session
1967 @param session: Optional SQLA session object (a temporary one will be
1968 generated if not supplied)
1971 @return: Suite object for the requested suite name (None if not present)
1974 q = session.query(Suite).filter_by(suite_name=suite)
1978 except NoResultFound:
1981 __all__.append('get_suite')
1983 ################################################################################
1985 class SuiteArchitecture(object):
1986 def __init__(self, *args, **kwargs):
1990 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1992 __all__.append('SuiteArchitecture')
1995 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1997 Returns list of Architecture objects for given C{suite} name
2000 @param source: Suite name to search for
2002 @type skipsrc: boolean
2003 @param skipsrc: Whether to skip returning the 'source' architecture entry
2006 @type skipall: boolean
2007 @param skipall: Whether to skip returning the 'all' architecture entry
2010 @type session: Session
2011 @param session: Optional SQL session object (a temporary one will be
2012 generated if not supplied)
2015 @return: list of Architecture objects for the given name (may be empty)
2018 q = session.query(Architecture)
2019 q = q.join(SuiteArchitecture)
2020 q = q.join(Suite).filter_by(suite_name=suite)
2023 q = q.filter(Architecture.arch_string != 'source')
2026 q = q.filter(Architecture.arch_string != 'all')
2028 q = q.order_by('arch_string')
2032 __all__.append('get_suite_architectures')
2034 ################################################################################
2036 class SuiteSrcFormat(object):
2037 def __init__(self, *args, **kwargs):
2041 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
2043 __all__.append('SuiteSrcFormat')
2046 def get_suite_src_formats(suite, session=None):
2048 Returns list of allowed SrcFormat for C{suite}.
2051 @param suite: Suite name to search for
2053 @type session: Session
2054 @param session: Optional SQL session object (a temporary one will be
2055 generated if not supplied)
2058 @return: the list of allowed source formats for I{suite}
2061 q = session.query(SrcFormat)
2062 q = q.join(SuiteSrcFormat)
2063 q = q.join(Suite).filter_by(suite_name=suite)
2064 q = q.order_by('format_name')
2068 __all__.append('get_suite_src_formats')
2070 ################################################################################
2073 def __init__(self, *args, **kwargs):
2076 def __eq__(self, val):
2077 if isinstance(val, str):
2078 return (self.uid == val)
2079 # This signals to use the normal comparison operator
2080 return NotImplemented
2082 def __ne__(self, val):
2083 if isinstance(val, str):
2084 return (self.uid != val)
2085 # This signals to use the normal comparison operator
2086 return NotImplemented
2089 return '<Uid %s (%s)>' % (self.uid, self.name)
2091 __all__.append('Uid')
2094 def add_database_user(uidname, session=None):
2096 Adds a database user
2098 @type uidname: string
2099 @param uidname: The uid of the user to add
2101 @type session: SQLAlchemy
2102 @param session: Optional SQL session object (a temporary one will be
2103 generated if not supplied). If not passed, a commit will be performed at
2104 the end of the function, otherwise the caller is responsible for commiting.
2107 @return: the uid object for the given uidname
2110 session.execute("CREATE USER :uid", {'uid': uidname})
2111 session.commit_or_flush()
2113 __all__.append('add_database_user')
2116 def get_or_set_uid(uidname, session=None):
2118 Returns uid object for given uidname.
2120 If no matching uidname is found, a row is inserted.
2122 @type uidname: string
2123 @param uidname: The uid to add
2125 @type session: SQLAlchemy
2126 @param session: Optional SQL session object (a temporary one will be
2127 generated if not supplied). If not passed, a commit will be performed at
2128 the end of the function, otherwise the caller is responsible for commiting.
2131 @return: the uid object for the given uidname
2134 q = session.query(Uid).filter_by(uid=uidname)
2138 except NoResultFound:
2142 session.commit_or_flush()
2147 __all__.append('get_or_set_uid')
2150 def get_uid_from_fingerprint(fpr, session=None):
2151 q = session.query(Uid)
2152 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2156 except NoResultFound:
2159 __all__.append('get_uid_from_fingerprint')
2161 ################################################################################
2163 class UploadBlock(object):
2164 def __init__(self, *args, **kwargs):
2168 return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
2170 __all__.append('UploadBlock')
2172 ################################################################################
2174 class DBConn(Singleton):
2176 database module init.
2178 def __init__(self, *args, **kwargs):
2179 super(DBConn, self).__init__(*args, **kwargs)
2181 def _startup(self, *args, **kwargs):
2183 if kwargs.has_key('debug'):
2187 def __setuptables(self):
2188 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2189 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2190 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2191 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2192 self.tbl_binary_acl = Table('binary_acl', self.db_meta, autoload=True)
2193 self.tbl_binary_acl_map = Table('binary_acl_map', self.db_meta, autoload=True)
2194 self.tbl_component = Table('component', self.db_meta, autoload=True)
2195 self.tbl_config = Table('config', self.db_meta, autoload=True)
2196 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2197 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2198 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2199 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2200 self.tbl_files = Table('files', self.db_meta, autoload=True)
2201 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2202 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2203 self.tbl_keyring_acl_map = Table('keyring_acl_map', self.db_meta, autoload=True)
2204 self.tbl_location = Table('location', self.db_meta, autoload=True)
2205 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2206 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2207 self.tbl_override = Table('override', self.db_meta, autoload=True)
2208 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2209 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2210 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2211 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2212 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2213 self.tbl_section = Table('section', self.db_meta, autoload=True)
2214 self.tbl_source = Table('source', self.db_meta, autoload=True)
2215 self.tbl_source_acl = Table('source_acl', self.db_meta, autoload=True)
2216 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2217 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2218 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2219 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2220 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2221 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2222 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2223 self.tbl_upload_blocks = Table('upload_blocks', self.db_meta, autoload=True)
2225 def __setupmappers(self):
2226 mapper(Architecture, self.tbl_architecture,
2227 properties = dict(arch_id = self.tbl_architecture.c.id))
2229 mapper(Archive, self.tbl_archive,
2230 properties = dict(archive_id = self.tbl_archive.c.id,
2231 archive_name = self.tbl_archive.c.name))
2233 mapper(BinAssociation, self.tbl_bin_associations,
2234 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2235 suite_id = self.tbl_bin_associations.c.suite,
2236 suite = relation(Suite),
2237 binary_id = self.tbl_bin_associations.c.bin,
2238 binary = relation(DBBinary)))
2240 mapper(DBBinary, self.tbl_binaries,
2241 properties = dict(binary_id = self.tbl_binaries.c.id,
2242 package = self.tbl_binaries.c.package,
2243 version = self.tbl_binaries.c.version,
2244 maintainer_id = self.tbl_binaries.c.maintainer,
2245 maintainer = relation(Maintainer),
2246 source_id = self.tbl_binaries.c.source,
2247 source = relation(DBSource),
2248 arch_id = self.tbl_binaries.c.architecture,
2249 architecture = relation(Architecture),
2250 poolfile_id = self.tbl_binaries.c.file,
2251 poolfile = relation(PoolFile),
2252 binarytype = self.tbl_binaries.c.type,
2253 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2254 fingerprint = relation(Fingerprint),
2255 install_date = self.tbl_binaries.c.install_date,
2256 binassociations = relation(BinAssociation,
2257 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2259 mapper(BinaryACL, self.tbl_binary_acl,
2260 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2262 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2263 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
2264 fingerprint = relation(Fingerprint, backref="binary_acl_map"),
2265 architecture = relation(Architecture)))
2267 mapper(Component, self.tbl_component,
2268 properties = dict(component_id = self.tbl_component.c.id,
2269 component_name = self.tbl_component.c.name))
2271 mapper(DBConfig, self.tbl_config,
2272 properties = dict(config_id = self.tbl_config.c.id))
2274 mapper(ContentAssociation, self.tbl_content_associations,
2275 properties = dict(ca_id = self.tbl_content_associations.c.id,
2276 filename_id = self.tbl_content_associations.c.filename,
2277 filename = relation(ContentFilename),
2278 filepath_id = self.tbl_content_associations.c.filepath,
2279 filepath = relation(ContentFilepath),
2280 binary_id = self.tbl_content_associations.c.binary_pkg,
2281 binary = relation(DBBinary)))
2284 mapper(ContentFilename, self.tbl_content_file_names,
2285 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2286 filename = self.tbl_content_file_names.c.file))
2288 mapper(ContentFilepath, self.tbl_content_file_paths,
2289 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2290 filepath = self.tbl_content_file_paths.c.path))
2292 mapper(DSCFile, self.tbl_dsc_files,
2293 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2294 source_id = self.tbl_dsc_files.c.source,
2295 source = relation(DBSource),
2296 poolfile_id = self.tbl_dsc_files.c.file,
2297 poolfile = relation(PoolFile)))
2299 mapper(PoolFile, self.tbl_files,
2300 properties = dict(file_id = self.tbl_files.c.id,
2301 filesize = self.tbl_files.c.size,
2302 location_id = self.tbl_files.c.location,
2303 location = relation(Location)))
2305 mapper(Fingerprint, self.tbl_fingerprint,
2306 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2307 uid_id = self.tbl_fingerprint.c.uid,
2308 uid = relation(Uid),
2309 keyring_id = self.tbl_fingerprint.c.keyring,
2310 keyring = relation(Keyring),
2311 source_acl = relation(SourceACL),
2312 binary_acl = relation(BinaryACL)))
2314 mapper(Keyring, self.tbl_keyrings,
2315 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2316 keyring_id = self.tbl_keyrings.c.id))
2318 mapper(KeyringACLMap, self.tbl_keyring_acl_map,
2319 properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
2320 keyring = relation(Keyring, backref="keyring_acl_map"),
2321 architecture = relation(Architecture)))
2323 mapper(Location, self.tbl_location,
2324 properties = dict(location_id = self.tbl_location.c.id,
2325 component_id = self.tbl_location.c.component,
2326 component = relation(Component),
2327 archive_id = self.tbl_location.c.archive,
2328 archive = relation(Archive),
2329 archive_type = self.tbl_location.c.type))
2331 mapper(Maintainer, self.tbl_maintainer,
2332 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2334 mapper(NewComment, self.tbl_new_comments,
2335 properties = dict(comment_id = self.tbl_new_comments.c.id))
2337 mapper(Override, self.tbl_override,
2338 properties = dict(suite_id = self.tbl_override.c.suite,
2339 suite = relation(Suite),
2340 component_id = self.tbl_override.c.component,
2341 component = relation(Component),
2342 priority_id = self.tbl_override.c.priority,
2343 priority = relation(Priority),
2344 section_id = self.tbl_override.c.section,
2345 section = relation(Section),
2346 overridetype_id = self.tbl_override.c.type,
2347 overridetype = relation(OverrideType)))
2349 mapper(OverrideType, self.tbl_override_type,
2350 properties = dict(overridetype = self.tbl_override_type.c.type,
2351 overridetype_id = self.tbl_override_type.c.id))
2353 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2354 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2355 filepath_id = self.tbl_pending_content_associations.c.filepath,
2356 filepath = relation(ContentFilepath),
2357 filename_id = self.tbl_pending_content_associations.c.filename,
2358 filename = relation(ContentFilename)))
2360 mapper(Priority, self.tbl_priority,
2361 properties = dict(priority_id = self.tbl_priority.c.id))
2363 mapper(Queue, self.tbl_queue,
2364 properties = dict(queue_id = self.tbl_queue.c.id))
2366 mapper(QueueBuild, self.tbl_queue_build,
2367 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2368 queue_id = self.tbl_queue_build.c.queue,
2369 queue = relation(Queue, backref='queuebuild')))
2371 mapper(Section, self.tbl_section,
2372 properties = dict(section_id = self.tbl_section.c.id))
2374 mapper(DBSource, self.tbl_source,
2375 properties = dict(source_id = self.tbl_source.c.id,
2376 version = self.tbl_source.c.version,
2377 maintainer_id = self.tbl_source.c.maintainer,
2378 maintainer = relation(Maintainer,
2379 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2380 poolfile_id = self.tbl_source.c.file,
2381 poolfile = relation(PoolFile),
2382 fingerprint_id = self.tbl_source.c.sig_fpr,
2383 fingerprint = relation(Fingerprint),
2384 changedby_id = self.tbl_source.c.changedby,
2385 changedby = relation(Maintainer,
2386 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2387 srcfiles = relation(DSCFile,
2388 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2389 srcassociations = relation(SrcAssociation,
2390 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source)),
2391 srcuploaders = relation(SrcUploader)))
2393 mapper(SourceACL, self.tbl_source_acl,
2394 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2396 mapper(SrcAssociation, self.tbl_src_associations,
2397 properties = dict(sa_id = self.tbl_src_associations.c.id,
2398 suite_id = self.tbl_src_associations.c.suite,
2399 suite = relation(Suite),
2400 source_id = self.tbl_src_associations.c.source,
2401 source = relation(DBSource)))
2403 mapper(SrcFormat, self.tbl_src_format,
2404 properties = dict(src_format_id = self.tbl_src_format.c.id,
2405 format_name = self.tbl_src_format.c.format_name))
2407 mapper(SrcUploader, self.tbl_src_uploaders,
2408 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2409 source_id = self.tbl_src_uploaders.c.source,
2410 source = relation(DBSource,
2411 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2412 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2413 maintainer = relation(Maintainer,
2414 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2416 mapper(Suite, self.tbl_suite,
2417 properties = dict(suite_id = self.tbl_suite.c.id))
2419 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2420 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2421 suite = relation(Suite, backref='suitearchitectures'),
2422 arch_id = self.tbl_suite_architectures.c.architecture,
2423 architecture = relation(Architecture)))
2425 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2426 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2427 suite = relation(Suite, backref='suitesrcformats'),
2428 src_format_id = self.tbl_suite_src_formats.c.src_format,
2429 src_format = relation(SrcFormat)))
2431 mapper(Uid, self.tbl_uid,
2432 properties = dict(uid_id = self.tbl_uid.c.id,
2433 fingerprint = relation(Fingerprint)))
2435 mapper(UploadBlock, self.tbl_upload_blocks,
2436 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
2437 fingerprint = relation(Fingerprint, backref="uploadblocks"),
2438 uid = relation(Uid, backref="uploadblocks")))
2440 ## Connection functions
2441 def __createconn(self):
2442 from config import Config
2446 connstr = "postgres://%s" % cnf["DB::Host"]
2447 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2448 connstr += ":%s" % cnf["DB::Port"]
2449 connstr += "/%s" % cnf["DB::Name"]
2452 connstr = "postgres:///%s" % cnf["DB::Name"]
2453 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2454 connstr += "?port=%s" % cnf["DB::Port"]
2456 self.db_pg = create_engine(connstr, echo=self.debug)
2457 self.db_meta = MetaData()
2458 self.db_meta.bind = self.db_pg
2459 self.db_smaker = sessionmaker(bind=self.db_pg,
2463 self.__setuptables()
2464 self.__setupmappers()
2467 return self.db_smaker()
2469 __all__.append('DBConn')