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, select
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
87 session = args[-1] = DBConn().session()
88 private_transaction = True
90 if private_transaction:
91 session.commit_or_flush = session.commit
93 session.commit_or_flush = session.flush
96 return fn(*args, **kwargs)
98 if private_transaction:
99 # We created a session; close it.
102 wrapped.__doc__ = fn.__doc__
103 wrapped.func_name = fn.func_name
107 ################################################################################
109 class Architecture(object):
110 def __init__(self, *args, **kwargs):
113 def __eq__(self, val):
114 if isinstance(val, str):
115 return (self.arch_string== val)
116 # This signals to use the normal comparison operator
117 return NotImplemented
119 def __ne__(self, val):
120 if isinstance(val, str):
121 return (self.arch_string != val)
122 # This signals to use the normal comparison operator
123 return NotImplemented
126 return '<Architecture %s>' % self.arch_string
128 __all__.append('Architecture')
131 def get_architecture(architecture, session=None):
133 Returns database id for given C{architecture}.
135 @type architecture: string
136 @param architecture: The name of the architecture
138 @type session: Session
139 @param session: Optional SQLA session object (a temporary one will be
140 generated if not supplied)
143 @return: Architecture object for the given arch (None if not present)
146 q = session.query(Architecture).filter_by(arch_string=architecture)
150 except NoResultFound:
153 __all__.append('get_architecture')
156 def get_architecture_suites(architecture, session=None):
158 Returns list of Suite objects for given C{architecture} name
161 @param source: Architecture name to search for
163 @type session: Session
164 @param session: Optional SQL session object (a temporary one will be
165 generated if not supplied)
168 @return: list of Suite objects for the given name (may be empty)
171 q = session.query(Suite)
172 q = q.join(SuiteArchitecture)
173 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
179 __all__.append('get_architecture_suites')
181 ################################################################################
183 class Archive(object):
184 def __init__(self, *args, **kwargs):
188 return '<Archive %s>' % self.archive_name
190 __all__.append('Archive')
193 def get_archive(archive, session=None):
195 returns database id for given C{archive}.
197 @type archive: string
198 @param archive: the name of the arhive
200 @type session: Session
201 @param session: Optional SQLA session object (a temporary one will be
202 generated if not supplied)
205 @return: Archive object for the given name (None if not present)
208 archive = archive.lower()
210 q = session.query(Archive).filter_by(archive_name=archive)
214 except NoResultFound:
217 __all__.append('get_archive')
219 ################################################################################
221 class BinAssociation(object):
222 def __init__(self, *args, **kwargs):
226 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
228 __all__.append('BinAssociation')
230 ################################################################################
232 class DBBinary(object):
233 def __init__(self, *args, **kwargs):
237 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
239 __all__.append('DBBinary')
242 def get_suites_binary_in(package, session=None):
244 Returns list of Suite objects which given C{package} name is in
247 @param source: DBBinary package name to search for
250 @return: list of Suite objects for the given package
253 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
255 __all__.append('get_suites_binary_in')
258 def get_binary_from_id(id, session=None):
260 Returns DBBinary object for given C{id}
263 @param id: Id of the required binary
265 @type session: Session
266 @param session: Optional SQLA session object (a temporary one will be
267 generated if not supplied)
270 @return: DBBinary object for the given binary (None if not present)
273 q = session.query(DBBinary).filter_by(binary_id=id)
277 except NoResultFound:
280 __all__.append('get_binary_from_id')
283 def get_binaries_from_name(package, version=None, architecture=None, session=None):
285 Returns list of DBBinary objects for given C{package} name
288 @param package: DBBinary package name to search for
290 @type version: str or None
291 @param version: Version to search for (or None)
293 @type package: str, list or None
294 @param package: Architectures to limit to (or None if no limit)
296 @type session: Session
297 @param session: Optional SQL session object (a temporary one will be
298 generated if not supplied)
301 @return: list of DBBinary objects for the given name (may be empty)
304 q = session.query(DBBinary).filter_by(package=package)
306 if version is not None:
307 q = q.filter_by(version=version)
309 if architecture is not None:
310 if not isinstance(architecture, list):
311 architecture = [architecture]
312 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
318 __all__.append('get_binaries_from_name')
321 def get_binaries_from_source_id(source_id, session=None):
323 Returns list of DBBinary objects for given C{source_id}
326 @param source_id: source_id to search for
328 @type session: Session
329 @param session: Optional SQL session object (a temporary one will be
330 generated if not supplied)
333 @return: list of DBBinary objects for the given name (may be empty)
336 return session.query(DBBinary).filter_by(source_id=source_id).all()
338 __all__.append('get_binaries_from_source_id')
341 def get_binary_from_name_suite(package, suitename, session=None):
342 ### For dak examine-package
343 ### XXX: Doesn't use object API yet
345 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
346 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
347 WHERE b.package=:package
349 AND fi.location = l.id
350 AND l.component = c.id
353 AND su.suite_name=:suitename
354 ORDER BY b.version DESC"""
356 return session.execute(sql, {'package': package, 'suitename': suitename})
358 __all__.append('get_binary_from_name_suite')
361 def get_binary_components(package, suitename, arch, session=None):
362 # Check for packages that have moved from one component to another
363 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
364 WHERE b.package=:package AND s.suite_name=:suitename
365 AND (a.arch_string = :arch OR a.arch_string = 'all')
366 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
367 AND f.location = l.id
368 AND l.component = c.id
371 vals = {'package': package, 'suitename': suitename, 'arch': arch}
373 return session.execute(query, vals)
375 __all__.append('get_binary_components')
377 ################################################################################
379 class Component(object):
380 def __init__(self, *args, **kwargs):
383 def __eq__(self, val):
384 if isinstance(val, str):
385 return (self.component_name == val)
386 # This signals to use the normal comparison operator
387 return NotImplemented
389 def __ne__(self, val):
390 if isinstance(val, str):
391 return (self.component_name != val)
392 # This signals to use the normal comparison operator
393 return NotImplemented
396 return '<Component %s>' % self.component_name
399 __all__.append('Component')
402 def get_component(component, session=None):
404 Returns database id for given C{component}.
406 @type component: string
407 @param component: The name of the override type
410 @return: the database id for the given component
413 component = component.lower()
415 q = session.query(Component).filter_by(component_name=component)
419 except NoResultFound:
422 __all__.append('get_component')
424 ################################################################################
426 class DBConfig(object):
427 def __init__(self, *args, **kwargs):
431 return '<DBConfig %s>' % self.name
433 __all__.append('DBConfig')
435 ################################################################################
437 class ContentFilename(object):
438 def __init__(self, *args, **kwargs):
442 return '<ContentFilename %s>' % self.filename
444 __all__.append('ContentFilename')
447 def get_or_set_contents_file_id(filename, session=None):
449 Returns database id for given filename.
451 If no matching file is found, a row is inserted.
453 @type filename: string
454 @param filename: The filename
455 @type session: SQLAlchemy
456 @param session: Optional SQL session object (a temporary one will be
457 generated if not supplied). If not passed, a commit will be performed at
458 the end of the function, otherwise the caller is responsible for commiting.
461 @return: the database id for the given component
464 q = session.query(ContentFilename).filter_by(filename=filename)
467 ret = q.one().cafilename_id
468 except NoResultFound:
469 cf = ContentFilename()
470 cf.filename = filename
472 session.commit_or_flush()
473 ret = cf.cafilename_id
477 __all__.append('get_or_set_contents_file_id')
480 def get_contents(suite, overridetype, section=None, session=None):
482 Returns contents for a suite / overridetype combination, limiting
483 to a section if not None.
486 @param suite: Suite object
488 @type overridetype: OverrideType
489 @param overridetype: OverrideType object
491 @type section: Section
492 @param section: Optional section object to limit results to
494 @type session: SQLAlchemy
495 @param session: Optional SQL session object (a temporary one will be
496 generated if not supplied)
499 @return: ResultsProxy object set up to return tuples of (filename, section,
503 # find me all of the contents for a given suite
504 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
508 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
509 JOIN content_file_names n ON (c.filename=n.id)
510 JOIN binaries b ON (b.id=c.binary_pkg)
511 JOIN override o ON (o.package=b.package)
512 JOIN section s ON (s.id=o.section)
513 WHERE o.suite = :suiteid AND o.type = :overridetypeid
514 AND b.type=:overridetypename"""
516 vals = {'suiteid': suite.suite_id,
517 'overridetypeid': overridetype.overridetype_id,
518 'overridetypename': overridetype.overridetype}
520 if section is not None:
521 contents_q += " AND s.id = :sectionid"
522 vals['sectionid'] = section.section_id
524 contents_q += " ORDER BY fn"
526 return session.execute(contents_q, vals)
528 __all__.append('get_contents')
530 ################################################################################
532 class ContentFilepath(object):
533 def __init__(self, *args, **kwargs):
537 return '<ContentFilepath %s>' % self.filepath
539 __all__.append('ContentFilepath')
542 def get_or_set_contents_path_id(filepath, session=None):
544 Returns database id for given path.
546 If no matching file is found, a row is inserted.
548 @type filename: string
549 @param filename: The filepath
550 @type session: SQLAlchemy
551 @param session: Optional SQL session object (a temporary one will be
552 generated if not supplied). If not passed, a commit will be performed at
553 the end of the function, otherwise the caller is responsible for commiting.
556 @return: the database id for the given path
559 q = session.query(ContentFilepath).filter_by(filepath=filepath)
562 ret = q.one().cafilepath_id
563 except NoResultFound:
564 cf = ContentFilepath()
565 cf.filepath = filepath
567 session.commit_or_flush()
568 ret = cf.cafilepath_id
572 __all__.append('get_or_set_contents_path_id')
574 ################################################################################
576 class ContentAssociation(object):
577 def __init__(self, *args, **kwargs):
581 return '<ContentAssociation %s>' % self.ca_id
583 __all__.append('ContentAssociation')
585 def insert_content_paths(binary_id, fullpaths, session=None):
587 Make sure given path is associated with given binary id
590 @param binary_id: the id of the binary
591 @type fullpaths: list
592 @param fullpaths: the list of paths of the file being associated with the binary
593 @type session: SQLAlchemy session
594 @param session: Optional SQLAlchemy session. If this is passed, the caller
595 is responsible for ensuring a transaction has begun and committing the
596 results or rolling back based on the result code. If not passed, a commit
597 will be performed at the end of the function, otherwise the caller is
598 responsible for commiting.
600 @return: True upon success
605 session = DBConn().session()
611 for fullpath in fullpaths:
612 # Get the necessary IDs ...
613 (path, file) = os.path.split(fullpath)
615 filepath_id = get_or_set_contents_path_id(path, session)
616 filename_id = get_or_set_contents_file_id(file, session)
618 pathcache[fullpath] = (filepath_id, filename_id)
620 for fullpath, dat in pathcache.items():
621 ca = ContentAssociation()
622 ca.binary_id = binary_id
623 ca.filepath_id = dat[0]
624 ca.filename_id = dat[1]
627 # Only commit if we set up the session ourself
637 traceback.print_exc()
639 # Only rollback if we set up the session ourself
646 __all__.append('insert_content_paths')
648 ################################################################################
650 class DSCFile(object):
651 def __init__(self, *args, **kwargs):
655 return '<DSCFile %s>' % self.dscfile_id
657 __all__.append('DSCFile')
660 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
662 Returns a list of DSCFiles which may be empty
664 @type dscfile_id: int (optional)
665 @param dscfile_id: the dscfile_id of the DSCFiles to find
667 @type source_id: int (optional)
668 @param source_id: the source id related to the DSCFiles to find
670 @type poolfile_id: int (optional)
671 @param poolfile_id: the poolfile id related to the DSCFiles to find
674 @return: Possibly empty list of DSCFiles
677 q = session.query(DSCFile)
679 if dscfile_id is not None:
680 q = q.filter_by(dscfile_id=dscfile_id)
682 if source_id is not None:
683 q = q.filter_by(source_id=source_id)
685 if poolfile_id is not None:
686 q = q.filter_by(poolfile_id=poolfile_id)
690 __all__.append('get_dscfiles')
692 ################################################################################
694 class PoolFile(object):
695 def __init__(self, *args, **kwargs):
699 return '<PoolFile %s>' % self.filename
701 __all__.append('PoolFile')
704 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
707 (ValidFileFound [boolean or None], PoolFile object or None)
709 @type filename: string
710 @param filename: the filename of the file to check against the DB
713 @param filesize: the size of the file to check against the DB
716 @param md5sum: the md5sum of the file to check against the DB
718 @type location_id: int
719 @param location_id: the id of the location to look in
722 @return: Tuple of length 2.
723 If more than one file found with that name:
725 If valid pool file found: (True, PoolFile object)
726 If valid pool file not found:
727 (False, None) if no file found
728 (False, PoolFile object) if file found with size/md5sum mismatch
731 q = session.query(PoolFile).filter_by(filename=filename)
732 q = q.join(Location).filter_by(location_id=location_id)
742 if obj.md5sum != md5sum or obj.filesize != filesize:
750 __all__.append('check_poolfile')
753 def get_poolfile_by_id(file_id, session=None):
755 Returns a PoolFile objects or None for the given id
758 @param file_id: the id of the file to look for
760 @rtype: PoolFile or None
761 @return: either the PoolFile object or None
764 q = session.query(PoolFile).filter_by(file_id=file_id)
768 except NoResultFound:
771 __all__.append('get_poolfile_by_id')
775 def get_poolfile_by_name(filename, location_id=None, session=None):
777 Returns an array of PoolFile objects for the given filename and
778 (optionally) location_id
780 @type filename: string
781 @param filename: the filename of the file to check against the DB
783 @type location_id: int
784 @param location_id: the id of the location to look in (optional)
787 @return: array of PoolFile objects
790 q = session.query(PoolFile).filter_by(filename=filename)
792 if location_id is not None:
793 q = q.join(Location).filter_by(location_id=location_id)
797 __all__.append('get_poolfile_by_name')
800 def get_poolfile_like_name(filename, session=None):
802 Returns an array of PoolFile objects which are like the given name
804 @type filename: string
805 @param filename: the filename of the file to check against the DB
808 @return: array of PoolFile objects
811 # TODO: There must be a way of properly using bind parameters with %FOO%
812 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
816 __all__.append('get_poolfile_like_name')
818 ################################################################################
820 class Fingerprint(object):
821 def __init__(self, *args, **kwargs):
825 return '<Fingerprint %s>' % self.fingerprint
827 __all__.append('Fingerprint')
830 def get_or_set_fingerprint(fpr, session=None):
832 Returns Fingerprint object for given fpr.
834 If no matching fpr is found, a row is inserted.
837 @param fpr: The fpr to find / add
839 @type session: SQLAlchemy
840 @param session: Optional SQL session object (a temporary one will be
841 generated if not supplied). If not passed, a commit will be performed at
842 the end of the function, otherwise the caller is responsible for commiting.
843 A flush will be performed either way.
846 @return: the Fingerprint object for the given fpr
849 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
853 except NoResultFound:
854 fingerprint = Fingerprint()
855 fingerprint.fingerprint = fpr
856 session.add(fingerprint)
857 session.commit_or_flush()
862 __all__.append('get_or_set_fingerprint')
864 ################################################################################
866 class Keyring(object):
867 def __init__(self, *args, **kwargs):
871 return '<Keyring %s>' % self.keyring_name
873 __all__.append('Keyring')
876 def get_or_set_keyring(keyring, session=None):
878 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
879 and return the new Keyring
880 If C{keyring} already has an entry, simply return the existing Keyring
882 @type keyring: string
883 @param keyring: the keyring name
886 @return: the Keyring object for this keyring
889 q = session.query(Keyring).filter_by(keyring_name=keyring)
893 except NoResultFound:
894 obj = Keyring(keyring_name=keyring)
896 session.commit_or_flush()
899 __all__.append('get_or_set_keyring')
901 ################################################################################
903 class Location(object):
904 def __init__(self, *args, **kwargs):
908 return '<Location %s (%s)>' % (self.path, self.location_id)
910 __all__.append('Location')
913 def get_location(location, component=None, archive=None, session=None):
915 Returns Location object for the given combination of location, component
918 @type location: string
919 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
921 @type component: string
922 @param component: the component name (if None, no restriction applied)
924 @type archive: string
925 @param archive_id: the archive name (if None, no restriction applied)
927 @rtype: Location / None
928 @return: Either a Location object or None if one can't be found
931 q = session.query(Location).filter_by(path=location)
933 if archive is not None:
934 q = q.join(Archive).filter_by(archive_name=archive)
936 if component is not None:
937 q = q.join(Component).filter_by(component_name=component)
941 except NoResultFound:
944 __all__.append('get_location')
946 ################################################################################
948 class Maintainer(object):
949 def __init__(self, *args, **kwargs):
953 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
955 def get_split_maintainer(self):
956 if not hasattr(self, 'name') or self.name is None:
957 return ('', '', '', '')
959 return fix_maintainer(self.name.strip())
961 __all__.append('Maintainer')
964 def get_or_set_maintainer(name, session=None):
966 Returns Maintainer object for given maintainer name.
968 If no matching maintainer name is found, a row is inserted.
971 @param name: The maintainer name to add
973 @type session: SQLAlchemy
974 @param session: Optional SQL session object (a temporary one will be
975 generated if not supplied). If not passed, a commit will be performed at
976 the end of the function, otherwise the caller is responsible for commiting.
977 A flush will be performed either way.
980 @return: the Maintainer object for the given maintainer
983 q = session.query(Maintainer).filter_by(name=name)
986 except NoResultFound:
987 maintainer = Maintainer()
988 maintainer.name = name
989 session.add(maintainer)
990 session.commit_or_flush()
995 __all__.append('get_or_set_maintainer')
998 def get_maintainer(maintainer_id, session=None):
1000 Return the name of the maintainer behind C{maintainer_id} or None if that
1001 maintainer_id is invalid.
1003 @type maintainer_id: int
1004 @param maintainer_id: the id of the maintainer
1007 @return: the Maintainer with this C{maintainer_id}
1010 return session.query(Maintainer).get(maintainer_id)
1012 __all__.append('get_maintainer')
1014 ################################################################################
1016 class NewComment(object):
1017 def __init__(self, *args, **kwargs):
1021 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1023 __all__.append('NewComment')
1026 def has_new_comment(package, version, session=None):
1028 Returns true if the given combination of C{package}, C{version} has a comment.
1030 @type package: string
1031 @param package: name of the package
1033 @type version: string
1034 @param version: package version
1036 @type session: Session
1037 @param session: Optional SQLA session object (a temporary one will be
1038 generated if not supplied)
1044 q = session.query(NewComment)
1045 q = q.filter_by(package=package)
1046 q = q.filter_by(version=version)
1048 return bool(q.count() > 0)
1050 __all__.append('has_new_comment')
1053 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1055 Returns (possibly empty) list of NewComment objects for the given
1058 @type package: string (optional)
1059 @param package: name of the package
1061 @type version: string (optional)
1062 @param version: package version
1064 @type comment_id: int (optional)
1065 @param comment_id: An id of a comment
1067 @type session: Session
1068 @param session: Optional SQLA session object (a temporary one will be
1069 generated if not supplied)
1072 @return: A (possibly empty) list of NewComment objects will be returned
1075 q = session.query(NewComment)
1076 if package is not None: q = q.filter_by(package=package)
1077 if version is not None: q = q.filter_by(version=version)
1078 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1082 __all__.append('get_new_comments')
1084 ################################################################################
1086 class Override(object):
1087 def __init__(self, *args, **kwargs):
1091 return '<Override %s (%s)>' % (self.package, self.suite_id)
1093 __all__.append('Override')
1096 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1098 Returns Override object for the given parameters
1100 @type package: string
1101 @param package: The name of the package
1103 @type suite: string, list or None
1104 @param suite: The name of the suite (or suites if a list) to limit to. If
1105 None, don't limit. Defaults to None.
1107 @type component: string, list or None
1108 @param component: The name of the component (or components if a list) to
1109 limit to. If None, don't limit. Defaults to None.
1111 @type overridetype: string, list or None
1112 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1113 limit to. If None, don't limit. Defaults to None.
1115 @type session: Session
1116 @param session: Optional SQLA session object (a temporary one will be
1117 generated if not supplied)
1120 @return: A (possibly empty) list of Override objects will be returned
1123 q = session.query(Override)
1124 q = q.filter_by(package=package)
1126 if suite is not None:
1127 if not isinstance(suite, list): suite = [suite]
1128 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1130 if component is not None:
1131 if not isinstance(component, list): component = [component]
1132 q = q.join(Component).filter(Component.component_name.in_(component))
1134 if overridetype is not None:
1135 if not isinstance(overridetype, list): overridetype = [overridetype]
1136 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1140 __all__.append('get_override')
1143 ################################################################################
1145 class OverrideType(object):
1146 def __init__(self, *args, **kwargs):
1150 return '<OverrideType %s>' % self.overridetype
1152 __all__.append('OverrideType')
1155 def get_override_type(override_type, session=None):
1157 Returns OverrideType object for given C{override type}.
1159 @type override_type: string
1160 @param override_type: The name of the override type
1162 @type session: Session
1163 @param session: Optional SQLA session object (a temporary one will be
1164 generated if not supplied)
1167 @return: the database id for the given override type
1170 q = session.query(OverrideType).filter_by(overridetype=override_type)
1174 except NoResultFound:
1177 __all__.append('get_override_type')
1179 ################################################################################
1181 class PendingContentAssociation(object):
1182 def __init__(self, *args, **kwargs):
1186 return '<PendingContentAssociation %s>' % self.pca_id
1188 __all__.append('PendingContentAssociation')
1190 def insert_pending_content_paths(package, fullpaths, session=None):
1192 Make sure given paths are temporarily associated with given
1196 @param package: the package to associate with should have been read in from the binary control file
1197 @type fullpaths: list
1198 @param fullpaths: the list of paths of the file being associated with the binary
1199 @type session: SQLAlchemy session
1200 @param session: Optional SQLAlchemy session. If this is passed, the caller
1201 is responsible for ensuring a transaction has begun and committing the
1202 results or rolling back based on the result code. If not passed, a commit
1203 will be performed at the end of the function
1205 @return: True upon success, False if there is a problem
1208 privatetrans = False
1211 session = DBConn().session()
1215 arch = get_architecture(package['Architecture'], session)
1216 arch_id = arch.arch_id
1218 # Remove any already existing recorded files for this package
1219 q = session.query(PendingContentAssociation)
1220 q = q.filter_by(package=package['Package'])
1221 q = q.filter_by(version=package['Version'])
1222 q = q.filter_by(architecture=arch_id)
1227 for fullpath in fullpaths:
1228 (path, file) = os.path.split(fullpath)
1230 if path.startswith( "./" ):
1233 filepath_id = get_or_set_contents_path_id(path, session)
1234 filename_id = get_or_set_contents_file_id(file, session)
1236 pathcache[fullpath] = (filepath_id, filename_id)
1238 for fullpath, dat in pathcache.items():
1239 pca = PendingContentAssociation()
1240 pca.package = package['Package']
1241 pca.version = package['Version']
1242 pca.filepath_id = dat[0]
1243 pca.filename_id = dat[1]
1244 pca.architecture = arch_id
1247 # Only commit if we set up the session ourself
1255 except Exception, e:
1256 traceback.print_exc()
1258 # Only rollback if we set up the session ourself
1265 __all__.append('insert_pending_content_paths')
1267 ################################################################################
1269 class Priority(object):
1270 def __init__(self, *args, **kwargs):
1273 def __eq__(self, val):
1274 if isinstance(val, str):
1275 return (self.priority == val)
1276 # This signals to use the normal comparison operator
1277 return NotImplemented
1279 def __ne__(self, val):
1280 if isinstance(val, str):
1281 return (self.priority != val)
1282 # This signals to use the normal comparison operator
1283 return NotImplemented
1286 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1288 __all__.append('Priority')
1291 def get_priority(priority, session=None):
1293 Returns Priority object for given C{priority name}.
1295 @type priority: string
1296 @param priority: The name of the priority
1298 @type session: Session
1299 @param session: Optional SQLA session object (a temporary one will be
1300 generated if not supplied)
1303 @return: Priority object for the given priority
1306 q = session.query(Priority).filter_by(priority=priority)
1310 except NoResultFound:
1313 __all__.append('get_priority')
1316 def get_priorities(session=None):
1318 Returns dictionary of priority names -> id mappings
1320 @type session: Session
1321 @param session: Optional SQL session object (a temporary one will be
1322 generated if not supplied)
1325 @return: dictionary of priority names -> id mappings
1329 q = session.query(Priority)
1331 ret[x.priority] = x.priority_id
1335 __all__.append('get_priorities')
1337 ################################################################################
1339 class Queue(object):
1340 def __init__(self, *args, **kwargs):
1344 return '<Queue %s>' % self.queue_name
1346 def autobuild_upload(self, changes, srcpath, session=None):
1348 Update queue_build database table used for incoming autobuild support.
1350 @type changes: Changes
1351 @param changes: changes object for the upload to process
1353 @type srcpath: string
1354 @param srcpath: path for the queue file entries/link destinations
1356 @type session: SQLAlchemy session
1357 @param session: Optional SQLAlchemy session. If this is passed, the
1358 caller is responsible for ensuring a transaction has begun and
1359 committing the results or rolling back based on the result code. If
1360 not passed, a commit will be performed at the end of the function,
1361 otherwise the caller is responsible for commiting.
1363 @rtype: NoneType or string
1364 @return: None if the operation failed, a string describing the error if not
1367 privatetrans = False
1369 session = DBConn().session()
1372 # TODO: Remove by moving queue config into the database
1375 for suitename in changes.changes["distribution"].keys():
1376 # TODO: Move into database as:
1377 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1378 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1379 # This also gets rid of the SecurityQueueBuild hack below
1380 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1384 s = get_suite(suitename, session)
1386 return "INTERNAL ERROR: Could not find suite %s" % suitename
1388 # TODO: Get from database as above
1389 dest_dir = conf["Dir::QueueBuild"]
1391 # TODO: Move into database as above
1392 if conf.FindB("Dinstall::SecurityQueueBuild"):
1393 dest_dir = os.path.join(dest_dir, suitename)
1395 for file_entry in changes.files.keys():
1396 src = os.path.join(srcpath, file_entry)
1397 dest = os.path.join(dest_dir, file_entry)
1399 # TODO: Move into database as above
1400 if conf.FindB("Dinstall::SecurityQueueBuild"):
1401 # Copy it since the original won't be readable by www-data
1403 utils.copy(src, dest)
1405 # Create a symlink to it
1406 os.symlink(src, dest)
1409 qb.suite_id = s.suite_id
1410 qb.queue_id = self.queue_id
1416 # If the .orig tarballs are in the pool, create a symlink to
1417 # them (if one doesn't already exist)
1418 for dsc_file in changes.dsc_files.keys():
1419 # Skip all files except orig tarballs
1420 if not re_is_orig_source.match(dsc_file):
1422 # Skip orig files not identified in the pool
1423 if not (changes.orig_files.has_key(dsc_file) and
1424 changes.orig_files[dsc_file].has_key("id")):
1426 orig_file_id = changes.orig_files[dsc_file]["id"]
1427 dest = os.path.join(dest_dir, dsc_file)
1429 # If it doesn't exist, create a symlink
1430 if not os.path.exists(dest):
1431 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1432 {'id': orig_file_id})
1435 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1437 src = os.path.join(res[0], res[1])
1438 os.symlink(src, dest)
1440 # Add it to the list of packages for later processing by apt-ftparchive
1442 qb.suite_id = s.suite_id
1443 qb.queue_id = self.queue_id
1448 # If it does, update things to ensure it's not removed prematurely
1450 qb = get_queue_build(dest, s.suite_id, session)
1462 __all__.append('Queue')
1465 def get_queue(queuename, session=None):
1467 Returns Queue object for given C{queue name}.
1469 @type queuename: string
1470 @param queuename: The name of the queue
1472 @type session: Session
1473 @param session: Optional SQLA session object (a temporary one will be
1474 generated if not supplied)
1477 @return: Queue object for the given queue
1480 q = session.query(Queue).filter_by(queue_name=queuename)
1484 except NoResultFound:
1487 __all__.append('get_queue')
1489 ################################################################################
1491 class QueueBuild(object):
1492 def __init__(self, *args, **kwargs):
1496 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1498 __all__.append('QueueBuild')
1501 def get_queue_build(filename, suite, session=None):
1503 Returns QueueBuild object for given C{filename} and C{suite}.
1505 @type filename: string
1506 @param filename: The name of the file
1508 @type suiteid: int or str
1509 @param suiteid: Suite name or ID
1511 @type session: Session
1512 @param session: Optional SQLA session object (a temporary one will be
1513 generated if not supplied)
1516 @return: Queue object for the given queue
1519 if isinstance(suite, int):
1520 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1522 q = session.query(QueueBuild).filter_by(filename=filename)
1523 q = q.join(Suite).filter_by(suite_name=suite)
1527 except NoResultFound:
1530 __all__.append('get_queue_build')
1532 ################################################################################
1534 class Section(object):
1535 def __init__(self, *args, **kwargs):
1538 def __eq__(self, val):
1539 if isinstance(val, str):
1540 return (self.section == val)
1541 # This signals to use the normal comparison operator
1542 return NotImplemented
1544 def __ne__(self, val):
1545 if isinstance(val, str):
1546 return (self.section != val)
1547 # This signals to use the normal comparison operator
1548 return NotImplemented
1551 return '<Section %s>' % self.section
1553 __all__.append('Section')
1556 def get_section(section, session=None):
1558 Returns Section object for given C{section name}.
1560 @type section: string
1561 @param section: The name of the section
1563 @type session: Session
1564 @param session: Optional SQLA session object (a temporary one will be
1565 generated if not supplied)
1568 @return: Section object for the given section name
1571 q = session.query(Section).filter_by(section=section)
1575 except NoResultFound:
1578 __all__.append('get_section')
1581 def get_sections(session=None):
1583 Returns dictionary of section names -> id mappings
1585 @type session: Session
1586 @param session: Optional SQL session object (a temporary one will be
1587 generated if not supplied)
1590 @return: dictionary of section names -> id mappings
1594 q = session.query(Section)
1596 ret[x.section] = x.section_id
1600 __all__.append('get_sections')
1602 ################################################################################
1604 class DBSource(object):
1605 def __init__(self, *args, **kwargs):
1609 return '<DBSource %s (%s)>' % (self.source, self.version)
1611 __all__.append('DBSource')
1614 def source_exists(source, source_version, suites = ["any"], session=None):
1616 Ensure that source exists somewhere in the archive for the binary
1617 upload being processed.
1618 1. exact match => 1.0-3
1619 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1621 @type package: string
1622 @param package: package source name
1624 @type source_version: string
1625 @param source_version: expected source version
1628 @param suites: list of suites to check in, default I{any}
1630 @type session: Session
1631 @param session: Optional SQLA session object (a temporary one will be
1632 generated if not supplied)
1635 @return: returns 1 if a source with expected version is found, otherwise 0
1642 for suite in suites:
1643 q = session.query(DBSource).filter_by(source=source)
1645 # source must exist in suite X, or in some other suite that's
1646 # mapped to X, recursively... silent-maps are counted too,
1647 # unreleased-maps aren't.
1648 maps = cnf.ValueList("SuiteMappings")[:]
1650 maps = [ m.split() for m in maps ]
1651 maps = [ (x[1], x[2]) for x in maps
1652 if x[0] == "map" or x[0] == "silent-map" ]
1655 if x[1] in s and x[0] not in s:
1658 q = q.join(SrcAssociation).join(Suite)
1659 q = q.filter(Suite.suite_name.in_(s))
1661 # Reduce the query results to a list of version numbers
1662 ql = [ j.version for j in q.all() ]
1665 if source_version in ql:
1669 from daklib.regexes import re_bin_only_nmu
1670 orig_source_version = re_bin_only_nmu.sub('', source_version)
1671 if orig_source_version in ql:
1674 # No source found so return not ok
1679 __all__.append('source_exists')
1682 def get_suites_source_in(source, session=None):
1684 Returns list of Suite objects which given C{source} name is in
1687 @param source: DBSource package name to search for
1690 @return: list of Suite objects for the given source
1693 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1695 __all__.append('get_suites_source_in')
1698 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1700 Returns list of DBSource objects for given C{source} name and other parameters
1703 @param source: DBSource package name to search for
1705 @type source: str or None
1706 @param source: DBSource version name to search for or None if not applicable
1708 @type dm_upload_allowed: bool
1709 @param dm_upload_allowed: If None, no effect. If True or False, only
1710 return packages with that dm_upload_allowed setting
1712 @type session: Session
1713 @param session: Optional SQL session object (a temporary one will be
1714 generated if not supplied)
1717 @return: list of DBSource objects for the given name (may be empty)
1720 q = session.query(DBSource).filter_by(source=source)
1722 if version is not None:
1723 q = q.filter_by(version=version)
1725 if dm_upload_allowed is not None:
1726 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1730 __all__.append('get_sources_from_name')
1733 def get_source_in_suite(source, suite, session=None):
1735 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1737 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1738 - B{suite} - a suite name, eg. I{unstable}
1740 @type source: string
1741 @param source: source package name
1744 @param suite: the suite name
1747 @return: the version for I{source} in I{suite}
1751 q = session.query(SrcAssociation)
1752 q = q.join('source').filter_by(source=source)
1753 q = q.join('suite').filter_by(suite_name=suite)
1756 return q.one().source
1757 except NoResultFound:
1760 __all__.append('get_source_in_suite')
1762 ################################################################################
1764 class SrcAssociation(object):
1765 def __init__(self, *args, **kwargs):
1769 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1771 __all__.append('SrcAssociation')
1773 ################################################################################
1775 class SrcFormat(object):
1776 def __init__(self, *args, **kwargs):
1780 return '<SrcFormat %s>' % (self.format_name)
1782 __all__.append('SrcFormat')
1784 ################################################################################
1786 class SrcUploader(object):
1787 def __init__(self, *args, **kwargs):
1791 return '<SrcUploader %s>' % self.uploader_id
1793 __all__.append('SrcUploader')
1795 ################################################################################
1797 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1798 ('SuiteID', 'suite_id'),
1799 ('Version', 'version'),
1800 ('Origin', 'origin'),
1802 ('Description', 'description'),
1803 ('Untouchable', 'untouchable'),
1804 ('Announce', 'announce'),
1805 ('Codename', 'codename'),
1806 ('OverrideCodename', 'overridecodename'),
1807 ('ValidTime', 'validtime'),
1808 ('Priority', 'priority'),
1809 ('NotAutomatic', 'notautomatic'),
1810 ('CopyChanges', 'copychanges'),
1811 ('CopyDotDak', 'copydotdak'),
1812 ('CommentsDir', 'commentsdir'),
1813 ('OverrideSuite', 'overridesuite'),
1814 ('ChangelogBase', 'changelogbase')]
1817 class Suite(object):
1818 def __init__(self, *args, **kwargs):
1822 return '<Suite %s>' % self.suite_name
1824 def __eq__(self, val):
1825 if isinstance(val, str):
1826 return (self.suite_name == val)
1827 # This signals to use the normal comparison operator
1828 return NotImplemented
1830 def __ne__(self, val):
1831 if isinstance(val, str):
1832 return (self.suite_name != val)
1833 # This signals to use the normal comparison operator
1834 return NotImplemented
1838 for disp, field in SUITE_FIELDS:
1839 val = getattr(self, field, None)
1841 ret.append("%s: %s" % (disp, val))
1843 return "\n".join(ret)
1845 __all__.append('Suite')
1848 def get_suite_architecture(suite, architecture, session=None):
1850 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1854 @param suite: Suite name to search for
1856 @type architecture: str
1857 @param architecture: Architecture name to search for
1859 @type session: Session
1860 @param session: Optional SQL session object (a temporary one will be
1861 generated if not supplied)
1863 @rtype: SuiteArchitecture
1864 @return: the SuiteArchitecture object or None
1867 q = session.query(SuiteArchitecture)
1868 q = q.join(Architecture).filter_by(arch_string=architecture)
1869 q = q.join(Suite).filter_by(suite_name=suite)
1873 except NoResultFound:
1876 __all__.append('get_suite_architecture')
1879 def get_suite(suite, session=None):
1881 Returns Suite object for given C{suite name}.
1884 @param suite: The name of the suite
1886 @type session: Session
1887 @param session: Optional SQLA session object (a temporary one will be
1888 generated if not supplied)
1891 @return: Suite object for the requested suite name (None if not present)
1894 q = session.query(Suite).filter_by(suite_name=suite)
1898 except NoResultFound:
1901 __all__.append('get_suite')
1903 ################################################################################
1905 class SuiteArchitecture(object):
1906 def __init__(self, *args, **kwargs):
1910 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1912 __all__.append('SuiteArchitecture')
1915 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1917 Returns list of Architecture objects for given C{suite} name
1920 @param source: Suite name to search for
1922 @type skipsrc: boolean
1923 @param skipsrc: Whether to skip returning the 'source' architecture entry
1926 @type skipall: boolean
1927 @param skipall: Whether to skip returning the 'all' architecture entry
1930 @type session: Session
1931 @param session: Optional SQL session object (a temporary one will be
1932 generated if not supplied)
1935 @return: list of Architecture objects for the given name (may be empty)
1938 q = session.query(Architecture)
1939 q = q.join(SuiteArchitecture)
1940 q = q.join(Suite).filter_by(suite_name=suite)
1943 q = q.filter(Architecture.arch_string != 'source')
1946 q = q.filter(Architecture.arch_string != 'all')
1948 q = q.order_by('arch_string')
1952 __all__.append('get_suite_architectures')
1954 ################################################################################
1956 class SuiteSrcFormat(object):
1957 def __init__(self, *args, **kwargs):
1961 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1963 __all__.append('SuiteSrcFormat')
1966 def get_suite_src_formats(suite, session=None):
1968 Returns list of allowed SrcFormat for C{suite}.
1971 @param suite: Suite name to search for
1973 @type session: Session
1974 @param session: Optional SQL session object (a temporary one will be
1975 generated if not supplied)
1978 @return: the list of allowed source formats for I{suite}
1981 q = session.query(SrcFormat)
1982 q = q.join(SuiteSrcFormat)
1983 q = q.join(Suite).filter_by(suite_name=suite)
1984 q = q.order_by('format_name')
1988 __all__.append('get_suite_src_formats')
1990 ################################################################################
1993 def __init__(self, *args, **kwargs):
1996 def __eq__(self, val):
1997 if isinstance(val, str):
1998 return (self.uid == val)
1999 # This signals to use the normal comparison operator
2000 return NotImplemented
2002 def __ne__(self, val):
2003 if isinstance(val, str):
2004 return (self.uid != val)
2005 # This signals to use the normal comparison operator
2006 return NotImplemented
2009 return '<Uid %s (%s)>' % (self.uid, self.name)
2011 __all__.append('Uid')
2014 def add_database_user(uidname, session=None):
2016 Adds a database user
2018 @type uidname: string
2019 @param uidname: The uid of the user to add
2021 @type session: SQLAlchemy
2022 @param session: Optional SQL session object (a temporary one will be
2023 generated if not supplied). If not passed, a commit will be performed at
2024 the end of the function, otherwise the caller is responsible for commiting.
2027 @return: the uid object for the given uidname
2030 session.execute("CREATE USER :uid", {'uid': uidname})
2031 session.commit_or_flush()
2033 __all__.append('add_database_user')
2036 def get_or_set_uid(uidname, session=None):
2038 Returns uid object for given uidname.
2040 If no matching uidname is found, a row is inserted.
2042 @type uidname: string
2043 @param uidname: The uid to add
2045 @type session: SQLAlchemy
2046 @param session: Optional SQL session object (a temporary one will be
2047 generated if not supplied). If not passed, a commit will be performed at
2048 the end of the function, otherwise the caller is responsible for commiting.
2051 @return: the uid object for the given uidname
2054 q = session.query(Uid).filter_by(uid=uidname)
2058 except NoResultFound:
2062 session.commit_or_flush()
2067 __all__.append('get_or_set_uid')
2070 def get_uid_from_fingerprint(fpr, session=None):
2071 q = session.query(Uid)
2072 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2076 except NoResultFound:
2079 __all__.append('get_uid_from_fingerprint')
2081 ################################################################################
2083 class DBConn(Singleton):
2085 database module init.
2087 def __init__(self, *args, **kwargs):
2088 super(DBConn, self).__init__(*args, **kwargs)
2090 def _startup(self, *args, **kwargs):
2092 if kwargs.has_key('debug'):
2096 def __setuptables(self):
2097 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2098 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2099 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2100 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2101 self.tbl_component = Table('component', self.db_meta, autoload=True)
2102 self.tbl_config = Table('config', self.db_meta, autoload=True)
2103 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2104 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2105 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2106 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2107 self.tbl_files = Table('files', self.db_meta, autoload=True)
2108 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2109 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2110 self.tbl_location = Table('location', self.db_meta, autoload=True)
2111 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2112 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2113 self.tbl_override = Table('override', self.db_meta, autoload=True)
2114 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2115 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2116 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2117 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2118 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2119 self.tbl_section = Table('section', self.db_meta, autoload=True)
2120 self.tbl_source = Table('source', self.db_meta, autoload=True)
2121 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2122 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2123 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2124 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2125 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2126 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2127 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2129 def __setupmappers(self):
2130 mapper(Architecture, self.tbl_architecture,
2131 properties = dict(arch_id = self.tbl_architecture.c.id))
2133 mapper(Archive, self.tbl_archive,
2134 properties = dict(archive_id = self.tbl_archive.c.id,
2135 archive_name = self.tbl_archive.c.name))
2137 mapper(BinAssociation, self.tbl_bin_associations,
2138 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2139 suite_id = self.tbl_bin_associations.c.suite,
2140 suite = relation(Suite),
2141 binary_id = self.tbl_bin_associations.c.bin,
2142 binary = relation(DBBinary)))
2144 mapper(DBBinary, self.tbl_binaries,
2145 properties = dict(binary_id = self.tbl_binaries.c.id,
2146 package = self.tbl_binaries.c.package,
2147 version = self.tbl_binaries.c.version,
2148 maintainer_id = self.tbl_binaries.c.maintainer,
2149 maintainer = relation(Maintainer),
2150 source_id = self.tbl_binaries.c.source,
2151 source = relation(DBSource),
2152 arch_id = self.tbl_binaries.c.architecture,
2153 architecture = relation(Architecture),
2154 poolfile_id = self.tbl_binaries.c.file,
2155 poolfile = relation(PoolFile),
2156 binarytype = self.tbl_binaries.c.type,
2157 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2158 fingerprint = relation(Fingerprint),
2159 install_date = self.tbl_binaries.c.install_date,
2160 binassociations = relation(BinAssociation,
2161 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2163 mapper(Component, self.tbl_component,
2164 properties = dict(component_id = self.tbl_component.c.id,
2165 component_name = self.tbl_component.c.name))
2167 mapper(DBConfig, self.tbl_config,
2168 properties = dict(config_id = self.tbl_config.c.id))
2170 mapper(ContentAssociation, self.tbl_content_associations,
2171 properties = dict(ca_id = self.tbl_content_associations.c.id,
2172 filename_id = self.tbl_content_associations.c.filename,
2173 filename = relation(ContentFilename),
2174 filepath_id = self.tbl_content_associations.c.filepath,
2175 filepath = relation(ContentFilepath),
2176 binary_id = self.tbl_content_associations.c.binary_pkg,
2177 binary = relation(DBBinary)))
2180 mapper(ContentFilename, self.tbl_content_file_names,
2181 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2182 filename = self.tbl_content_file_names.c.file))
2184 mapper(ContentFilepath, self.tbl_content_file_paths,
2185 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2186 filepath = self.tbl_content_file_paths.c.path))
2188 mapper(DSCFile, self.tbl_dsc_files,
2189 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2190 source_id = self.tbl_dsc_files.c.source,
2191 source = relation(DBSource),
2192 poolfile_id = self.tbl_dsc_files.c.file,
2193 poolfile = relation(PoolFile)))
2195 mapper(PoolFile, self.tbl_files,
2196 properties = dict(file_id = self.tbl_files.c.id,
2197 filesize = self.tbl_files.c.size,
2198 location_id = self.tbl_files.c.location,
2199 location = relation(Location)))
2201 mapper(Fingerprint, self.tbl_fingerprint,
2202 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2203 uid_id = self.tbl_fingerprint.c.uid,
2204 uid = relation(Uid),
2205 keyring_id = self.tbl_fingerprint.c.keyring,
2206 keyring = relation(Keyring)))
2208 mapper(Keyring, self.tbl_keyrings,
2209 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2210 keyring_id = self.tbl_keyrings.c.id))
2212 mapper(Location, self.tbl_location,
2213 properties = dict(location_id = self.tbl_location.c.id,
2214 component_id = self.tbl_location.c.component,
2215 component = relation(Component),
2216 archive_id = self.tbl_location.c.archive,
2217 archive = relation(Archive),
2218 archive_type = self.tbl_location.c.type))
2220 mapper(Maintainer, self.tbl_maintainer,
2221 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2223 mapper(NewComment, self.tbl_new_comments,
2224 properties = dict(comment_id = self.tbl_new_comments.c.id))
2226 mapper(Override, self.tbl_override,
2227 properties = dict(suite_id = self.tbl_override.c.suite,
2228 suite = relation(Suite),
2229 component_id = self.tbl_override.c.component,
2230 component = relation(Component),
2231 priority_id = self.tbl_override.c.priority,
2232 priority = relation(Priority),
2233 section_id = self.tbl_override.c.section,
2234 section = relation(Section),
2235 overridetype_id = self.tbl_override.c.type,
2236 overridetype = relation(OverrideType)))
2238 mapper(OverrideType, self.tbl_override_type,
2239 properties = dict(overridetype = self.tbl_override_type.c.type,
2240 overridetype_id = self.tbl_override_type.c.id))
2242 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2243 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2244 filepath_id = self.tbl_pending_content_associations.c.filepath,
2245 filepath = relation(ContentFilepath),
2246 filename_id = self.tbl_pending_content_associations.c.filename,
2247 filename = relation(ContentFilename)))
2249 mapper(Priority, self.tbl_priority,
2250 properties = dict(priority_id = self.tbl_priority.c.id))
2252 mapper(Queue, self.tbl_queue,
2253 properties = dict(queue_id = self.tbl_queue.c.id))
2255 mapper(QueueBuild, self.tbl_queue_build,
2256 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2257 queue_id = self.tbl_queue_build.c.queue,
2258 queue = relation(Queue, backref='queuebuild')))
2260 mapper(Section, self.tbl_section,
2261 properties = dict(section_id = self.tbl_section.c.id))
2263 mapper(DBSource, self.tbl_source,
2264 properties = dict(source_id = self.tbl_source.c.id,
2265 version = self.tbl_source.c.version,
2266 maintainer_id = self.tbl_source.c.maintainer,
2267 maintainer = relation(Maintainer,
2268 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2269 poolfile_id = self.tbl_source.c.file,
2270 poolfile = relation(PoolFile),
2271 fingerprint_id = self.tbl_source.c.sig_fpr,
2272 fingerprint = relation(Fingerprint),
2273 changedby_id = self.tbl_source.c.changedby,
2274 changedby = relation(Maintainer,
2275 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2276 srcfiles = relation(DSCFile,
2277 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2278 srcassociations = relation(SrcAssociation,
2279 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2281 mapper(SrcAssociation, self.tbl_src_associations,
2282 properties = dict(sa_id = self.tbl_src_associations.c.id,
2283 suite_id = self.tbl_src_associations.c.suite,
2284 suite = relation(Suite),
2285 source_id = self.tbl_src_associations.c.source,
2286 source = relation(DBSource)))
2288 mapper(SrcFormat, self.tbl_src_format,
2289 properties = dict(src_format_id = self.tbl_src_format.c.id,
2290 format_name = self.tbl_src_format.c.format_name))
2292 mapper(SrcUploader, self.tbl_src_uploaders,
2293 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2294 source_id = self.tbl_src_uploaders.c.source,
2295 source = relation(DBSource,
2296 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2297 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2298 maintainer = relation(Maintainer,
2299 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2301 mapper(Suite, self.tbl_suite,
2302 properties = dict(suite_id = self.tbl_suite.c.id))
2304 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2305 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2306 suite = relation(Suite, backref='suitearchitectures'),
2307 arch_id = self.tbl_suite_architectures.c.architecture,
2308 architecture = relation(Architecture)))
2310 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2311 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2312 suite = relation(Suite, backref='suitesrcformats'),
2313 src_format_id = self.tbl_suite_src_formats.c.src_format,
2314 src_format = relation(SrcFormat)))
2316 mapper(Uid, self.tbl_uid,
2317 properties = dict(uid_id = self.tbl_uid.c.id,
2318 fingerprint = relation(Fingerprint)))
2320 ## Connection functions
2321 def __createconn(self):
2322 from config import Config
2326 connstr = "postgres://%s" % cnf["DB::Host"]
2327 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2328 connstr += ":%s" % cnf["DB::Port"]
2329 connstr += "/%s" % cnf["DB::Name"]
2332 connstr = "postgres:///%s" % cnf["DB::Name"]
2333 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2334 connstr += "?port=%s" % cnf["DB::Port"]
2336 self.db_pg = create_engine(connstr, echo=self.debug)
2337 self.db_meta = MetaData()
2338 self.db_meta.bind = self.db_pg
2339 self.db_smaker = sessionmaker(bind=self.db_pg,
2343 self.__setuptables()
2344 self.__setupmappers()
2347 return self.db_smaker()
2349 __all__.append('DBConn')