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
54 from daklib.utils import ensure_orig_files
56 ################################################################################
58 __all__ = ['IntegrityError', 'SQLAlchemyError']
60 ################################################################################
62 def session_wrapper(fn):
64 Wrapper around common ".., session=None):" handling. If the wrapped
65 function is called without passing 'session', we create a local one
66 and destroy it when the function ends.
68 Also attaches a commit_or_flush method to the session; if we created a
69 local session, this is a synonym for session.commit(), otherwise it is a
70 synonym for session.flush().
73 def wrapped(*args, **kwargs):
74 private_transaction = False
76 # Find the session object
77 session = kwargs.get('session')
80 if len(args) <= len(getargspec(fn)[0]) - 1:
81 # No session specified as last argument or in kwargs
82 private_transaction = True
83 session = kwargs['session'] = DBConn().session()
85 # Session is last argument in args
89 session = args[-1] = DBConn().session()
90 private_transaction = True
92 if private_transaction:
93 session.commit_or_flush = session.commit
95 session.commit_or_flush = session.flush
98 return fn(*args, **kwargs)
100 if private_transaction:
101 # We created a session; close it.
104 wrapped.__doc__ = fn.__doc__
105 wrapped.func_name = fn.func_name
109 ################################################################################
111 class Architecture(object):
112 def __init__(self, *args, **kwargs):
115 def __eq__(self, val):
116 if isinstance(val, str):
117 return (self.arch_string== val)
118 # This signals to use the normal comparison operator
119 return NotImplemented
121 def __ne__(self, val):
122 if isinstance(val, str):
123 return (self.arch_string != val)
124 # This signals to use the normal comparison operator
125 return NotImplemented
128 return '<Architecture %s>' % self.arch_string
130 __all__.append('Architecture')
133 def get_architecture(architecture, session=None):
135 Returns database id for given C{architecture}.
137 @type architecture: string
138 @param architecture: The name of the architecture
140 @type session: Session
141 @param session: Optional SQLA session object (a temporary one will be
142 generated if not supplied)
145 @return: Architecture object for the given arch (None if not present)
148 q = session.query(Architecture).filter_by(arch_string=architecture)
152 except NoResultFound:
155 __all__.append('get_architecture')
158 def get_architecture_suites(architecture, session=None):
160 Returns list of Suite objects for given C{architecture} name
163 @param source: Architecture name to search for
165 @type session: Session
166 @param session: Optional SQL session object (a temporary one will be
167 generated if not supplied)
170 @return: list of Suite objects for the given name (may be empty)
173 q = session.query(Suite)
174 q = q.join(SuiteArchitecture)
175 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
181 __all__.append('get_architecture_suites')
183 ################################################################################
185 class Archive(object):
186 def __init__(self, *args, **kwargs):
190 return '<Archive %s>' % self.archive_name
192 __all__.append('Archive')
195 def get_archive(archive, session=None):
197 returns database id for given C{archive}.
199 @type archive: string
200 @param archive: the name of the arhive
202 @type session: Session
203 @param session: Optional SQLA session object (a temporary one will be
204 generated if not supplied)
207 @return: Archive object for the given name (None if not present)
210 archive = archive.lower()
212 q = session.query(Archive).filter_by(archive_name=archive)
216 except NoResultFound:
219 __all__.append('get_archive')
221 ################################################################################
223 class BinAssociation(object):
224 def __init__(self, *args, **kwargs):
228 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
230 __all__.append('BinAssociation')
232 ################################################################################
234 class DBBinary(object):
235 def __init__(self, *args, **kwargs):
239 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
241 __all__.append('DBBinary')
244 def get_suites_binary_in(package, session=None):
246 Returns list of Suite objects which given C{package} name is in
249 @param source: DBBinary package name to search for
252 @return: list of Suite objects for the given package
255 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
257 __all__.append('get_suites_binary_in')
260 def get_binary_from_id(id, session=None):
262 Returns DBBinary object for given C{id}
265 @param id: Id of the required binary
267 @type session: Session
268 @param session: Optional SQLA session object (a temporary one will be
269 generated if not supplied)
272 @return: DBBinary object for the given binary (None if not present)
275 q = session.query(DBBinary).filter_by(binary_id=id)
279 except NoResultFound:
282 __all__.append('get_binary_from_id')
285 def get_binaries_from_name(package, version=None, architecture=None, session=None):
287 Returns list of DBBinary objects for given C{package} name
290 @param package: DBBinary package name to search for
292 @type version: str or None
293 @param version: Version to search for (or None)
295 @type package: str, list or None
296 @param package: Architectures to limit to (or None if no limit)
298 @type session: Session
299 @param session: Optional SQL session object (a temporary one will be
300 generated if not supplied)
303 @return: list of DBBinary objects for the given name (may be empty)
306 q = session.query(DBBinary).filter_by(package=package)
308 if version is not None:
309 q = q.filter_by(version=version)
311 if architecture is not None:
312 if not isinstance(architecture, list):
313 architecture = [architecture]
314 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
320 __all__.append('get_binaries_from_name')
323 def get_binaries_from_source_id(source_id, session=None):
325 Returns list of DBBinary objects for given C{source_id}
328 @param source_id: source_id to search for
330 @type session: Session
331 @param session: Optional SQL session object (a temporary one will be
332 generated if not supplied)
335 @return: list of DBBinary objects for the given name (may be empty)
338 return session.query(DBBinary).filter_by(source_id=source_id).all()
340 __all__.append('get_binaries_from_source_id')
343 def get_binary_from_name_suite(package, suitename, session=None):
344 ### For dak examine-package
345 ### XXX: Doesn't use object API yet
347 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
348 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
349 WHERE b.package=:package
351 AND fi.location = l.id
352 AND l.component = c.id
355 AND su.suite_name=:suitename
356 ORDER BY b.version DESC"""
358 return session.execute(sql, {'package': package, 'suitename': suitename})
360 __all__.append('get_binary_from_name_suite')
363 def get_binary_components(package, suitename, arch, session=None):
364 # Check for packages that have moved from one component to another
365 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
366 WHERE b.package=:package AND s.suite_name=:suitename
367 AND (a.arch_string = :arch OR a.arch_string = 'all')
368 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
369 AND f.location = l.id
370 AND l.component = c.id
373 vals = {'package': package, 'suitename': suitename, 'arch': arch}
375 return session.execute(query, vals)
377 __all__.append('get_binary_components')
379 ################################################################################
381 class Component(object):
382 def __init__(self, *args, **kwargs):
385 def __eq__(self, val):
386 if isinstance(val, str):
387 return (self.component_name == val)
388 # This signals to use the normal comparison operator
389 return NotImplemented
391 def __ne__(self, val):
392 if isinstance(val, str):
393 return (self.component_name != val)
394 # This signals to use the normal comparison operator
395 return NotImplemented
398 return '<Component %s>' % self.component_name
401 __all__.append('Component')
404 def get_component(component, session=None):
406 Returns database id for given C{component}.
408 @type component: string
409 @param component: The name of the override type
412 @return: the database id for the given component
415 component = component.lower()
417 q = session.query(Component).filter_by(component_name=component)
421 except NoResultFound:
424 __all__.append('get_component')
426 ################################################################################
428 class DBConfig(object):
429 def __init__(self, *args, **kwargs):
433 return '<DBConfig %s>' % self.name
435 __all__.append('DBConfig')
437 ################################################################################
439 class ContentFilename(object):
440 def __init__(self, *args, **kwargs):
444 return '<ContentFilename %s>' % self.filename
446 __all__.append('ContentFilename')
449 def get_or_set_contents_file_id(filename, session=None):
451 Returns database id for given filename.
453 If no matching file is found, a row is inserted.
455 @type filename: string
456 @param filename: The filename
457 @type session: SQLAlchemy
458 @param session: Optional SQL session object (a temporary one will be
459 generated if not supplied). If not passed, a commit will be performed at
460 the end of the function, otherwise the caller is responsible for commiting.
463 @return: the database id for the given component
466 q = session.query(ContentFilename).filter_by(filename=filename)
469 ret = q.one().cafilename_id
470 except NoResultFound:
471 cf = ContentFilename()
472 cf.filename = filename
474 session.commit_or_flush()
475 ret = cf.cafilename_id
479 __all__.append('get_or_set_contents_file_id')
482 def get_contents(suite, overridetype, section=None, session=None):
484 Returns contents for a suite / overridetype combination, limiting
485 to a section if not None.
488 @param suite: Suite object
490 @type overridetype: OverrideType
491 @param overridetype: OverrideType object
493 @type section: Section
494 @param section: Optional section object to limit results to
496 @type session: SQLAlchemy
497 @param session: Optional SQL session object (a temporary one will be
498 generated if not supplied)
501 @return: ResultsProxy object set up to return tuples of (filename, section,
505 # find me all of the contents for a given suite
506 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
510 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
511 JOIN content_file_names n ON (c.filename=n.id)
512 JOIN binaries b ON (b.id=c.binary_pkg)
513 JOIN override o ON (o.package=b.package)
514 JOIN section s ON (s.id=o.section)
515 WHERE o.suite = :suiteid AND o.type = :overridetypeid
516 AND b.type=:overridetypename"""
518 vals = {'suiteid': suite.suite_id,
519 'overridetypeid': overridetype.overridetype_id,
520 'overridetypename': overridetype.overridetype}
522 if section is not None:
523 contents_q += " AND s.id = :sectionid"
524 vals['sectionid'] = section.section_id
526 contents_q += " ORDER BY fn"
528 return session.execute(contents_q, vals)
530 __all__.append('get_contents')
532 ################################################################################
534 class ContentFilepath(object):
535 def __init__(self, *args, **kwargs):
539 return '<ContentFilepath %s>' % self.filepath
541 __all__.append('ContentFilepath')
544 def get_or_set_contents_path_id(filepath, session=None):
546 Returns database id for given path.
548 If no matching file is found, a row is inserted.
550 @type filename: string
551 @param filename: The filepath
552 @type session: SQLAlchemy
553 @param session: Optional SQL session object (a temporary one will be
554 generated if not supplied). If not passed, a commit will be performed at
555 the end of the function, otherwise the caller is responsible for commiting.
558 @return: the database id for the given path
561 q = session.query(ContentFilepath).filter_by(filepath=filepath)
564 ret = q.one().cafilepath_id
565 except NoResultFound:
566 cf = ContentFilepath()
567 cf.filepath = filepath
569 session.commit_or_flush()
570 ret = cf.cafilepath_id
574 __all__.append('get_or_set_contents_path_id')
576 ################################################################################
578 class ContentAssociation(object):
579 def __init__(self, *args, **kwargs):
583 return '<ContentAssociation %s>' % self.ca_id
585 __all__.append('ContentAssociation')
587 def insert_content_paths(binary_id, fullpaths, session=None):
589 Make sure given path is associated with given binary id
592 @param binary_id: the id of the binary
593 @type fullpaths: list
594 @param fullpaths: the list of paths of the file being associated with the binary
595 @type session: SQLAlchemy session
596 @param session: Optional SQLAlchemy session. If this is passed, the caller
597 is responsible for ensuring a transaction has begun and committing the
598 results or rolling back based on the result code. If not passed, a commit
599 will be performed at the end of the function, otherwise the caller is
600 responsible for commiting.
602 @return: True upon success
607 session = DBConn().session()
613 for fullpath in fullpaths:
614 # Get the necessary IDs ...
615 (path, file) = os.path.split(fullpath)
617 filepath_id = get_or_set_contents_path_id(path, session)
618 filename_id = get_or_set_contents_file_id(file, session)
620 pathcache[fullpath] = (filepath_id, filename_id)
622 for fullpath, dat in pathcache.items():
623 ca = ContentAssociation()
624 ca.binary_id = binary_id
625 ca.filepath_id = dat[0]
626 ca.filename_id = dat[1]
629 # Only commit if we set up the session ourself
639 traceback.print_exc()
641 # Only rollback if we set up the session ourself
648 __all__.append('insert_content_paths')
650 ################################################################################
652 class DSCFile(object):
653 def __init__(self, *args, **kwargs):
657 return '<DSCFile %s>' % self.dscfile_id
659 __all__.append('DSCFile')
662 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
664 Returns a list of DSCFiles which may be empty
666 @type dscfile_id: int (optional)
667 @param dscfile_id: the dscfile_id of the DSCFiles to find
669 @type source_id: int (optional)
670 @param source_id: the source id related to the DSCFiles to find
672 @type poolfile_id: int (optional)
673 @param poolfile_id: the poolfile id related to the DSCFiles to find
676 @return: Possibly empty list of DSCFiles
679 q = session.query(DSCFile)
681 if dscfile_id is not None:
682 q = q.filter_by(dscfile_id=dscfile_id)
684 if source_id is not None:
685 q = q.filter_by(source_id=source_id)
687 if poolfile_id is not None:
688 q = q.filter_by(poolfile_id=poolfile_id)
692 __all__.append('get_dscfiles')
694 ################################################################################
696 class PoolFile(object):
697 def __init__(self, *args, **kwargs):
701 return '<PoolFile %s>' % self.filename
703 __all__.append('PoolFile')
706 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
709 (ValidFileFound [boolean or None], PoolFile object or None)
711 @type filename: string
712 @param filename: the filename of the file to check against the DB
715 @param filesize: the size of the file to check against the DB
718 @param md5sum: the md5sum of the file to check against the DB
720 @type location_id: int
721 @param location_id: the id of the location to look in
724 @return: Tuple of length 2.
725 If more than one file found with that name:
727 If valid pool file found: (True, PoolFile object)
728 If valid pool file not found:
729 (False, None) if no file found
730 (False, PoolFile object) if file found with size/md5sum mismatch
733 q = session.query(PoolFile).filter_by(filename=filename)
734 q = q.join(Location).filter_by(location_id=location_id)
744 if obj.md5sum != md5sum or obj.filesize != filesize:
752 __all__.append('check_poolfile')
755 def get_poolfile_by_id(file_id, session=None):
757 Returns a PoolFile objects or None for the given id
760 @param file_id: the id of the file to look for
762 @rtype: PoolFile or None
763 @return: either the PoolFile object or None
766 q = session.query(PoolFile).filter_by(file_id=file_id)
770 except NoResultFound:
773 __all__.append('get_poolfile_by_id')
777 def get_poolfile_by_name(filename, location_id=None, session=None):
779 Returns an array of PoolFile objects for the given filename and
780 (optionally) location_id
782 @type filename: string
783 @param filename: the filename of the file to check against the DB
785 @type location_id: int
786 @param location_id: the id of the location to look in (optional)
789 @return: array of PoolFile objects
792 q = session.query(PoolFile).filter_by(filename=filename)
794 if location_id is not None:
795 q = q.join(Location).filter_by(location_id=location_id)
799 __all__.append('get_poolfile_by_name')
802 def get_poolfile_like_name(filename, session=None):
804 Returns an array of PoolFile objects which are like the given name
806 @type filename: string
807 @param filename: the filename of the file to check against the DB
810 @return: array of PoolFile objects
813 # TODO: There must be a way of properly using bind parameters with %FOO%
814 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
818 __all__.append('get_poolfile_like_name')
820 ################################################################################
822 class Fingerprint(object):
823 def __init__(self, *args, **kwargs):
827 return '<Fingerprint %s>' % self.fingerprint
829 __all__.append('Fingerprint')
832 def get_or_set_fingerprint(fpr, session=None):
834 Returns Fingerprint object for given fpr.
836 If no matching fpr is found, a row is inserted.
839 @param fpr: The fpr to find / add
841 @type session: SQLAlchemy
842 @param session: Optional SQL session object (a temporary one will be
843 generated if not supplied). If not passed, a commit will be performed at
844 the end of the function, otherwise the caller is responsible for commiting.
845 A flush will be performed either way.
848 @return: the Fingerprint object for the given fpr
851 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
855 except NoResultFound:
856 fingerprint = Fingerprint()
857 fingerprint.fingerprint = fpr
858 session.add(fingerprint)
859 session.commit_or_flush()
864 __all__.append('get_or_set_fingerprint')
866 ################################################################################
868 class Keyring(object):
869 def __init__(self, *args, **kwargs):
873 return '<Keyring %s>' % self.keyring_name
875 __all__.append('Keyring')
878 def get_or_set_keyring(keyring, session=None):
880 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
881 and return the new Keyring
882 If C{keyring} already has an entry, simply return the existing Keyring
884 @type keyring: string
885 @param keyring: the keyring name
888 @return: the Keyring object for this keyring
891 q = session.query(Keyring).filter_by(keyring_name=keyring)
895 except NoResultFound:
896 obj = Keyring(keyring_name=keyring)
898 session.commit_or_flush()
901 __all__.append('get_or_set_keyring')
903 ################################################################################
905 class Location(object):
906 def __init__(self, *args, **kwargs):
910 return '<Location %s (%s)>' % (self.path, self.location_id)
912 __all__.append('Location')
915 def get_location(location, component=None, archive=None, session=None):
917 Returns Location object for the given combination of location, component
920 @type location: string
921 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
923 @type component: string
924 @param component: the component name (if None, no restriction applied)
926 @type archive: string
927 @param archive_id: the archive name (if None, no restriction applied)
929 @rtype: Location / None
930 @return: Either a Location object or None if one can't be found
933 q = session.query(Location).filter_by(path=location)
935 if archive is not None:
936 q = q.join(Archive).filter_by(archive_name=archive)
938 if component is not None:
939 q = q.join(Component).filter_by(component_name=component)
943 except NoResultFound:
946 __all__.append('get_location')
948 ################################################################################
950 class Maintainer(object):
951 def __init__(self, *args, **kwargs):
955 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
957 def get_split_maintainer(self):
958 if not hasattr(self, 'name') or self.name is None:
959 return ('', '', '', '')
961 return fix_maintainer(self.name.strip())
963 __all__.append('Maintainer')
966 def get_or_set_maintainer(name, session=None):
968 Returns Maintainer object for given maintainer name.
970 If no matching maintainer name is found, a row is inserted.
973 @param name: The maintainer name to add
975 @type session: SQLAlchemy
976 @param session: Optional SQL session object (a temporary one will be
977 generated if not supplied). If not passed, a commit will be performed at
978 the end of the function, otherwise the caller is responsible for commiting.
979 A flush will be performed either way.
982 @return: the Maintainer object for the given maintainer
985 q = session.query(Maintainer).filter_by(name=name)
988 except NoResultFound:
989 maintainer = Maintainer()
990 maintainer.name = name
991 session.add(maintainer)
992 session.commit_or_flush()
997 __all__.append('get_or_set_maintainer')
1000 def get_maintainer(maintainer_id, session=None):
1002 Return the name of the maintainer behind C{maintainer_id} or None if that
1003 maintainer_id is invalid.
1005 @type maintainer_id: int
1006 @param maintainer_id: the id of the maintainer
1009 @return: the Maintainer with this C{maintainer_id}
1012 return session.query(Maintainer).get(maintainer_id)
1014 __all__.append('get_maintainer')
1016 ################################################################################
1018 class NewComment(object):
1019 def __init__(self, *args, **kwargs):
1023 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1025 __all__.append('NewComment')
1028 def has_new_comment(package, version, session=None):
1030 Returns true if the given combination of C{package}, C{version} has a comment.
1032 @type package: string
1033 @param package: name of the package
1035 @type version: string
1036 @param version: package version
1038 @type session: Session
1039 @param session: Optional SQLA session object (a temporary one will be
1040 generated if not supplied)
1046 q = session.query(NewComment)
1047 q = q.filter_by(package=package)
1048 q = q.filter_by(version=version)
1050 return bool(q.count() > 0)
1052 __all__.append('has_new_comment')
1055 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1057 Returns (possibly empty) list of NewComment objects for the given
1060 @type package: string (optional)
1061 @param package: name of the package
1063 @type version: string (optional)
1064 @param version: package version
1066 @type comment_id: int (optional)
1067 @param comment_id: An id of a comment
1069 @type session: Session
1070 @param session: Optional SQLA session object (a temporary one will be
1071 generated if not supplied)
1074 @return: A (possibly empty) list of NewComment objects will be returned
1077 q = session.query(NewComment)
1078 if package is not None: q = q.filter_by(package=package)
1079 if version is not None: q = q.filter_by(version=version)
1080 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1084 __all__.append('get_new_comments')
1086 ################################################################################
1088 class Override(object):
1089 def __init__(self, *args, **kwargs):
1093 return '<Override %s (%s)>' % (self.package, self.suite_id)
1095 __all__.append('Override')
1098 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1100 Returns Override object for the given parameters
1102 @type package: string
1103 @param package: The name of the package
1105 @type suite: string, list or None
1106 @param suite: The name of the suite (or suites if a list) to limit to. If
1107 None, don't limit. Defaults to None.
1109 @type component: string, list or None
1110 @param component: The name of the component (or components if a list) to
1111 limit to. If None, don't limit. Defaults to None.
1113 @type overridetype: string, list or None
1114 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1115 limit to. If None, don't limit. Defaults to None.
1117 @type session: Session
1118 @param session: Optional SQLA session object (a temporary one will be
1119 generated if not supplied)
1122 @return: A (possibly empty) list of Override objects will be returned
1125 q = session.query(Override)
1126 q = q.filter_by(package=package)
1128 if suite is not None:
1129 if not isinstance(suite, list): suite = [suite]
1130 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1132 if component is not None:
1133 if not isinstance(component, list): component = [component]
1134 q = q.join(Component).filter(Component.component_name.in_(component))
1136 if overridetype is not None:
1137 if not isinstance(overridetype, list): overridetype = [overridetype]
1138 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1142 __all__.append('get_override')
1145 ################################################################################
1147 class OverrideType(object):
1148 def __init__(self, *args, **kwargs):
1152 return '<OverrideType %s>' % self.overridetype
1154 __all__.append('OverrideType')
1157 def get_override_type(override_type, session=None):
1159 Returns OverrideType object for given C{override type}.
1161 @type override_type: string
1162 @param override_type: The name of the override type
1164 @type session: Session
1165 @param session: Optional SQLA session object (a temporary one will be
1166 generated if not supplied)
1169 @return: the database id for the given override type
1172 q = session.query(OverrideType).filter_by(overridetype=override_type)
1176 except NoResultFound:
1179 __all__.append('get_override_type')
1181 ################################################################################
1183 class PendingContentAssociation(object):
1184 def __init__(self, *args, **kwargs):
1188 return '<PendingContentAssociation %s>' % self.pca_id
1190 __all__.append('PendingContentAssociation')
1192 def insert_pending_content_paths(package, fullpaths, session=None):
1194 Make sure given paths are temporarily associated with given
1198 @param package: the package to associate with should have been read in from the binary control file
1199 @type fullpaths: list
1200 @param fullpaths: the list of paths of the file being associated with the binary
1201 @type session: SQLAlchemy session
1202 @param session: Optional SQLAlchemy session. If this is passed, the caller
1203 is responsible for ensuring a transaction has begun and committing the
1204 results or rolling back based on the result code. If not passed, a commit
1205 will be performed at the end of the function
1207 @return: True upon success, False if there is a problem
1210 privatetrans = False
1213 session = DBConn().session()
1217 arch = get_architecture(package['Architecture'], session)
1218 arch_id = arch.arch_id
1220 # Remove any already existing recorded files for this package
1221 q = session.query(PendingContentAssociation)
1222 q = q.filter_by(package=package['Package'])
1223 q = q.filter_by(version=package['Version'])
1224 q = q.filter_by(architecture=arch_id)
1229 for fullpath in fullpaths:
1230 (path, file) = os.path.split(fullpath)
1232 if path.startswith( "./" ):
1235 filepath_id = get_or_set_contents_path_id(path, session)
1236 filename_id = get_or_set_contents_file_id(file, session)
1238 pathcache[fullpath] = (filepath_id, filename_id)
1240 for fullpath, dat in pathcache.items():
1241 pca = PendingContentAssociation()
1242 pca.package = package['Package']
1243 pca.version = package['Version']
1244 pca.filepath_id = dat[0]
1245 pca.filename_id = dat[1]
1246 pca.architecture = arch_id
1249 # Only commit if we set up the session ourself
1257 except Exception, e:
1258 traceback.print_exc()
1260 # Only rollback if we set up the session ourself
1267 __all__.append('insert_pending_content_paths')
1269 ################################################################################
1271 class Priority(object):
1272 def __init__(self, *args, **kwargs):
1275 def __eq__(self, val):
1276 if isinstance(val, str):
1277 return (self.priority == val)
1278 # This signals to use the normal comparison operator
1279 return NotImplemented
1281 def __ne__(self, val):
1282 if isinstance(val, str):
1283 return (self.priority != val)
1284 # This signals to use the normal comparison operator
1285 return NotImplemented
1288 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1290 __all__.append('Priority')
1293 def get_priority(priority, session=None):
1295 Returns Priority object for given C{priority name}.
1297 @type priority: string
1298 @param priority: The name of the priority
1300 @type session: Session
1301 @param session: Optional SQLA session object (a temporary one will be
1302 generated if not supplied)
1305 @return: Priority object for the given priority
1308 q = session.query(Priority).filter_by(priority=priority)
1312 except NoResultFound:
1315 __all__.append('get_priority')
1318 def get_priorities(session=None):
1320 Returns dictionary of priority names -> id mappings
1322 @type session: Session
1323 @param session: Optional SQL session object (a temporary one will be
1324 generated if not supplied)
1327 @return: dictionary of priority names -> id mappings
1331 q = session.query(Priority)
1333 ret[x.priority] = x.priority_id
1337 __all__.append('get_priorities')
1339 ################################################################################
1341 class Queue(object):
1342 def __init__(self, *args, **kwargs):
1346 return '<Queue %s>' % self.queue_name
1348 def autobuild_upload(self, changes, srcpath, session=None):
1350 Update queue_build database table used for incoming autobuild support.
1352 @type changes: Changes
1353 @param changes: changes object for the upload to process
1355 @type srcpath: string
1356 @param srcpath: path for the queue file entries/link destinations
1358 @type session: SQLAlchemy session
1359 @param session: Optional SQLAlchemy session. If this is passed, the
1360 caller is responsible for ensuring a transaction has begun and
1361 committing the results or rolling back based on the result code. If
1362 not passed, a commit will be performed at the end of the function,
1363 otherwise the caller is responsible for commiting.
1365 @rtype: NoneType or string
1366 @return: None if the operation failed, a string describing the error if not
1369 privatetrans = False
1371 session = DBConn().session()
1374 # TODO: Remove by moving queue config into the database
1377 for suitename in changes.changes["distribution"].keys():
1378 # TODO: Move into database as:
1379 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1380 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1381 # This also gets rid of the SecurityQueueBuild hack below
1382 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1386 s = get_suite(suitename, session)
1388 return "INTERNAL ERROR: Could not find suite %s" % suitename
1390 # TODO: Get from database as above
1391 dest_dir = conf["Dir::QueueBuild"]
1393 # TODO: Move into database as above
1394 if conf.FindB("Dinstall::SecurityQueueBuild"):
1395 dest_dir = os.path.join(dest_dir, suitename)
1397 for file_entry in changes.files.keys():
1398 src = os.path.join(srcpath, file_entry)
1399 dest = os.path.join(dest_dir, file_entry)
1401 # TODO: Move into database as above
1402 if conf.FindB("Dinstall::SecurityQueueBuild"):
1403 # Copy it since the original won't be readable by www-data
1405 utils.copy(src, dest)
1407 # Create a symlink to it
1408 os.symlink(src, dest)
1411 qb.suite_id = s.suite_id
1412 qb.queue_id = self.queue_id
1418 exists, symlinked = ensure_orig_files(changes, dest, session)
1420 # Add symlinked files to the list of packages for later processing
1422 for filename in symlinked:
1424 qb.suite_id = s.suite_id
1425 qb.queue_id = self.queue_id
1426 qb.filename = filename
1430 # Update files to ensure they are not removed prematurely
1431 for filename in exists:
1432 qb = get_queue_build(filename, s.suite_id, session)
1444 __all__.append('Queue')
1447 def get_or_set_queue(queuename, session=None):
1449 Returns Queue object for given C{queue name}, creating it if it does not
1452 @type queuename: string
1453 @param queuename: The name of the queue
1455 @type session: Session
1456 @param session: Optional SQLA session object (a temporary one will be
1457 generated if not supplied)
1460 @return: Queue object for the given queue
1463 q = session.query(Queue).filter_by(queue_name=queuename)
1467 except NoResultFound:
1469 queue.queue_name = queuename
1471 session.commit_or_flush()
1476 __all__.append('get_or_set_queue')
1478 ################################################################################
1480 class QueueBuild(object):
1481 def __init__(self, *args, **kwargs):
1485 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1487 __all__.append('QueueBuild')
1490 def get_queue_build(filename, suite, session=None):
1492 Returns QueueBuild object for given C{filename} and C{suite}.
1494 @type filename: string
1495 @param filename: The name of the file
1497 @type suiteid: int or str
1498 @param suiteid: Suite name or ID
1500 @type session: Session
1501 @param session: Optional SQLA session object (a temporary one will be
1502 generated if not supplied)
1505 @return: Queue object for the given queue
1508 if isinstance(suite, int):
1509 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1511 q = session.query(QueueBuild).filter_by(filename=filename)
1512 q = q.join(Suite).filter_by(suite_name=suite)
1516 except NoResultFound:
1519 __all__.append('get_queue_build')
1521 ################################################################################
1523 class Section(object):
1524 def __init__(self, *args, **kwargs):
1527 def __eq__(self, val):
1528 if isinstance(val, str):
1529 return (self.section == val)
1530 # This signals to use the normal comparison operator
1531 return NotImplemented
1533 def __ne__(self, val):
1534 if isinstance(val, str):
1535 return (self.section != val)
1536 # This signals to use the normal comparison operator
1537 return NotImplemented
1540 return '<Section %s>' % self.section
1542 __all__.append('Section')
1545 def get_section(section, session=None):
1547 Returns Section object for given C{section name}.
1549 @type section: string
1550 @param section: The name of the section
1552 @type session: Session
1553 @param session: Optional SQLA session object (a temporary one will be
1554 generated if not supplied)
1557 @return: Section object for the given section name
1560 q = session.query(Section).filter_by(section=section)
1564 except NoResultFound:
1567 __all__.append('get_section')
1570 def get_sections(session=None):
1572 Returns dictionary of section names -> id mappings
1574 @type session: Session
1575 @param session: Optional SQL session object (a temporary one will be
1576 generated if not supplied)
1579 @return: dictionary of section names -> id mappings
1583 q = session.query(Section)
1585 ret[x.section] = x.section_id
1589 __all__.append('get_sections')
1591 ################################################################################
1593 class DBSource(object):
1594 def __init__(self, *args, **kwargs):
1598 return '<DBSource %s (%s)>' % (self.source, self.version)
1600 __all__.append('DBSource')
1603 def source_exists(source, source_version, suites = ["any"], session=None):
1605 Ensure that source exists somewhere in the archive for the binary
1606 upload being processed.
1607 1. exact match => 1.0-3
1608 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1610 @type package: string
1611 @param package: package source name
1613 @type source_version: string
1614 @param source_version: expected source version
1617 @param suites: list of suites to check in, default I{any}
1619 @type session: Session
1620 @param session: Optional SQLA session object (a temporary one will be
1621 generated if not supplied)
1624 @return: returns 1 if a source with expected version is found, otherwise 0
1631 for suite in suites:
1632 q = session.query(DBSource).filter_by(source=source)
1634 # source must exist in suite X, or in some other suite that's
1635 # mapped to X, recursively... silent-maps are counted too,
1636 # unreleased-maps aren't.
1637 maps = cnf.ValueList("SuiteMappings")[:]
1639 maps = [ m.split() for m in maps ]
1640 maps = [ (x[1], x[2]) for x in maps
1641 if x[0] == "map" or x[0] == "silent-map" ]
1644 if x[1] in s and x[0] not in s:
1647 q = q.join(SrcAssociation).join(Suite)
1648 q = q.filter(Suite.suite_name.in_(s))
1650 # Reduce the query results to a list of version numbers
1651 ql = [ j.version for j in q.all() ]
1654 if source_version in ql:
1658 from daklib.regexes import re_bin_only_nmu
1659 orig_source_version = re_bin_only_nmu.sub('', source_version)
1660 if orig_source_version in ql:
1663 # No source found so return not ok
1668 __all__.append('source_exists')
1671 def get_suites_source_in(source, session=None):
1673 Returns list of Suite objects which given C{source} name is in
1676 @param source: DBSource package name to search for
1679 @return: list of Suite objects for the given source
1682 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1684 __all__.append('get_suites_source_in')
1687 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1689 Returns list of DBSource objects for given C{source} name and other parameters
1692 @param source: DBSource package name to search for
1694 @type source: str or None
1695 @param source: DBSource version name to search for or None if not applicable
1697 @type dm_upload_allowed: bool
1698 @param dm_upload_allowed: If None, no effect. If True or False, only
1699 return packages with that dm_upload_allowed setting
1701 @type session: Session
1702 @param session: Optional SQL session object (a temporary one will be
1703 generated if not supplied)
1706 @return: list of DBSource objects for the given name (may be empty)
1709 q = session.query(DBSource).filter_by(source=source)
1711 if version is not None:
1712 q = q.filter_by(version=version)
1714 if dm_upload_allowed is not None:
1715 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1719 __all__.append('get_sources_from_name')
1722 def get_source_in_suite(source, suite, session=None):
1724 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1726 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1727 - B{suite} - a suite name, eg. I{unstable}
1729 @type source: string
1730 @param source: source package name
1733 @param suite: the suite name
1736 @return: the version for I{source} in I{suite}
1740 q = session.query(SrcAssociation)
1741 q = q.join('source').filter_by(source=source)
1742 q = q.join('suite').filter_by(suite_name=suite)
1745 return q.one().source
1746 except NoResultFound:
1749 __all__.append('get_source_in_suite')
1751 ################################################################################
1753 class SrcAssociation(object):
1754 def __init__(self, *args, **kwargs):
1758 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1760 __all__.append('SrcAssociation')
1762 ################################################################################
1764 class SrcFormat(object):
1765 def __init__(self, *args, **kwargs):
1769 return '<SrcFormat %s>' % (self.format_name)
1771 __all__.append('SrcFormat')
1773 ################################################################################
1775 class SrcUploader(object):
1776 def __init__(self, *args, **kwargs):
1780 return '<SrcUploader %s>' % self.uploader_id
1782 __all__.append('SrcUploader')
1784 ################################################################################
1786 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1787 ('SuiteID', 'suite_id'),
1788 ('Version', 'version'),
1789 ('Origin', 'origin'),
1791 ('Description', 'description'),
1792 ('Untouchable', 'untouchable'),
1793 ('Announce', 'announce'),
1794 ('Codename', 'codename'),
1795 ('OverrideCodename', 'overridecodename'),
1796 ('ValidTime', 'validtime'),
1797 ('Priority', 'priority'),
1798 ('NotAutomatic', 'notautomatic'),
1799 ('CopyChanges', 'copychanges'),
1800 ('CopyDotDak', 'copydotdak'),
1801 ('CommentsDir', 'commentsdir'),
1802 ('OverrideSuite', 'overridesuite'),
1803 ('ChangelogBase', 'changelogbase')]
1806 class Suite(object):
1807 def __init__(self, *args, **kwargs):
1811 return '<Suite %s>' % self.suite_name
1813 def __eq__(self, val):
1814 if isinstance(val, str):
1815 return (self.suite_name == val)
1816 # This signals to use the normal comparison operator
1817 return NotImplemented
1819 def __ne__(self, val):
1820 if isinstance(val, str):
1821 return (self.suite_name != val)
1822 # This signals to use the normal comparison operator
1823 return NotImplemented
1827 for disp, field in SUITE_FIELDS:
1828 val = getattr(self, field, None)
1830 ret.append("%s: %s" % (disp, val))
1832 return "\n".join(ret)
1834 __all__.append('Suite')
1837 def get_suite_architecture(suite, architecture, session=None):
1839 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1843 @param suite: Suite name to search for
1845 @type architecture: str
1846 @param architecture: Architecture name to search for
1848 @type session: Session
1849 @param session: Optional SQL session object (a temporary one will be
1850 generated if not supplied)
1852 @rtype: SuiteArchitecture
1853 @return: the SuiteArchitecture object or None
1856 q = session.query(SuiteArchitecture)
1857 q = q.join(Architecture).filter_by(arch_string=architecture)
1858 q = q.join(Suite).filter_by(suite_name=suite)
1862 except NoResultFound:
1865 __all__.append('get_suite_architecture')
1868 def get_suite(suite, session=None):
1870 Returns Suite object for given C{suite name}.
1873 @param suite: The name of the suite
1875 @type session: Session
1876 @param session: Optional SQLA session object (a temporary one will be
1877 generated if not supplied)
1880 @return: Suite object for the requested suite name (None if not present)
1883 q = session.query(Suite).filter_by(suite_name=suite)
1887 except NoResultFound:
1890 __all__.append('get_suite')
1892 ################################################################################
1894 class SuiteArchitecture(object):
1895 def __init__(self, *args, **kwargs):
1899 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1901 __all__.append('SuiteArchitecture')
1904 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1906 Returns list of Architecture objects for given C{suite} name
1909 @param source: Suite name to search for
1911 @type skipsrc: boolean
1912 @param skipsrc: Whether to skip returning the 'source' architecture entry
1915 @type skipall: boolean
1916 @param skipall: Whether to skip returning the 'all' architecture entry
1919 @type session: Session
1920 @param session: Optional SQL session object (a temporary one will be
1921 generated if not supplied)
1924 @return: list of Architecture objects for the given name (may be empty)
1927 q = session.query(Architecture)
1928 q = q.join(SuiteArchitecture)
1929 q = q.join(Suite).filter_by(suite_name=suite)
1932 q = q.filter(Architecture.arch_string != 'source')
1935 q = q.filter(Architecture.arch_string != 'all')
1937 q = q.order_by('arch_string')
1941 __all__.append('get_suite_architectures')
1943 ################################################################################
1945 class SuiteSrcFormat(object):
1946 def __init__(self, *args, **kwargs):
1950 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1952 __all__.append('SuiteSrcFormat')
1955 def get_suite_src_formats(suite, session=None):
1957 Returns list of allowed SrcFormat for C{suite}.
1960 @param suite: Suite name to search for
1962 @type session: Session
1963 @param session: Optional SQL session object (a temporary one will be
1964 generated if not supplied)
1967 @return: the list of allowed source formats for I{suite}
1970 q = session.query(SrcFormat)
1971 q = q.join(SuiteSrcFormat)
1972 q = q.join(Suite).filter_by(suite_name=suite)
1973 q = q.order_by('format_name')
1977 __all__.append('get_suite_src_formats')
1979 ################################################################################
1982 def __init__(self, *args, **kwargs):
1985 def __eq__(self, val):
1986 if isinstance(val, str):
1987 return (self.uid == val)
1988 # This signals to use the normal comparison operator
1989 return NotImplemented
1991 def __ne__(self, val):
1992 if isinstance(val, str):
1993 return (self.uid != val)
1994 # This signals to use the normal comparison operator
1995 return NotImplemented
1998 return '<Uid %s (%s)>' % (self.uid, self.name)
2000 __all__.append('Uid')
2003 def add_database_user(uidname, session=None):
2005 Adds a database user
2007 @type uidname: string
2008 @param uidname: The uid of the user to add
2010 @type session: SQLAlchemy
2011 @param session: Optional SQL session object (a temporary one will be
2012 generated if not supplied). If not passed, a commit will be performed at
2013 the end of the function, otherwise the caller is responsible for commiting.
2016 @return: the uid object for the given uidname
2019 session.execute("CREATE USER :uid", {'uid': uidname})
2020 session.commit_or_flush()
2022 __all__.append('add_database_user')
2025 def get_or_set_uid(uidname, session=None):
2027 Returns uid object for given uidname.
2029 If no matching uidname is found, a row is inserted.
2031 @type uidname: string
2032 @param uidname: The uid to add
2034 @type session: SQLAlchemy
2035 @param session: Optional SQL session object (a temporary one will be
2036 generated if not supplied). If not passed, a commit will be performed at
2037 the end of the function, otherwise the caller is responsible for commiting.
2040 @return: the uid object for the given uidname
2043 q = session.query(Uid).filter_by(uid=uidname)
2047 except NoResultFound:
2051 session.commit_or_flush()
2056 __all__.append('get_or_set_uid')
2059 def get_uid_from_fingerprint(fpr, session=None):
2060 q = session.query(Uid)
2061 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2065 except NoResultFound:
2068 __all__.append('get_uid_from_fingerprint')
2070 ################################################################################
2072 class DBConn(Singleton):
2074 database module init.
2076 def __init__(self, *args, **kwargs):
2077 super(DBConn, self).__init__(*args, **kwargs)
2079 def _startup(self, *args, **kwargs):
2081 if kwargs.has_key('debug'):
2085 def __setuptables(self):
2086 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2087 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2088 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2089 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2090 self.tbl_component = Table('component', self.db_meta, autoload=True)
2091 self.tbl_config = Table('config', self.db_meta, autoload=True)
2092 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2093 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2094 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2095 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2096 self.tbl_files = Table('files', self.db_meta, autoload=True)
2097 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2098 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2099 self.tbl_location = Table('location', self.db_meta, autoload=True)
2100 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2101 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2102 self.tbl_override = Table('override', self.db_meta, autoload=True)
2103 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2104 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2105 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2106 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2107 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2108 self.tbl_section = Table('section', self.db_meta, autoload=True)
2109 self.tbl_source = Table('source', self.db_meta, autoload=True)
2110 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2111 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2112 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2113 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2114 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2115 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2116 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2118 def __setupmappers(self):
2119 mapper(Architecture, self.tbl_architecture,
2120 properties = dict(arch_id = self.tbl_architecture.c.id))
2122 mapper(Archive, self.tbl_archive,
2123 properties = dict(archive_id = self.tbl_archive.c.id,
2124 archive_name = self.tbl_archive.c.name))
2126 mapper(BinAssociation, self.tbl_bin_associations,
2127 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2128 suite_id = self.tbl_bin_associations.c.suite,
2129 suite = relation(Suite),
2130 binary_id = self.tbl_bin_associations.c.bin,
2131 binary = relation(DBBinary)))
2133 mapper(DBBinary, self.tbl_binaries,
2134 properties = dict(binary_id = self.tbl_binaries.c.id,
2135 package = self.tbl_binaries.c.package,
2136 version = self.tbl_binaries.c.version,
2137 maintainer_id = self.tbl_binaries.c.maintainer,
2138 maintainer = relation(Maintainer),
2139 source_id = self.tbl_binaries.c.source,
2140 source = relation(DBSource),
2141 arch_id = self.tbl_binaries.c.architecture,
2142 architecture = relation(Architecture),
2143 poolfile_id = self.tbl_binaries.c.file,
2144 poolfile = relation(PoolFile),
2145 binarytype = self.tbl_binaries.c.type,
2146 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2147 fingerprint = relation(Fingerprint),
2148 install_date = self.tbl_binaries.c.install_date,
2149 binassociations = relation(BinAssociation,
2150 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2152 mapper(Component, self.tbl_component,
2153 properties = dict(component_id = self.tbl_component.c.id,
2154 component_name = self.tbl_component.c.name))
2156 mapper(DBConfig, self.tbl_config,
2157 properties = dict(config_id = self.tbl_config.c.id))
2159 mapper(ContentAssociation, self.tbl_content_associations,
2160 properties = dict(ca_id = self.tbl_content_associations.c.id,
2161 filename_id = self.tbl_content_associations.c.filename,
2162 filename = relation(ContentFilename),
2163 filepath_id = self.tbl_content_associations.c.filepath,
2164 filepath = relation(ContentFilepath),
2165 binary_id = self.tbl_content_associations.c.binary_pkg,
2166 binary = relation(DBBinary)))
2169 mapper(ContentFilename, self.tbl_content_file_names,
2170 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2171 filename = self.tbl_content_file_names.c.file))
2173 mapper(ContentFilepath, self.tbl_content_file_paths,
2174 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2175 filepath = self.tbl_content_file_paths.c.path))
2177 mapper(DSCFile, self.tbl_dsc_files,
2178 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2179 source_id = self.tbl_dsc_files.c.source,
2180 source = relation(DBSource),
2181 poolfile_id = self.tbl_dsc_files.c.file,
2182 poolfile = relation(PoolFile)))
2184 mapper(PoolFile, self.tbl_files,
2185 properties = dict(file_id = self.tbl_files.c.id,
2186 filesize = self.tbl_files.c.size,
2187 location_id = self.tbl_files.c.location,
2188 location = relation(Location)))
2190 mapper(Fingerprint, self.tbl_fingerprint,
2191 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2192 uid_id = self.tbl_fingerprint.c.uid,
2193 uid = relation(Uid),
2194 keyring_id = self.tbl_fingerprint.c.keyring,
2195 keyring = relation(Keyring)))
2197 mapper(Keyring, self.tbl_keyrings,
2198 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2199 keyring_id = self.tbl_keyrings.c.id))
2201 mapper(Location, self.tbl_location,
2202 properties = dict(location_id = self.tbl_location.c.id,
2203 component_id = self.tbl_location.c.component,
2204 component = relation(Component),
2205 archive_id = self.tbl_location.c.archive,
2206 archive = relation(Archive),
2207 archive_type = self.tbl_location.c.type))
2209 mapper(Maintainer, self.tbl_maintainer,
2210 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2212 mapper(NewComment, self.tbl_new_comments,
2213 properties = dict(comment_id = self.tbl_new_comments.c.id))
2215 mapper(Override, self.tbl_override,
2216 properties = dict(suite_id = self.tbl_override.c.suite,
2217 suite = relation(Suite),
2218 component_id = self.tbl_override.c.component,
2219 component = relation(Component),
2220 priority_id = self.tbl_override.c.priority,
2221 priority = relation(Priority),
2222 section_id = self.tbl_override.c.section,
2223 section = relation(Section),
2224 overridetype_id = self.tbl_override.c.type,
2225 overridetype = relation(OverrideType)))
2227 mapper(OverrideType, self.tbl_override_type,
2228 properties = dict(overridetype = self.tbl_override_type.c.type,
2229 overridetype_id = self.tbl_override_type.c.id))
2231 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2232 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2233 filepath_id = self.tbl_pending_content_associations.c.filepath,
2234 filepath = relation(ContentFilepath),
2235 filename_id = self.tbl_pending_content_associations.c.filename,
2236 filename = relation(ContentFilename)))
2238 mapper(Priority, self.tbl_priority,
2239 properties = dict(priority_id = self.tbl_priority.c.id))
2241 mapper(Queue, self.tbl_queue,
2242 properties = dict(queue_id = self.tbl_queue.c.id))
2244 mapper(QueueBuild, self.tbl_queue_build,
2245 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2246 queue_id = self.tbl_queue_build.c.queue,
2247 queue = relation(Queue, backref='queuebuild')))
2249 mapper(Section, self.tbl_section,
2250 properties = dict(section_id = self.tbl_section.c.id))
2252 mapper(DBSource, self.tbl_source,
2253 properties = dict(source_id = self.tbl_source.c.id,
2254 version = self.tbl_source.c.version,
2255 maintainer_id = self.tbl_source.c.maintainer,
2256 maintainer = relation(Maintainer,
2257 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2258 poolfile_id = self.tbl_source.c.file,
2259 poolfile = relation(PoolFile),
2260 fingerprint_id = self.tbl_source.c.sig_fpr,
2261 fingerprint = relation(Fingerprint),
2262 changedby_id = self.tbl_source.c.changedby,
2263 changedby = relation(Maintainer,
2264 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2265 srcfiles = relation(DSCFile,
2266 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2267 srcassociations = relation(SrcAssociation,
2268 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2270 mapper(SrcAssociation, self.tbl_src_associations,
2271 properties = dict(sa_id = self.tbl_src_associations.c.id,
2272 suite_id = self.tbl_src_associations.c.suite,
2273 suite = relation(Suite),
2274 source_id = self.tbl_src_associations.c.source,
2275 source = relation(DBSource)))
2277 mapper(SrcFormat, self.tbl_src_format,
2278 properties = dict(src_format_id = self.tbl_src_format.c.id,
2279 format_name = self.tbl_src_format.c.format_name))
2281 mapper(SrcUploader, self.tbl_src_uploaders,
2282 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2283 source_id = self.tbl_src_uploaders.c.source,
2284 source = relation(DBSource,
2285 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2286 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2287 maintainer = relation(Maintainer,
2288 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2290 mapper(Suite, self.tbl_suite,
2291 properties = dict(suite_id = self.tbl_suite.c.id))
2293 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2294 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2295 suite = relation(Suite, backref='suitearchitectures'),
2296 arch_id = self.tbl_suite_architectures.c.architecture,
2297 architecture = relation(Architecture)))
2299 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2300 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2301 suite = relation(Suite, backref='suitesrcformats'),
2302 src_format_id = self.tbl_suite_src_formats.c.src_format,
2303 src_format = relation(SrcFormat)))
2305 mapper(Uid, self.tbl_uid,
2306 properties = dict(uid_id = self.tbl_uid.c.id,
2307 fingerprint = relation(Fingerprint)))
2309 ## Connection functions
2310 def __createconn(self):
2311 from config import Config
2315 connstr = "postgres://%s" % cnf["DB::Host"]
2316 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2317 connstr += ":%s" % cnf["DB::Port"]
2318 connstr += "/%s" % cnf["DB::Name"]
2321 connstr = "postgres:///%s" % cnf["DB::Name"]
2322 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2323 connstr += "?port=%s" % cnf["DB::Port"]
2325 self.db_pg = create_engine(connstr, echo=self.debug)
2326 self.db_meta = MetaData()
2327 self.db_meta.bind = self.db_pg
2328 self.db_smaker = sessionmaker(bind=self.db_pg,
2332 self.__setuptables()
2333 self.__setupmappers()
2336 return self.db_smaker()
2338 __all__.append('DBConn')