5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from inspect import getargspec
42 from sqlalchemy import create_engine, Table, MetaData
43 from sqlalchemy.orm import sessionmaker, mapper, relation
45 # Don't remove this, we re-export the exceptions to scripts which import us
46 from sqlalchemy.exc import *
47 from sqlalchemy.orm.exc import NoResultFound
49 # Only import Config until Queue stuff is changed to store its config
51 from config import Config
52 from singleton import Singleton
53 from textutils import fix_maintainer
55 ################################################################################
57 __all__ = ['IntegrityError', 'SQLAlchemyError']
59 ################################################################################
61 def session_wrapper(fn):
63 Wrapper around common ".., session=None):" handling. If the wrapped
64 function is called without passing 'session', we create a local one
65 and destroy it when the function ends.
67 Also attaches a commit_or_flush method to the session; if we created a
68 local session, this is a synonym for session.commit(), otherwise it is a
69 synonym for session.flush().
72 def wrapped(*args, **kwargs):
73 private_transaction = False
75 # Find the session object
76 session = kwargs.get('session')
79 if len(args) <= len(getargspec(fn)[0]) - 1:
80 # No session specified as last argument or in kwargs
81 private_transaction = True
82 session = kwargs['session'] = DBConn().session()
84 # Session is last argument in args
88 session = args[-1] = DBConn().session()
89 private_transaction = True
91 if private_transaction:
92 session.commit_or_flush = session.commit
94 session.commit_or_flush = session.flush
97 return fn(*args, **kwargs)
99 if private_transaction:
100 # We created a session; close it.
103 wrapped.__doc__ = fn.__doc__
104 wrapped.func_name = fn.func_name
108 ################################################################################
110 class Architecture(object):
111 def __init__(self, *args, **kwargs):
114 def __eq__(self, val):
115 if isinstance(val, str):
116 return (self.arch_string== val)
117 # This signals to use the normal comparison operator
118 return NotImplemented
120 def __ne__(self, val):
121 if isinstance(val, str):
122 return (self.arch_string != val)
123 # This signals to use the normal comparison operator
124 return NotImplemented
127 return '<Architecture %s>' % self.arch_string
129 __all__.append('Architecture')
132 def get_architecture(architecture, session=None):
134 Returns database id for given C{architecture}.
136 @type architecture: string
137 @param architecture: The name of the architecture
139 @type session: Session
140 @param session: Optional SQLA session object (a temporary one will be
141 generated if not supplied)
144 @return: Architecture object for the given arch (None if not present)
147 q = session.query(Architecture).filter_by(arch_string=architecture)
151 except NoResultFound:
154 __all__.append('get_architecture')
157 def get_architecture_suites(architecture, session=None):
159 Returns list of Suite objects for given C{architecture} name
162 @param source: Architecture name to search for
164 @type session: Session
165 @param session: Optional SQL session object (a temporary one will be
166 generated if not supplied)
169 @return: list of Suite objects for the given name (may be empty)
172 q = session.query(Suite)
173 q = q.join(SuiteArchitecture)
174 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
180 __all__.append('get_architecture_suites')
182 ################################################################################
184 class Archive(object):
185 def __init__(self, *args, **kwargs):
189 return '<Archive %s>' % self.archive_name
191 __all__.append('Archive')
194 def get_archive(archive, session=None):
196 returns database id for given C{archive}.
198 @type archive: string
199 @param archive: the name of the arhive
201 @type session: Session
202 @param session: Optional SQLA session object (a temporary one will be
203 generated if not supplied)
206 @return: Archive object for the given name (None if not present)
209 archive = archive.lower()
211 q = session.query(Archive).filter_by(archive_name=archive)
215 except NoResultFound:
218 __all__.append('get_archive')
220 ################################################################################
222 class BinAssociation(object):
223 def __init__(self, *args, **kwargs):
227 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
229 __all__.append('BinAssociation')
231 ################################################################################
233 class BinContents(object):
234 def __init__(self, *args, **kwargs):
238 return '<BinContents (%s, %s)>' % (self.binary, self.filename)
240 __all__.append('BinContents')
242 ################################################################################
244 class DBBinary(object):
245 def __init__(self, *args, **kwargs):
249 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
251 __all__.append('DBBinary')
254 def get_suites_binary_in(package, session=None):
256 Returns list of Suite objects which given C{package} name is in
259 @param source: DBBinary package name to search for
262 @return: list of Suite objects for the given package
265 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
267 __all__.append('get_suites_binary_in')
270 def get_binary_from_id(id, session=None):
272 Returns DBBinary object for given C{id}
275 @param id: Id of the required binary
277 @type session: Session
278 @param session: Optional SQLA session object (a temporary one will be
279 generated if not supplied)
282 @return: DBBinary object for the given binary (None if not present)
285 q = session.query(DBBinary).filter_by(binary_id=id)
289 except NoResultFound:
292 __all__.append('get_binary_from_id')
295 def get_binaries_from_name(package, version=None, architecture=None, session=None):
297 Returns list of DBBinary objects for given C{package} name
300 @param package: DBBinary package name to search for
302 @type version: str or None
303 @param version: Version to search for (or None)
305 @type package: str, list or None
306 @param package: Architectures to limit to (or None if no limit)
308 @type session: Session
309 @param session: Optional SQL session object (a temporary one will be
310 generated if not supplied)
313 @return: list of DBBinary objects for the given name (may be empty)
316 q = session.query(DBBinary).filter_by(package=package)
318 if version is not None:
319 q = q.filter_by(version=version)
321 if architecture is not None:
322 if not isinstance(architecture, list):
323 architecture = [architecture]
324 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
330 __all__.append('get_binaries_from_name')
333 def get_binaries_from_source_id(source_id, session=None):
335 Returns list of DBBinary objects for given C{source_id}
338 @param source_id: source_id to search for
340 @type session: Session
341 @param session: Optional SQL session object (a temporary one will be
342 generated if not supplied)
345 @return: list of DBBinary objects for the given name (may be empty)
348 return session.query(DBBinary).filter_by(source_id=source_id).all()
350 __all__.append('get_binaries_from_source_id')
353 def get_binary_from_name_suite(package, suitename, session=None):
354 ### For dak examine-package
355 ### XXX: Doesn't use object API yet
357 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
358 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
359 WHERE b.package=:package
361 AND fi.location = l.id
362 AND l.component = c.id
365 AND su.suite_name=:suitename
366 ORDER BY b.version DESC"""
368 return session.execute(sql, {'package': package, 'suitename': suitename})
370 __all__.append('get_binary_from_name_suite')
373 def get_binary_components(package, suitename, arch, session=None):
374 # Check for packages that have moved from one component to another
375 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
376 WHERE b.package=:package AND s.suite_name=:suitename
377 AND (a.arch_string = :arch OR a.arch_string = 'all')
378 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
379 AND f.location = l.id
380 AND l.component = c.id
383 vals = {'package': package, 'suitename': suitename, 'arch': arch}
385 return session.execute(query, vals)
387 __all__.append('get_binary_components')
389 ################################################################################
391 class Component(object):
392 def __init__(self, *args, **kwargs):
395 def __eq__(self, val):
396 if isinstance(val, str):
397 return (self.component_name == val)
398 # This signals to use the normal comparison operator
399 return NotImplemented
401 def __ne__(self, val):
402 if isinstance(val, str):
403 return (self.component_name != val)
404 # This signals to use the normal comparison operator
405 return NotImplemented
408 return '<Component %s>' % self.component_name
411 __all__.append('Component')
414 def get_component(component, session=None):
416 Returns database id for given C{component}.
418 @type component: string
419 @param component: The name of the override type
422 @return: the database id for the given component
425 component = component.lower()
427 q = session.query(Component).filter_by(component_name=component)
431 except NoResultFound:
434 __all__.append('get_component')
436 ################################################################################
438 class DBConfig(object):
439 def __init__(self, *args, **kwargs):
443 return '<DBConfig %s>' % self.name
445 __all__.append('DBConfig')
447 ################################################################################
450 def get_or_set_contents_file_id(filename, session=None):
452 Returns database id for given filename.
454 If no matching file is found, a row is inserted.
456 @type filename: string
457 @param filename: The filename
458 @type session: SQLAlchemy
459 @param session: Optional SQL session object (a temporary one will be
460 generated if not supplied). If not passed, a commit will be performed at
461 the end of the function, otherwise the caller is responsible for commiting.
464 @return: the database id for the given component
467 q = session.query(ContentFilename).filter_by(filename=filename)
470 ret = q.one().cafilename_id
471 except NoResultFound:
472 cf = ContentFilename()
473 cf.filename = filename
475 session.commit_or_flush()
476 ret = cf.cafilename_id
480 __all__.append('get_or_set_contents_file_id')
483 def get_contents(suite, overridetype, section=None, session=None):
485 Returns contents for a suite / overridetype combination, limiting
486 to a section if not None.
489 @param suite: Suite object
491 @type overridetype: OverrideType
492 @param overridetype: OverrideType object
494 @type section: Section
495 @param section: Optional section object to limit results to
497 @type session: SQLAlchemy
498 @param session: Optional SQL session object (a temporary one will be
499 generated if not supplied)
502 @return: ResultsProxy object set up to return tuples of (filename, section,
506 # find me all of the contents for a given suite
507 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
511 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
512 JOIN content_file_names n ON (c.filename=n.id)
513 JOIN binaries b ON (b.id=c.binary_pkg)
514 JOIN override o ON (o.package=b.package)
515 JOIN section s ON (s.id=o.section)
516 WHERE o.suite = :suiteid AND o.type = :overridetypeid
517 AND b.type=:overridetypename"""
519 vals = {'suiteid': suite.suite_id,
520 'overridetypeid': overridetype.overridetype_id,
521 'overridetypename': overridetype.overridetype}
523 if section is not None:
524 contents_q += " AND s.id = :sectionid"
525 vals['sectionid'] = section.section_id
527 contents_q += " ORDER BY fn"
529 return session.execute(contents_q, vals)
531 __all__.append('get_contents')
533 ################################################################################
535 class ContentFilepath(object):
536 def __init__(self, *args, **kwargs):
540 return '<ContentFilepath %s>' % self.filepath
542 __all__.append('ContentFilepath')
545 def get_or_set_contents_path_id(filepath, session=None):
547 Returns database id for given path.
549 If no matching file is found, a row is inserted.
551 @type filename: string
552 @param filename: The filepath
553 @type session: SQLAlchemy
554 @param session: Optional SQL session object (a temporary one will be
555 generated if not supplied). If not passed, a commit will be performed at
556 the end of the function, otherwise the caller is responsible for commiting.
559 @return: the database id for the given path
562 q = session.query(ContentFilepath).filter_by(filepath=filepath)
565 ret = q.one().cafilepath_id
566 except NoResultFound:
567 cf = ContentFilepath()
568 cf.filepath = filepath
570 session.commit_or_flush()
571 ret = cf.cafilepath_id
575 __all__.append('get_or_set_contents_path_id')
577 ################################################################################
579 class ContentAssociation(object):
580 def __init__(self, *args, **kwargs):
584 return '<ContentAssociation %s>' % self.ca_id
586 __all__.append('ContentAssociation')
588 def insert_content_paths(binary_id, fullpaths, session=None):
590 Make sure given path is associated with given binary id
593 @param binary_id: the id of the binary
594 @type fullpaths: list
595 @param fullpaths: the list of paths of the file being associated with the binary
596 @type session: SQLAlchemy session
597 @param session: Optional SQLAlchemy session. If this is passed, the caller
598 is responsible for ensuring a transaction has begun and committing the
599 results or rolling back based on the result code. If not passed, a commit
600 will be performed at the end of the function, otherwise the caller is
601 responsible for commiting.
603 @return: True upon success
608 session = DBConn().session()
614 for fullpath in fullpaths:
615 if fullpath.startswith( './' ):
616 fullpath = fullpath[2:]
618 session.execute( "INSERT INTO bin_contents ( file, binary_id ) VALUES ( :filename, :id )", { 'filename': fullpath, 'id': binary_id} )
626 traceback.print_exc()
628 # Only rollback if we set up the session ourself
635 __all__.append('insert_content_paths')
637 ################################################################################
639 class DSCFile(object):
640 def __init__(self, *args, **kwargs):
644 return '<DSCFile %s>' % self.dscfile_id
646 __all__.append('DSCFile')
649 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
651 Returns a list of DSCFiles which may be empty
653 @type dscfile_id: int (optional)
654 @param dscfile_id: the dscfile_id of the DSCFiles to find
656 @type source_id: int (optional)
657 @param source_id: the source id related to the DSCFiles to find
659 @type poolfile_id: int (optional)
660 @param poolfile_id: the poolfile id related to the DSCFiles to find
663 @return: Possibly empty list of DSCFiles
666 q = session.query(DSCFile)
668 if dscfile_id is not None:
669 q = q.filter_by(dscfile_id=dscfile_id)
671 if source_id is not None:
672 q = q.filter_by(source_id=source_id)
674 if poolfile_id is not None:
675 q = q.filter_by(poolfile_id=poolfile_id)
679 __all__.append('get_dscfiles')
681 ################################################################################
683 class PoolFile(object):
684 def __init__(self, *args, **kwargs):
688 return '<PoolFile %s>' % self.filename
690 __all__.append('PoolFile')
693 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
696 (ValidFileFound [boolean or None], PoolFile object or None)
698 @type filename: string
699 @param filename: the filename of the file to check against the DB
702 @param filesize: the size of the file to check against the DB
705 @param md5sum: the md5sum of the file to check against the DB
707 @type location_id: int
708 @param location_id: the id of the location to look in
711 @return: Tuple of length 2.
712 If more than one file found with that name:
714 If valid pool file found: (True, PoolFile object)
715 If valid pool file not found:
716 (False, None) if no file found
717 (False, PoolFile object) if file found with size/md5sum mismatch
720 q = session.query(PoolFile).filter_by(filename=filename)
721 q = q.join(Location).filter_by(location_id=location_id)
731 if obj.md5sum != md5sum or obj.filesize != filesize:
739 __all__.append('check_poolfile')
742 def get_poolfile_by_id(file_id, session=None):
744 Returns a PoolFile objects or None for the given id
747 @param file_id: the id of the file to look for
749 @rtype: PoolFile or None
750 @return: either the PoolFile object or None
753 q = session.query(PoolFile).filter_by(file_id=file_id)
757 except NoResultFound:
760 __all__.append('get_poolfile_by_id')
764 def get_poolfile_by_name(filename, location_id=None, session=None):
766 Returns an array of PoolFile objects for the given filename and
767 (optionally) location_id
769 @type filename: string
770 @param filename: the filename of the file to check against the DB
772 @type location_id: int
773 @param location_id: the id of the location to look in (optional)
776 @return: array of PoolFile objects
779 q = session.query(PoolFile).filter_by(filename=filename)
781 if location_id is not None:
782 q = q.join(Location).filter_by(location_id=location_id)
786 __all__.append('get_poolfile_by_name')
789 def get_poolfile_like_name(filename, session=None):
791 Returns an array of PoolFile objects which are like the given name
793 @type filename: string
794 @param filename: the filename of the file to check against the DB
797 @return: array of PoolFile objects
800 # TODO: There must be a way of properly using bind parameters with %FOO%
801 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
805 __all__.append('get_poolfile_like_name')
807 ################################################################################
809 class Fingerprint(object):
810 def __init__(self, *args, **kwargs):
814 return '<Fingerprint %s>' % self.fingerprint
816 __all__.append('Fingerprint')
819 def get_or_set_fingerprint(fpr, session=None):
821 Returns Fingerprint object for given fpr.
823 If no matching fpr is found, a row is inserted.
826 @param fpr: The fpr to find / add
828 @type session: SQLAlchemy
829 @param session: Optional SQL session object (a temporary one will be
830 generated if not supplied). If not passed, a commit will be performed at
831 the end of the function, otherwise the caller is responsible for commiting.
832 A flush will be performed either way.
835 @return: the Fingerprint object for the given fpr
838 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
842 except NoResultFound:
843 fingerprint = Fingerprint()
844 fingerprint.fingerprint = fpr
845 session.add(fingerprint)
846 session.commit_or_flush()
851 __all__.append('get_or_set_fingerprint')
853 ################################################################################
855 class Keyring(object):
856 def __init__(self, *args, **kwargs):
860 return '<Keyring %s>' % self.keyring_name
862 __all__.append('Keyring')
865 def get_or_set_keyring(keyring, session=None):
867 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
868 and return the new Keyring
869 If C{keyring} already has an entry, simply return the existing Keyring
871 @type keyring: string
872 @param keyring: the keyring name
875 @return: the Keyring object for this keyring
878 q = session.query(Keyring).filter_by(keyring_name=keyring)
882 except NoResultFound:
883 obj = Keyring(keyring_name=keyring)
885 session.commit_or_flush()
888 __all__.append('get_or_set_keyring')
890 ################################################################################
892 class Location(object):
893 def __init__(self, *args, **kwargs):
897 return '<Location %s (%s)>' % (self.path, self.location_id)
899 __all__.append('Location')
902 def get_location(location, component=None, archive=None, session=None):
904 Returns Location object for the given combination of location, component
907 @type location: string
908 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
910 @type component: string
911 @param component: the component name (if None, no restriction applied)
913 @type archive: string
914 @param archive_id: the archive name (if None, no restriction applied)
916 @rtype: Location / None
917 @return: Either a Location object or None if one can't be found
920 q = session.query(Location).filter_by(path=location)
922 if archive is not None:
923 q = q.join(Archive).filter_by(archive_name=archive)
925 if component is not None:
926 q = q.join(Component).filter_by(component_name=component)
930 except NoResultFound:
933 __all__.append('get_location')
935 ################################################################################
937 class Maintainer(object):
938 def __init__(self, *args, **kwargs):
942 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
944 def get_split_maintainer(self):
945 if not hasattr(self, 'name') or self.name is None:
946 return ('', '', '', '')
948 return fix_maintainer(self.name.strip())
950 __all__.append('Maintainer')
953 def get_or_set_maintainer(name, session=None):
955 Returns Maintainer object for given maintainer name.
957 If no matching maintainer name is found, a row is inserted.
960 @param name: The maintainer name to add
962 @type session: SQLAlchemy
963 @param session: Optional SQL session object (a temporary one will be
964 generated if not supplied). If not passed, a commit will be performed at
965 the end of the function, otherwise the caller is responsible for commiting.
966 A flush will be performed either way.
969 @return: the Maintainer object for the given maintainer
972 q = session.query(Maintainer).filter_by(name=name)
975 except NoResultFound:
976 maintainer = Maintainer()
977 maintainer.name = name
978 session.add(maintainer)
979 session.commit_or_flush()
984 __all__.append('get_or_set_maintainer')
987 def get_maintainer(maintainer_id, session=None):
989 Return the name of the maintainer behind C{maintainer_id} or None if that
990 maintainer_id is invalid.
992 @type maintainer_id: int
993 @param maintainer_id: the id of the maintainer
996 @return: the Maintainer with this C{maintainer_id}
999 return session.query(Maintainer).get(maintainer_id)
1001 __all__.append('get_maintainer')
1003 ################################################################################
1005 class NewComment(object):
1006 def __init__(self, *args, **kwargs):
1010 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1012 __all__.append('NewComment')
1015 def has_new_comment(package, version, session=None):
1017 Returns true if the given combination of C{package}, C{version} has a comment.
1019 @type package: string
1020 @param package: name of the package
1022 @type version: string
1023 @param version: package version
1025 @type session: Session
1026 @param session: Optional SQLA session object (a temporary one will be
1027 generated if not supplied)
1033 q = session.query(NewComment)
1034 q = q.filter_by(package=package)
1035 q = q.filter_by(version=version)
1037 return bool(q.count() > 0)
1039 __all__.append('has_new_comment')
1042 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1044 Returns (possibly empty) list of NewComment objects for the given
1047 @type package: string (optional)
1048 @param package: name of the package
1050 @type version: string (optional)
1051 @param version: package version
1053 @type comment_id: int (optional)
1054 @param comment_id: An id of a comment
1056 @type session: Session
1057 @param session: Optional SQLA session object (a temporary one will be
1058 generated if not supplied)
1061 @return: A (possibly empty) list of NewComment objects will be returned
1064 q = session.query(NewComment)
1065 if package is not None: q = q.filter_by(package=package)
1066 if version is not None: q = q.filter_by(version=version)
1067 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1071 __all__.append('get_new_comments')
1073 ################################################################################
1075 class Override(object):
1076 def __init__(self, *args, **kwargs):
1080 return '<Override %s (%s)>' % (self.package, self.suite_id)
1082 __all__.append('Override')
1085 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1087 Returns Override object for the given parameters
1089 @type package: string
1090 @param package: The name of the package
1092 @type suite: string, list or None
1093 @param suite: The name of the suite (or suites if a list) to limit to. If
1094 None, don't limit. Defaults to None.
1096 @type component: string, list or None
1097 @param component: The name of the component (or components if a list) to
1098 limit to. If None, don't limit. Defaults to None.
1100 @type overridetype: string, list or None
1101 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1102 limit to. If None, don't limit. Defaults to None.
1104 @type session: Session
1105 @param session: Optional SQLA session object (a temporary one will be
1106 generated if not supplied)
1109 @return: A (possibly empty) list of Override objects will be returned
1112 q = session.query(Override)
1113 q = q.filter_by(package=package)
1115 if suite is not None:
1116 if not isinstance(suite, list): suite = [suite]
1117 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1119 if component is not None:
1120 if not isinstance(component, list): component = [component]
1121 q = q.join(Component).filter(Component.component_name.in_(component))
1123 if overridetype is not None:
1124 if not isinstance(overridetype, list): overridetype = [overridetype]
1125 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1129 __all__.append('get_override')
1132 ################################################################################
1134 class OverrideType(object):
1135 def __init__(self, *args, **kwargs):
1139 return '<OverrideType %s>' % self.overridetype
1141 __all__.append('OverrideType')
1144 def get_override_type(override_type, session=None):
1146 Returns OverrideType object for given C{override type}.
1148 @type override_type: string
1149 @param override_type: The name of the override type
1151 @type session: Session
1152 @param session: Optional SQLA session object (a temporary one will be
1153 generated if not supplied)
1156 @return: the database id for the given override type
1159 q = session.query(OverrideType).filter_by(overridetype=override_type)
1163 except NoResultFound:
1166 __all__.append('get_override_type')
1168 ################################################################################
1170 class DebContents(object):
1171 def __init__(self, *args, **kwargs):
1175 return '<DebConetnts %s: %s>' % (self.package.package,self.file)
1177 __all__.append('DebContents')
1180 class UdebContents(object):
1181 def __init__(self, *args, **kwargs):
1185 return '<UdebConetnts %s: %s>' % (self.package.package,self.file)
1187 __all__.append('UdebContents')
1189 class PendingBinContents(object):
1190 def __init__(self, *args, **kwargs):
1194 return '<PendingBinContents %s>' % self.contents_id
1196 __all__.append('PendingBinContents')
1198 def insert_pending_content_paths(package,
1203 Make sure given paths are temporarily associated with given
1207 @param package: the package to associate with should have been read in from the binary control file
1208 @type fullpaths: list
1209 @param fullpaths: the list of paths of the file being associated with the binary
1210 @type session: SQLAlchemy session
1211 @param session: Optional SQLAlchemy session. If this is passed, the caller
1212 is responsible for ensuring a transaction has begun and committing the
1213 results or rolling back based on the result code. If not passed, a commit
1214 will be performed at the end of the function
1216 @return: True upon success, False if there is a problem
1219 privatetrans = False
1222 session = DBConn().session()
1226 arch = get_architecture(package['Architecture'], session)
1227 arch_id = arch.arch_id
1229 # Remove any already existing recorded files for this package
1230 q = session.query(PendingBinContents)
1231 q = q.filter_by(package=package['Package'])
1232 q = q.filter_by(version=package['Version'])
1233 q = q.filter_by(architecture=arch_id)
1236 for fullpath in fullpaths:
1238 if fullpath.startswith( "./" ):
1239 fullpath = fullpath[2:]
1241 pca = PendingBinContents()
1242 pca.package = package['Package']
1243 pca.version = package['Version']
1245 pca.architecture = arch_id
1248 pca.type = 8 # gross
1250 pca.type = 7 # also gross
1253 # Only commit if we set up the session ourself
1261 except Exception, e:
1262 traceback.print_exc()
1264 # Only rollback if we set up the session ourself
1271 __all__.append('insert_pending_content_paths')
1273 ################################################################################
1275 class Priority(object):
1276 def __init__(self, *args, **kwargs):
1279 def __eq__(self, val):
1280 if isinstance(val, str):
1281 return (self.priority == val)
1282 # This signals to use the normal comparison operator
1283 return NotImplemented
1285 def __ne__(self, val):
1286 if isinstance(val, str):
1287 return (self.priority != val)
1288 # This signals to use the normal comparison operator
1289 return NotImplemented
1292 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1294 __all__.append('Priority')
1297 def get_priority(priority, session=None):
1299 Returns Priority object for given C{priority name}.
1301 @type priority: string
1302 @param priority: The name of the priority
1304 @type session: Session
1305 @param session: Optional SQLA session object (a temporary one will be
1306 generated if not supplied)
1309 @return: Priority object for the given priority
1312 q = session.query(Priority).filter_by(priority=priority)
1316 except NoResultFound:
1319 __all__.append('get_priority')
1322 def get_priorities(session=None):
1324 Returns dictionary of priority names -> id mappings
1326 @type session: Session
1327 @param session: Optional SQL session object (a temporary one will be
1328 generated if not supplied)
1331 @return: dictionary of priority names -> id mappings
1335 q = session.query(Priority)
1337 ret[x.priority] = x.priority_id
1341 __all__.append('get_priorities')
1343 ################################################################################
1345 class Queue(object):
1346 def __init__(self, *args, **kwargs):
1350 return '<Queue %s>' % self.queue_name
1352 def autobuild_upload(self, changes, srcpath, session=None):
1354 Update queue_build database table used for incoming autobuild support.
1356 @type changes: Changes
1357 @param changes: changes object for the upload to process
1359 @type srcpath: string
1360 @param srcpath: path for the queue file entries/link destinations
1362 @type session: SQLAlchemy session
1363 @param session: Optional SQLAlchemy session. If this is passed, the
1364 caller is responsible for ensuring a transaction has begun and
1365 committing the results or rolling back based on the result code. If
1366 not passed, a commit will be performed at the end of the function,
1367 otherwise the caller is responsible for commiting.
1369 @rtype: NoneType or string
1370 @return: None if the operation failed, a string describing the error if not
1373 privatetrans = False
1375 session = DBConn().session()
1378 # TODO: Remove by moving queue config into the database
1381 for suitename in changes.changes["distribution"].keys():
1382 # TODO: Move into database as:
1383 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1384 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1385 # This also gets rid of the SecurityQueueBuild hack below
1386 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1390 s = get_suite(suitename, session)
1392 return "INTERNAL ERROR: Could not find suite %s" % suitename
1394 # TODO: Get from database as above
1395 dest_dir = conf["Dir::QueueBuild"]
1397 # TODO: Move into database as above
1398 if conf.FindB("Dinstall::SecurityQueueBuild"):
1399 dest_dir = os.path.join(dest_dir, suitename)
1401 for file_entry in changes.files.keys():
1402 src = os.path.join(srcpath, file_entry)
1403 dest = os.path.join(dest_dir, file_entry)
1405 # TODO: Move into database as above
1406 if conf.FindB("Dinstall::SecurityQueueBuild"):
1407 # Copy it since the original won't be readable by www-data
1409 utils.copy(src, dest)
1411 # Create a symlink to it
1412 os.symlink(src, dest)
1415 qb.suite_id = s.suite_id
1416 qb.queue_id = self.queue_id
1422 exists, symlinked = utils.ensure_orig_files(changes, dest, session)
1424 # Add symlinked files to the list of packages for later processing
1426 for filename in symlinked:
1428 qb.suite_id = s.suite_id
1429 qb.queue_id = self.queue_id
1430 qb.filename = filename
1434 # Update files to ensure they are not removed prematurely
1435 for filename in exists:
1436 qb = get_queue_build(filename, s.suite_id, session)
1448 __all__.append('Queue')
1451 def get_or_set_queue(queuename, session=None):
1453 Returns Queue object for given C{queue name}, creating it if it does not
1456 @type queuename: string
1457 @param queuename: The name of the queue
1459 @type session: Session
1460 @param session: Optional SQLA session object (a temporary one will be
1461 generated if not supplied)
1464 @return: Queue object for the given queue
1467 q = session.query(Queue).filter_by(queue_name=queuename)
1471 except NoResultFound:
1473 queue.queue_name = queuename
1475 session.commit_or_flush()
1480 __all__.append('get_or_set_queue')
1482 ################################################################################
1484 class QueueBuild(object):
1485 def __init__(self, *args, **kwargs):
1489 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1491 __all__.append('QueueBuild')
1494 def get_queue_build(filename, suite, session=None):
1496 Returns QueueBuild object for given C{filename} and C{suite}.
1498 @type filename: string
1499 @param filename: The name of the file
1501 @type suiteid: int or str
1502 @param suiteid: Suite name or ID
1504 @type session: Session
1505 @param session: Optional SQLA session object (a temporary one will be
1506 generated if not supplied)
1509 @return: Queue object for the given queue
1512 if isinstance(suite, int):
1513 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1515 q = session.query(QueueBuild).filter_by(filename=filename)
1516 q = q.join(Suite).filter_by(suite_name=suite)
1520 except NoResultFound:
1523 __all__.append('get_queue_build')
1525 ################################################################################
1527 class Section(object):
1528 def __init__(self, *args, **kwargs):
1531 def __eq__(self, val):
1532 if isinstance(val, str):
1533 return (self.section == val)
1534 # This signals to use the normal comparison operator
1535 return NotImplemented
1537 def __ne__(self, val):
1538 if isinstance(val, str):
1539 return (self.section != val)
1540 # This signals to use the normal comparison operator
1541 return NotImplemented
1544 return '<Section %s>' % self.section
1546 __all__.append('Section')
1549 def get_section(section, session=None):
1551 Returns Section object for given C{section name}.
1553 @type section: string
1554 @param section: The name of the section
1556 @type session: Session
1557 @param session: Optional SQLA session object (a temporary one will be
1558 generated if not supplied)
1561 @return: Section object for the given section name
1564 q = session.query(Section).filter_by(section=section)
1568 except NoResultFound:
1571 __all__.append('get_section')
1574 def get_sections(session=None):
1576 Returns dictionary of section names -> id mappings
1578 @type session: Session
1579 @param session: Optional SQL session object (a temporary one will be
1580 generated if not supplied)
1583 @return: dictionary of section names -> id mappings
1587 q = session.query(Section)
1589 ret[x.section] = x.section_id
1593 __all__.append('get_sections')
1595 ################################################################################
1597 class DBSource(object):
1598 def __init__(self, *args, **kwargs):
1602 return '<DBSource %s (%s)>' % (self.source, self.version)
1604 __all__.append('DBSource')
1607 def source_exists(source, source_version, suites = ["any"], session=None):
1609 Ensure that source exists somewhere in the archive for the binary
1610 upload being processed.
1611 1. exact match => 1.0-3
1612 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1614 @type package: string
1615 @param package: package source name
1617 @type source_version: string
1618 @param source_version: expected source version
1621 @param suites: list of suites to check in, default I{any}
1623 @type session: Session
1624 @param session: Optional SQLA session object (a temporary one will be
1625 generated if not supplied)
1628 @return: returns 1 if a source with expected version is found, otherwise 0
1635 for suite in suites:
1636 q = session.query(DBSource).filter_by(source=source)
1638 # source must exist in suite X, or in some other suite that's
1639 # mapped to X, recursively... silent-maps are counted too,
1640 # unreleased-maps aren't.
1641 maps = cnf.ValueList("SuiteMappings")[:]
1643 maps = [ m.split() for m in maps ]
1644 maps = [ (x[1], x[2]) for x in maps
1645 if x[0] == "map" or x[0] == "silent-map" ]
1648 if x[1] in s and x[0] not in s:
1651 q = q.join(SrcAssociation).join(Suite)
1652 q = q.filter(Suite.suite_name.in_(s))
1654 # Reduce the query results to a list of version numbers
1655 ql = [ j.version for j in q.all() ]
1658 if source_version in ql:
1662 from daklib.regexes import re_bin_only_nmu
1663 orig_source_version = re_bin_only_nmu.sub('', source_version)
1664 if orig_source_version in ql:
1667 # No source found so return not ok
1672 __all__.append('source_exists')
1675 def get_suites_source_in(source, session=None):
1677 Returns list of Suite objects which given C{source} name is in
1680 @param source: DBSource package name to search for
1683 @return: list of Suite objects for the given source
1686 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1688 __all__.append('get_suites_source_in')
1691 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1693 Returns list of DBSource objects for given C{source} name and other parameters
1696 @param source: DBSource package name to search for
1698 @type source: str or None
1699 @param source: DBSource version name to search for or None if not applicable
1701 @type dm_upload_allowed: bool
1702 @param dm_upload_allowed: If None, no effect. If True or False, only
1703 return packages with that dm_upload_allowed setting
1705 @type session: Session
1706 @param session: Optional SQL session object (a temporary one will be
1707 generated if not supplied)
1710 @return: list of DBSource objects for the given name (may be empty)
1713 q = session.query(DBSource).filter_by(source=source)
1715 if version is not None:
1716 q = q.filter_by(version=version)
1718 if dm_upload_allowed is not None:
1719 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1723 __all__.append('get_sources_from_name')
1726 def get_source_in_suite(source, suite, session=None):
1728 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1730 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1731 - B{suite} - a suite name, eg. I{unstable}
1733 @type source: string
1734 @param source: source package name
1737 @param suite: the suite name
1740 @return: the version for I{source} in I{suite}
1744 q = session.query(SrcAssociation)
1745 q = q.join('source').filter_by(source=source)
1746 q = q.join('suite').filter_by(suite_name=suite)
1749 return q.one().source
1750 except NoResultFound:
1753 __all__.append('get_source_in_suite')
1755 ################################################################################
1757 class SrcAssociation(object):
1758 def __init__(self, *args, **kwargs):
1762 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1764 __all__.append('SrcAssociation')
1766 ################################################################################
1768 class SrcFormat(object):
1769 def __init__(self, *args, **kwargs):
1773 return '<SrcFormat %s>' % (self.format_name)
1775 __all__.append('SrcFormat')
1777 ################################################################################
1779 class SrcUploader(object):
1780 def __init__(self, *args, **kwargs):
1784 return '<SrcUploader %s>' % self.uploader_id
1786 __all__.append('SrcUploader')
1788 ################################################################################
1790 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1791 ('SuiteID', 'suite_id'),
1792 ('Version', 'version'),
1793 ('Origin', 'origin'),
1795 ('Description', 'description'),
1796 ('Untouchable', 'untouchable'),
1797 ('Announce', 'announce'),
1798 ('Codename', 'codename'),
1799 ('OverrideCodename', 'overridecodename'),
1800 ('ValidTime', 'validtime'),
1801 ('Priority', 'priority'),
1802 ('NotAutomatic', 'notautomatic'),
1803 ('CopyChanges', 'copychanges'),
1804 ('CopyDotDak', 'copydotdak'),
1805 ('CommentsDir', 'commentsdir'),
1806 ('OverrideSuite', 'overridesuite'),
1807 ('ChangelogBase', 'changelogbase')]
1810 class Suite(object):
1811 def __init__(self, *args, **kwargs):
1815 return '<Suite %s>' % self.suite_name
1817 def __eq__(self, val):
1818 if isinstance(val, str):
1819 return (self.suite_name == val)
1820 # This signals to use the normal comparison operator
1821 return NotImplemented
1823 def __ne__(self, val):
1824 if isinstance(val, str):
1825 return (self.suite_name != val)
1826 # This signals to use the normal comparison operator
1827 return NotImplemented
1831 for disp, field in SUITE_FIELDS:
1832 val = getattr(self, field, None)
1834 ret.append("%s: %s" % (disp, val))
1836 return "\n".join(ret)
1838 __all__.append('Suite')
1841 def get_suite_architecture(suite, architecture, session=None):
1843 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1847 @param suite: Suite name to search for
1849 @type architecture: str
1850 @param architecture: Architecture name to search for
1852 @type session: Session
1853 @param session: Optional SQL session object (a temporary one will be
1854 generated if not supplied)
1856 @rtype: SuiteArchitecture
1857 @return: the SuiteArchitecture object or None
1860 q = session.query(SuiteArchitecture)
1861 q = q.join(Architecture).filter_by(arch_string=architecture)
1862 q = q.join(Suite).filter_by(suite_name=suite)
1866 except NoResultFound:
1869 __all__.append('get_suite_architecture')
1872 def get_suite(suite, session=None):
1874 Returns Suite object for given C{suite name}.
1877 @param suite: The name of the suite
1879 @type session: Session
1880 @param session: Optional SQLA session object (a temporary one will be
1881 generated if not supplied)
1884 @return: Suite object for the requested suite name (None if not present)
1887 q = session.query(Suite).filter_by(suite_name=suite)
1891 except NoResultFound:
1894 __all__.append('get_suite')
1896 ################################################################################
1898 class SuiteArchitecture(object):
1899 def __init__(self, *args, **kwargs):
1903 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1905 __all__.append('SuiteArchitecture')
1908 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1910 Returns list of Architecture objects for given C{suite} name
1913 @param source: Suite name to search for
1915 @type skipsrc: boolean
1916 @param skipsrc: Whether to skip returning the 'source' architecture entry
1919 @type skipall: boolean
1920 @param skipall: Whether to skip returning the 'all' architecture entry
1923 @type session: Session
1924 @param session: Optional SQL session object (a temporary one will be
1925 generated if not supplied)
1928 @return: list of Architecture objects for the given name (may be empty)
1931 q = session.query(Architecture)
1932 q = q.join(SuiteArchitecture)
1933 q = q.join(Suite).filter_by(suite_name=suite)
1936 q = q.filter(Architecture.arch_string != 'source')
1939 q = q.filter(Architecture.arch_string != 'all')
1941 q = q.order_by('arch_string')
1945 __all__.append('get_suite_architectures')
1947 ################################################################################
1949 class SuiteSrcFormat(object):
1950 def __init__(self, *args, **kwargs):
1954 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1956 __all__.append('SuiteSrcFormat')
1959 def get_suite_src_formats(suite, session=None):
1961 Returns list of allowed SrcFormat for C{suite}.
1964 @param suite: Suite name to search for
1966 @type session: Session
1967 @param session: Optional SQL session object (a temporary one will be
1968 generated if not supplied)
1971 @return: the list of allowed source formats for I{suite}
1974 q = session.query(SrcFormat)
1975 q = q.join(SuiteSrcFormat)
1976 q = q.join(Suite).filter_by(suite_name=suite)
1977 q = q.order_by('format_name')
1981 __all__.append('get_suite_src_formats')
1983 ################################################################################
1986 def __init__(self, *args, **kwargs):
1989 def __eq__(self, val):
1990 if isinstance(val, str):
1991 return (self.uid == val)
1992 # This signals to use the normal comparison operator
1993 return NotImplemented
1995 def __ne__(self, val):
1996 if isinstance(val, str):
1997 return (self.uid != val)
1998 # This signals to use the normal comparison operator
1999 return NotImplemented
2002 return '<Uid %s (%s)>' % (self.uid, self.name)
2004 __all__.append('Uid')
2007 def add_database_user(uidname, session=None):
2009 Adds a database user
2011 @type uidname: string
2012 @param uidname: The uid of the user to add
2014 @type session: SQLAlchemy
2015 @param session: Optional SQL session object (a temporary one will be
2016 generated if not supplied). If not passed, a commit will be performed at
2017 the end of the function, otherwise the caller is responsible for commiting.
2020 @return: the uid object for the given uidname
2023 session.execute("CREATE USER :uid", {'uid': uidname})
2024 session.commit_or_flush()
2026 __all__.append('add_database_user')
2029 def get_or_set_uid(uidname, session=None):
2031 Returns uid object for given uidname.
2033 If no matching uidname is found, a row is inserted.
2035 @type uidname: string
2036 @param uidname: The uid to add
2038 @type session: SQLAlchemy
2039 @param session: Optional SQL session object (a temporary one will be
2040 generated if not supplied). If not passed, a commit will be performed at
2041 the end of the function, otherwise the caller is responsible for commiting.
2044 @return: the uid object for the given uidname
2047 q = session.query(Uid).filter_by(uid=uidname)
2051 except NoResultFound:
2055 session.commit_or_flush()
2060 __all__.append('get_or_set_uid')
2063 def get_uid_from_fingerprint(fpr, session=None):
2064 q = session.query(Uid)
2065 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2069 except NoResultFound:
2072 __all__.append('get_uid_from_fingerprint')
2074 ################################################################################
2076 class DBConn(Singleton):
2078 database module init.
2080 def __init__(self, *args, **kwargs):
2081 super(DBConn, self).__init__(*args, **kwargs)
2083 def _startup(self, *args, **kwargs):
2085 if kwargs.has_key('debug'):
2089 def __setuptables(self):
2090 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2091 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2092 self.tbl_bin_contents = Table('bin_contents', self.db_meta, autoload=True)
2093 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2094 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2095 self.tbl_component = Table('component', self.db_meta, autoload=True)
2096 self.tbl_config = Table('config', self.db_meta, autoload=True)
2097 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2098 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2099 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2100 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2101 self.tbl_deb_contents = Table('deb_contents', self.db_meta, autoload=True)
2102 self.tbl_files = Table('files', self.db_meta, autoload=True)
2103 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2104 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2105 self.tbl_location = Table('location', self.db_meta, autoload=True)
2106 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2107 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2108 self.tbl_override = Table('override', self.db_meta, autoload=True)
2109 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2110 self.tbl_pending_bin_contents = Table('pending_bin_contents', self.db_meta, autoload=True)
2111 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2112 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2113 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2114 self.tbl_section = Table('section', self.db_meta, autoload=True)
2115 self.tbl_source = Table('source', self.db_meta, autoload=True)
2116 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2117 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2118 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2119 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2120 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2121 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2122 self.tbl_udeb_contents = Table('udeb_contents', self.db_meta, autoload=True)
2123 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2125 def __setupmappers(self):
2126 mapper(Architecture, self.tbl_architecture,
2127 properties = dict(arch_id = self.tbl_architecture.c.id))
2129 mapper(Archive, self.tbl_archive,
2130 properties = dict(archive_id = self.tbl_archive.c.id,
2131 archive_name = self.tbl_archive.c.name))
2133 mapper(BinAssociation, self.tbl_bin_associations,
2134 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2135 suite_id = self.tbl_bin_associations.c.suite,
2136 suite = relation(Suite),
2137 binary_id = self.tbl_bin_associations.c.bin,
2138 binary = relation(DBBinary)))
2140 mapper(PendingBinContents, self.tbl_pending_bin_contents,
2141 properties = dict(contents_id =self.tbl_pending_bin_contents.c.id,
2142 filename = self.tbl_pending_bin_contents.c.filename,
2143 package = self.tbl_pending_bin_contents.c.package,
2144 version = self.tbl_pending_bin_contents.c.version,
2145 arch = self.tbl_pending_bin_contents.c.arch,
2146 otype = self.tbl_pending_bin_contents.c.type))
2148 mapper(DebContents, self.tbl_deb_contents,
2149 properties = dict(binary_id=self.tbl_deb_contents.c.binary_id,
2150 package=self.tbl_deb_contents.c.package,
2151 component=self.tbl_deb_contents.c.component,
2152 arch=self.tbl_deb_contents.c.arch,
2153 section=self.tbl_deb_contents.c.section,
2154 filename=self.tbl_deb_contents.c.filename))
2156 mapper(UdebContents, self.tbl_udeb_contents,
2157 properties = dict(binary_id=self.tbl_udeb_contents.c.binary_id,
2158 package=self.tbl_udeb_contents.c.package,
2159 component=self.tbl_udeb_contents.c.component,
2160 arch=self.tbl_udeb_contents.c.arch,
2161 section=self.tbl_udeb_contents.c.section,
2162 filename=self.tbl_udeb_contents.c.filename))
2164 mapper(DBBinary, self.tbl_binaries,
2165 properties = dict(binary_id = self.tbl_binaries.c.id,
2166 package = self.tbl_binaries.c.package,
2167 version = self.tbl_binaries.c.version,
2168 maintainer_id = self.tbl_binaries.c.maintainer,
2169 maintainer = relation(Maintainer),
2170 source_id = self.tbl_binaries.c.source,
2171 source = relation(DBSource),
2172 arch_id = self.tbl_binaries.c.architecture,
2173 architecture = relation(Architecture),
2174 poolfile_id = self.tbl_binaries.c.file,
2175 poolfile = relation(PoolFile),
2176 binarytype = self.tbl_binaries.c.type,
2177 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2178 fingerprint = relation(Fingerprint),
2179 install_date = self.tbl_binaries.c.install_date,
2180 binassociations = relation(BinAssociation,
2181 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2183 mapper(Component, self.tbl_component,
2184 properties = dict(component_id = self.tbl_component.c.id,
2185 component_name = self.tbl_component.c.name))
2187 mapper(DBConfig, self.tbl_config,
2188 properties = dict(config_id = self.tbl_config.c.id))
2190 mapper(DSCFile, self.tbl_dsc_files,
2191 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2192 source_id = self.tbl_dsc_files.c.source,
2193 source = relation(DBSource),
2194 poolfile_id = self.tbl_dsc_files.c.file,
2195 poolfile = relation(PoolFile)))
2197 mapper(PoolFile, self.tbl_files,
2198 properties = dict(file_id = self.tbl_files.c.id,
2199 filesize = self.tbl_files.c.size,
2200 location_id = self.tbl_files.c.location,
2201 location = relation(Location)))
2203 mapper(Fingerprint, self.tbl_fingerprint,
2204 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2205 uid_id = self.tbl_fingerprint.c.uid,
2206 uid = relation(Uid),
2207 keyring_id = self.tbl_fingerprint.c.keyring,
2208 keyring = relation(Keyring)))
2210 mapper(Keyring, self.tbl_keyrings,
2211 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2212 keyring_id = self.tbl_keyrings.c.id))
2214 mapper(Location, self.tbl_location,
2215 properties = dict(location_id = self.tbl_location.c.id,
2216 component_id = self.tbl_location.c.component,
2217 component = relation(Component),
2218 archive_id = self.tbl_location.c.archive,
2219 archive = relation(Archive),
2220 archive_type = self.tbl_location.c.type))
2222 mapper(Maintainer, self.tbl_maintainer,
2223 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2225 mapper(NewComment, self.tbl_new_comments,
2226 properties = dict(comment_id = self.tbl_new_comments.c.id))
2228 mapper(Override, self.tbl_override,
2229 properties = dict(suite_id = self.tbl_override.c.suite,
2230 suite = relation(Suite),
2231 component_id = self.tbl_override.c.component,
2232 component = relation(Component),
2233 priority_id = self.tbl_override.c.priority,
2234 priority = relation(Priority),
2235 section_id = self.tbl_override.c.section,
2236 section = relation(Section),
2237 overridetype_id = self.tbl_override.c.type,
2238 overridetype = relation(OverrideType)))
2240 mapper(OverrideType, self.tbl_override_type,
2241 properties = dict(overridetype = self.tbl_override_type.c.type,
2242 overridetype_id = self.tbl_override_type.c.id))
2244 mapper(Priority, self.tbl_priority,
2245 properties = dict(priority_id = self.tbl_priority.c.id))
2247 mapper(Queue, self.tbl_queue,
2248 properties = dict(queue_id = self.tbl_queue.c.id))
2250 mapper(QueueBuild, self.tbl_queue_build,
2251 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2252 queue_id = self.tbl_queue_build.c.queue,
2253 queue = relation(Queue, backref='queuebuild')))
2255 mapper(Section, self.tbl_section,
2256 properties = dict(section_id = self.tbl_section.c.id,
2257 section=self.tbl_section.c.section))
2259 mapper(DBSource, self.tbl_source,
2260 properties = dict(source_id = self.tbl_source.c.id,
2261 version = self.tbl_source.c.version,
2262 maintainer_id = self.tbl_source.c.maintainer,
2263 maintainer = relation(Maintainer,
2264 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2265 poolfile_id = self.tbl_source.c.file,
2266 poolfile = relation(PoolFile),
2267 fingerprint_id = self.tbl_source.c.sig_fpr,
2268 fingerprint = relation(Fingerprint),
2269 changedby_id = self.tbl_source.c.changedby,
2270 changedby = relation(Maintainer,
2271 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2272 srcfiles = relation(DSCFile,
2273 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2274 srcassociations = relation(SrcAssociation,
2275 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2277 mapper(SrcAssociation, self.tbl_src_associations,
2278 properties = dict(sa_id = self.tbl_src_associations.c.id,
2279 suite_id = self.tbl_src_associations.c.suite,
2280 suite = relation(Suite),
2281 source_id = self.tbl_src_associations.c.source,
2282 source = relation(DBSource)))
2284 mapper(SrcFormat, self.tbl_src_format,
2285 properties = dict(src_format_id = self.tbl_src_format.c.id,
2286 format_name = self.tbl_src_format.c.format_name))
2288 mapper(SrcUploader, self.tbl_src_uploaders,
2289 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2290 source_id = self.tbl_src_uploaders.c.source,
2291 source = relation(DBSource,
2292 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2293 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2294 maintainer = relation(Maintainer,
2295 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2297 mapper(Suite, self.tbl_suite,
2298 properties = dict(suite_id = self.tbl_suite.c.id))
2300 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2301 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2302 suite = relation(Suite, backref='suitearchitectures'),
2303 arch_id = self.tbl_suite_architectures.c.architecture,
2304 architecture = relation(Architecture)))
2306 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2307 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2308 suite = relation(Suite, backref='suitesrcformats'),
2309 src_format_id = self.tbl_suite_src_formats.c.src_format,
2310 src_format = relation(SrcFormat)))
2312 mapper(Uid, self.tbl_uid,
2313 properties = dict(uid_id = self.tbl_uid.c.id,
2314 fingerprint = relation(Fingerprint)))
2316 ## Connection functions
2317 def __createconn(self):
2318 from config import Config
2322 connstr = "postgres://%s" % cnf["DB::Host"]
2323 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2324 connstr += ":%s" % cnf["DB::Port"]
2325 connstr += "/%s" % cnf["DB::Name"]
2328 connstr = "postgres:///%s" % cnf["DB::Name"]
2329 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2330 connstr += "?port=%s" % cnf["DB::Port"]
2332 self.db_pg = create_engine(connstr, echo=self.debug)
2333 self.db_meta = MetaData()
2334 self.db_meta.bind = self.db_pg
2335 self.db_smaker = sessionmaker(bind=self.db_pg,
2339 self.__setuptables()
2340 self.__setupmappers()
2343 return self.db_smaker()
2345 __all__.append('DBConn')