5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from inspect import getargspec
42 from sqlalchemy import create_engine, Table, MetaData
43 from sqlalchemy.orm import sessionmaker, mapper, relation
45 # Don't remove this, we re-export the exceptions to scripts which import us
46 from sqlalchemy.exc import *
47 from sqlalchemy.orm.exc import NoResultFound
49 # Only import Config until Queue stuff is changed to store its config
51 from config import Config
52 from singleton import Singleton
53 from textutils import fix_maintainer
55 ################################################################################
57 __all__ = ['IntegrityError', 'SQLAlchemyError']
59 ################################################################################
61 def session_wrapper(fn):
63 Wrapper around common ".., session=None):" handling. If the wrapped
64 function is called without passing 'session', we create a local one
65 and destroy it when the function ends.
67 Also attaches a commit_or_flush method to the session; if we created a
68 local session, this is a synonym for session.commit(), otherwise it is a
69 synonym for session.flush().
72 def wrapped(*args, **kwargs):
73 private_transaction = False
75 # Find the session object
76 session = kwargs.get('session')
79 if len(args) <= len(getargspec(fn)[0]) - 1:
80 # No session specified as last argument or in kwargs
81 private_transaction = True
82 session = kwargs['session'] = DBConn().session()
84 # Session is last argument in args
88 session = args[-1] = DBConn().session()
89 private_transaction = True
91 if private_transaction:
92 session.commit_or_flush = session.commit
94 session.commit_or_flush = session.flush
97 return fn(*args, **kwargs)
99 if private_transaction:
100 # We created a session; close it.
103 wrapped.__doc__ = fn.__doc__
104 wrapped.func_name = fn.func_name
108 ################################################################################
110 class Architecture(object):
111 def __init__(self, *args, **kwargs):
114 def __eq__(self, val):
115 if isinstance(val, str):
116 return (self.arch_string== val)
117 # This signals to use the normal comparison operator
118 return NotImplemented
120 def __ne__(self, val):
121 if isinstance(val, str):
122 return (self.arch_string != val)
123 # This signals to use the normal comparison operator
124 return NotImplemented
127 return '<Architecture %s>' % self.arch_string
129 __all__.append('Architecture')
132 def get_architecture(architecture, session=None):
134 Returns database id for given C{architecture}.
136 @type architecture: string
137 @param architecture: The name of the architecture
139 @type session: Session
140 @param session: Optional SQLA session object (a temporary one will be
141 generated if not supplied)
144 @return: Architecture object for the given arch (None if not present)
147 q = session.query(Architecture).filter_by(arch_string=architecture)
151 except NoResultFound:
154 __all__.append('get_architecture')
157 def get_architecture_suites(architecture, session=None):
159 Returns list of Suite objects for given C{architecture} name
162 @param source: Architecture name to search for
164 @type session: Session
165 @param session: Optional SQL session object (a temporary one will be
166 generated if not supplied)
169 @return: list of Suite objects for the given name (may be empty)
172 q = session.query(Suite)
173 q = q.join(SuiteArchitecture)
174 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
180 __all__.append('get_architecture_suites')
182 ################################################################################
184 class Archive(object):
185 def __init__(self, *args, **kwargs):
189 return '<Archive %s>' % self.archive_name
191 __all__.append('Archive')
194 def get_archive(archive, session=None):
196 returns database id for given C{archive}.
198 @type archive: string
199 @param archive: the name of the arhive
201 @type session: Session
202 @param session: Optional SQLA session object (a temporary one will be
203 generated if not supplied)
206 @return: Archive object for the given name (None if not present)
209 archive = archive.lower()
211 q = session.query(Archive).filter_by(archive_name=archive)
215 except NoResultFound:
218 __all__.append('get_archive')
220 ################################################################################
222 class BinAssociation(object):
223 def __init__(self, *args, **kwargs):
227 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
229 __all__.append('BinAssociation')
231 ################################################################################
233 class DBBinary(object):
234 def __init__(self, *args, **kwargs):
238 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
240 __all__.append('DBBinary')
243 def get_suites_binary_in(package, session=None):
245 Returns list of Suite objects which given C{package} name is in
248 @param source: DBBinary package name to search for
251 @return: list of Suite objects for the given package
254 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
256 __all__.append('get_suites_binary_in')
259 def get_binary_from_id(id, session=None):
261 Returns DBBinary object for given C{id}
264 @param id: Id of the required binary
266 @type session: Session
267 @param session: Optional SQLA session object (a temporary one will be
268 generated if not supplied)
271 @return: DBBinary object for the given binary (None if not present)
274 q = session.query(DBBinary).filter_by(binary_id=id)
278 except NoResultFound:
281 __all__.append('get_binary_from_id')
284 def get_binaries_from_name(package, version=None, architecture=None, session=None):
286 Returns list of DBBinary objects for given C{package} name
289 @param package: DBBinary package name to search for
291 @type version: str or None
292 @param version: Version to search for (or None)
294 @type package: str, list or None
295 @param package: Architectures to limit to (or None if no limit)
297 @type session: Session
298 @param session: Optional SQL session object (a temporary one will be
299 generated if not supplied)
302 @return: list of DBBinary objects for the given name (may be empty)
305 q = session.query(DBBinary).filter_by(package=package)
307 if version is not None:
308 q = q.filter_by(version=version)
310 if architecture is not None:
311 if not isinstance(architecture, list):
312 architecture = [architecture]
313 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
319 __all__.append('get_binaries_from_name')
322 def get_binaries_from_source_id(source_id, session=None):
324 Returns list of DBBinary objects for given C{source_id}
327 @param source_id: source_id to search for
329 @type session: Session
330 @param session: Optional SQL session object (a temporary one will be
331 generated if not supplied)
334 @return: list of DBBinary objects for the given name (may be empty)
337 return session.query(DBBinary).filter_by(source_id=source_id).all()
339 __all__.append('get_binaries_from_source_id')
342 def get_binary_from_name_suite(package, suitename, session=None):
343 ### For dak examine-package
344 ### XXX: Doesn't use object API yet
346 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
347 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
348 WHERE b.package=:package
350 AND fi.location = l.id
351 AND l.component = c.id
354 AND su.suite_name=:suitename
355 ORDER BY b.version DESC"""
357 return session.execute(sql, {'package': package, 'suitename': suitename})
359 __all__.append('get_binary_from_name_suite')
362 def get_binary_components(package, suitename, arch, session=None):
363 # Check for packages that have moved from one component to another
364 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
365 WHERE b.package=:package AND s.suite_name=:suitename
366 AND (a.arch_string = :arch OR a.arch_string = 'all')
367 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
368 AND f.location = l.id
369 AND l.component = c.id
372 vals = {'package': package, 'suitename': suitename, 'arch': arch}
374 return session.execute(query, vals)
376 __all__.append('get_binary_components')
378 ################################################################################
380 class Component(object):
381 def __init__(self, *args, **kwargs):
384 def __eq__(self, val):
385 if isinstance(val, str):
386 return (self.component_name == val)
387 # This signals to use the normal comparison operator
388 return NotImplemented
390 def __ne__(self, val):
391 if isinstance(val, str):
392 return (self.component_name != val)
393 # This signals to use the normal comparison operator
394 return NotImplemented
397 return '<Component %s>' % self.component_name
400 __all__.append('Component')
403 def get_component(component, session=None):
405 Returns database id for given C{component}.
407 @type component: string
408 @param component: The name of the override type
411 @return: the database id for the given component
414 component = component.lower()
416 q = session.query(Component).filter_by(component_name=component)
420 except NoResultFound:
423 __all__.append('get_component')
425 ################################################################################
427 class DBConfig(object):
428 def __init__(self, *args, **kwargs):
432 return '<DBConfig %s>' % self.name
434 __all__.append('DBConfig')
436 ################################################################################
438 class ContentFilename(object):
439 def __init__(self, *args, **kwargs):
443 return '<ContentFilename %s>' % self.filename
445 __all__.append('ContentFilename')
448 def get_or_set_contents_file_id(filename, session=None):
450 Returns database id for given filename.
452 If no matching file is found, a row is inserted.
454 @type filename: string
455 @param filename: The filename
456 @type session: SQLAlchemy
457 @param session: Optional SQL session object (a temporary one will be
458 generated if not supplied). If not passed, a commit will be performed at
459 the end of the function, otherwise the caller is responsible for commiting.
462 @return: the database id for the given component
465 q = session.query(ContentFilename).filter_by(filename=filename)
468 ret = q.one().cafilename_id
469 except NoResultFound:
470 cf = ContentFilename()
471 cf.filename = filename
473 session.commit_or_flush()
474 ret = cf.cafilename_id
478 __all__.append('get_or_set_contents_file_id')
481 def get_contents(suite, overridetype, section=None, session=None):
483 Returns contents for a suite / overridetype combination, limiting
484 to a section if not None.
487 @param suite: Suite object
489 @type overridetype: OverrideType
490 @param overridetype: OverrideType object
492 @type section: Section
493 @param section: Optional section object to limit results to
495 @type session: SQLAlchemy
496 @param session: Optional SQL session object (a temporary one will be
497 generated if not supplied)
500 @return: ResultsProxy object set up to return tuples of (filename, section,
504 # find me all of the contents for a given suite
505 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
509 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
510 JOIN content_file_names n ON (c.filename=n.id)
511 JOIN binaries b ON (b.id=c.binary_pkg)
512 JOIN override o ON (o.package=b.package)
513 JOIN section s ON (s.id=o.section)
514 WHERE o.suite = :suiteid AND o.type = :overridetypeid
515 AND b.type=:overridetypename"""
517 vals = {'suiteid': suite.suite_id,
518 'overridetypeid': overridetype.overridetype_id,
519 'overridetypename': overridetype.overridetype}
521 if section is not None:
522 contents_q += " AND s.id = :sectionid"
523 vals['sectionid'] = section.section_id
525 contents_q += " ORDER BY fn"
527 return session.execute(contents_q, vals)
529 __all__.append('get_contents')
531 ################################################################################
533 class ContentFilepath(object):
534 def __init__(self, *args, **kwargs):
538 return '<ContentFilepath %s>' % self.filepath
540 __all__.append('ContentFilepath')
543 def get_or_set_contents_path_id(filepath, session=None):
545 Returns database id for given path.
547 If no matching file is found, a row is inserted.
549 @type filename: string
550 @param filename: The filepath
551 @type session: SQLAlchemy
552 @param session: Optional SQL session object (a temporary one will be
553 generated if not supplied). If not passed, a commit will be performed at
554 the end of the function, otherwise the caller is responsible for commiting.
557 @return: the database id for the given path
560 q = session.query(ContentFilepath).filter_by(filepath=filepath)
563 ret = q.one().cafilepath_id
564 except NoResultFound:
565 cf = ContentFilepath()
566 cf.filepath = filepath
568 session.commit_or_flush()
569 ret = cf.cafilepath_id
573 __all__.append('get_or_set_contents_path_id')
575 ################################################################################
577 class ContentAssociation(object):
578 def __init__(self, *args, **kwargs):
582 return '<ContentAssociation %s>' % self.ca_id
584 __all__.append('ContentAssociation')
586 def insert_content_paths(binary_id, fullpaths, session=None):
588 Make sure given path is associated with given binary id
591 @param binary_id: the id of the binary
592 @type fullpaths: list
593 @param fullpaths: the list of paths of the file being associated with the binary
594 @type session: SQLAlchemy session
595 @param session: Optional SQLAlchemy session. If this is passed, the caller
596 is responsible for ensuring a transaction has begun and committing the
597 results or rolling back based on the result code. If not passed, a commit
598 will be performed at the end of the function, otherwise the caller is
599 responsible for commiting.
601 @return: True upon success
606 session = DBConn().session()
612 for fullpath in fullpaths:
613 # Get the necessary IDs ...
614 (path, file) = os.path.split(fullpath)
616 filepath_id = get_or_set_contents_path_id(path, session)
617 filename_id = get_or_set_contents_file_id(file, session)
619 pathcache[fullpath] = (filepath_id, filename_id)
621 for fullpath, dat in pathcache.items():
622 ca = ContentAssociation()
623 ca.binary_id = binary_id
624 ca.filepath_id = dat[0]
625 ca.filename_id = dat[1]
628 # Only commit if we set up the session ourself
638 traceback.print_exc()
640 # Only rollback if we set up the session ourself
647 __all__.append('insert_content_paths')
649 ################################################################################
651 class DSCFile(object):
652 def __init__(self, *args, **kwargs):
656 return '<DSCFile %s>' % self.dscfile_id
658 __all__.append('DSCFile')
661 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
663 Returns a list of DSCFiles which may be empty
665 @type dscfile_id: int (optional)
666 @param dscfile_id: the dscfile_id of the DSCFiles to find
668 @type source_id: int (optional)
669 @param source_id: the source id related to the DSCFiles to find
671 @type poolfile_id: int (optional)
672 @param poolfile_id: the poolfile id related to the DSCFiles to find
675 @return: Possibly empty list of DSCFiles
678 q = session.query(DSCFile)
680 if dscfile_id is not None:
681 q = q.filter_by(dscfile_id=dscfile_id)
683 if source_id is not None:
684 q = q.filter_by(source_id=source_id)
686 if poolfile_id is not None:
687 q = q.filter_by(poolfile_id=poolfile_id)
691 __all__.append('get_dscfiles')
693 ################################################################################
695 class PoolFile(object):
696 def __init__(self, *args, **kwargs):
700 return '<PoolFile %s>' % self.filename
702 __all__.append('PoolFile')
705 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
708 (ValidFileFound [boolean or None], PoolFile object or None)
710 @type filename: string
711 @param filename: the filename of the file to check against the DB
714 @param filesize: the size of the file to check against the DB
717 @param md5sum: the md5sum of the file to check against the DB
719 @type location_id: int
720 @param location_id: the id of the location to look in
723 @return: Tuple of length 2.
724 If more than one file found with that name:
726 If valid pool file found: (True, PoolFile object)
727 If valid pool file not found:
728 (False, None) if no file found
729 (False, PoolFile object) if file found with size/md5sum mismatch
732 q = session.query(PoolFile).filter_by(filename=filename)
733 q = q.join(Location).filter_by(location_id=location_id)
743 if obj.md5sum != md5sum or obj.filesize != filesize:
751 __all__.append('check_poolfile')
754 def get_poolfile_by_id(file_id, session=None):
756 Returns a PoolFile objects or None for the given id
759 @param file_id: the id of the file to look for
761 @rtype: PoolFile or None
762 @return: either the PoolFile object or None
765 q = session.query(PoolFile).filter_by(file_id=file_id)
769 except NoResultFound:
772 __all__.append('get_poolfile_by_id')
776 def get_poolfile_by_name(filename, location_id=None, session=None):
778 Returns an array of PoolFile objects for the given filename and
779 (optionally) location_id
781 @type filename: string
782 @param filename: the filename of the file to check against the DB
784 @type location_id: int
785 @param location_id: the id of the location to look in (optional)
788 @return: array of PoolFile objects
791 q = session.query(PoolFile).filter_by(filename=filename)
793 if location_id is not None:
794 q = q.join(Location).filter_by(location_id=location_id)
798 __all__.append('get_poolfile_by_name')
801 def get_poolfile_like_name(filename, session=None):
803 Returns an array of PoolFile objects which are like the given name
805 @type filename: string
806 @param filename: the filename of the file to check against the DB
809 @return: array of PoolFile objects
812 # TODO: There must be a way of properly using bind parameters with %FOO%
813 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
817 __all__.append('get_poolfile_like_name')
819 ################################################################################
821 class Fingerprint(object):
822 def __init__(self, *args, **kwargs):
826 return '<Fingerprint %s>' % self.fingerprint
828 __all__.append('Fingerprint')
831 def get_or_set_fingerprint(fpr, session=None):
833 Returns Fingerprint object for given fpr.
835 If no matching fpr is found, a row is inserted.
838 @param fpr: The fpr to find / add
840 @type session: SQLAlchemy
841 @param session: Optional SQL session object (a temporary one will be
842 generated if not supplied). If not passed, a commit will be performed at
843 the end of the function, otherwise the caller is responsible for commiting.
844 A flush will be performed either way.
847 @return: the Fingerprint object for the given fpr
850 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
854 except NoResultFound:
855 fingerprint = Fingerprint()
856 fingerprint.fingerprint = fpr
857 session.add(fingerprint)
858 session.commit_or_flush()
863 __all__.append('get_or_set_fingerprint')
865 ################################################################################
867 class Keyring(object):
868 def __init__(self, *args, **kwargs):
872 return '<Keyring %s>' % self.keyring_name
874 __all__.append('Keyring')
877 def get_or_set_keyring(keyring, session=None):
879 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
880 and return the new Keyring
881 If C{keyring} already has an entry, simply return the existing Keyring
883 @type keyring: string
884 @param keyring: the keyring name
887 @return: the Keyring object for this keyring
890 q = session.query(Keyring).filter_by(keyring_name=keyring)
894 except NoResultFound:
895 obj = Keyring(keyring_name=keyring)
897 session.commit_or_flush()
900 __all__.append('get_or_set_keyring')
902 ################################################################################
904 class Location(object):
905 def __init__(self, *args, **kwargs):
909 return '<Location %s (%s)>' % (self.path, self.location_id)
911 __all__.append('Location')
914 def get_location(location, component=None, archive=None, session=None):
916 Returns Location object for the given combination of location, component
919 @type location: string
920 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
922 @type component: string
923 @param component: the component name (if None, no restriction applied)
925 @type archive: string
926 @param archive_id: the archive name (if None, no restriction applied)
928 @rtype: Location / None
929 @return: Either a Location object or None if one can't be found
932 q = session.query(Location).filter_by(path=location)
934 if archive is not None:
935 q = q.join(Archive).filter_by(archive_name=archive)
937 if component is not None:
938 q = q.join(Component).filter_by(component_name=component)
942 except NoResultFound:
945 __all__.append('get_location')
947 ################################################################################
949 class Maintainer(object):
950 def __init__(self, *args, **kwargs):
954 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
956 def get_split_maintainer(self):
957 if not hasattr(self, 'name') or self.name is None:
958 return ('', '', '', '')
960 return fix_maintainer(self.name.strip())
962 __all__.append('Maintainer')
965 def get_or_set_maintainer(name, session=None):
967 Returns Maintainer object for given maintainer name.
969 If no matching maintainer name is found, a row is inserted.
972 @param name: The maintainer name to add
974 @type session: SQLAlchemy
975 @param session: Optional SQL session object (a temporary one will be
976 generated if not supplied). If not passed, a commit will be performed at
977 the end of the function, otherwise the caller is responsible for commiting.
978 A flush will be performed either way.
981 @return: the Maintainer object for the given maintainer
984 q = session.query(Maintainer).filter_by(name=name)
987 except NoResultFound:
988 maintainer = Maintainer()
989 maintainer.name = name
990 session.add(maintainer)
991 session.commit_or_flush()
996 __all__.append('get_or_set_maintainer')
999 def get_maintainer(maintainer_id, session=None):
1001 Return the name of the maintainer behind C{maintainer_id} or None if that
1002 maintainer_id is invalid.
1004 @type maintainer_id: int
1005 @param maintainer_id: the id of the maintainer
1008 @return: the Maintainer with this C{maintainer_id}
1011 return session.query(Maintainer).get(maintainer_id)
1013 __all__.append('get_maintainer')
1015 ################################################################################
1017 class NewComment(object):
1018 def __init__(self, *args, **kwargs):
1022 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1024 __all__.append('NewComment')
1027 def has_new_comment(package, version, session=None):
1029 Returns true if the given combination of C{package}, C{version} has a comment.
1031 @type package: string
1032 @param package: name of the package
1034 @type version: string
1035 @param version: package version
1037 @type session: Session
1038 @param session: Optional SQLA session object (a temporary one will be
1039 generated if not supplied)
1045 q = session.query(NewComment)
1046 q = q.filter_by(package=package)
1047 q = q.filter_by(version=version)
1049 return bool(q.count() > 0)
1051 __all__.append('has_new_comment')
1054 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1056 Returns (possibly empty) list of NewComment objects for the given
1059 @type package: string (optional)
1060 @param package: name of the package
1062 @type version: string (optional)
1063 @param version: package version
1065 @type comment_id: int (optional)
1066 @param comment_id: An id of a comment
1068 @type session: Session
1069 @param session: Optional SQLA session object (a temporary one will be
1070 generated if not supplied)
1073 @return: A (possibly empty) list of NewComment objects will be returned
1076 q = session.query(NewComment)
1077 if package is not None: q = q.filter_by(package=package)
1078 if version is not None: q = q.filter_by(version=version)
1079 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1083 __all__.append('get_new_comments')
1085 ################################################################################
1087 class Override(object):
1088 def __init__(self, *args, **kwargs):
1092 return '<Override %s (%s)>' % (self.package, self.suite_id)
1094 __all__.append('Override')
1097 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1099 Returns Override object for the given parameters
1101 @type package: string
1102 @param package: The name of the package
1104 @type suite: string, list or None
1105 @param suite: The name of the suite (or suites if a list) to limit to. If
1106 None, don't limit. Defaults to None.
1108 @type component: string, list or None
1109 @param component: The name of the component (or components if a list) to
1110 limit to. If None, don't limit. Defaults to None.
1112 @type overridetype: string, list or None
1113 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1114 limit to. If None, don't limit. Defaults to None.
1116 @type session: Session
1117 @param session: Optional SQLA session object (a temporary one will be
1118 generated if not supplied)
1121 @return: A (possibly empty) list of Override objects will be returned
1124 q = session.query(Override)
1125 q = q.filter_by(package=package)
1127 if suite is not None:
1128 if not isinstance(suite, list): suite = [suite]
1129 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1131 if component is not None:
1132 if not isinstance(component, list): component = [component]
1133 q = q.join(Component).filter(Component.component_name.in_(component))
1135 if overridetype is not None:
1136 if not isinstance(overridetype, list): overridetype = [overridetype]
1137 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1141 __all__.append('get_override')
1144 ################################################################################
1146 class OverrideType(object):
1147 def __init__(self, *args, **kwargs):
1151 return '<OverrideType %s>' % self.overridetype
1153 __all__.append('OverrideType')
1156 def get_override_type(override_type, session=None):
1158 Returns OverrideType object for given C{override type}.
1160 @type override_type: string
1161 @param override_type: The name of the override type
1163 @type session: Session
1164 @param session: Optional SQLA session object (a temporary one will be
1165 generated if not supplied)
1168 @return: the database id for the given override type
1171 q = session.query(OverrideType).filter_by(overridetype=override_type)
1175 except NoResultFound:
1178 __all__.append('get_override_type')
1180 ################################################################################
1182 class PendingContentAssociation(object):
1183 def __init__(self, *args, **kwargs):
1187 return '<PendingContentAssociation %s>' % self.pca_id
1189 __all__.append('PendingContentAssociation')
1191 def insert_pending_content_paths(package, fullpaths, session=None):
1193 Make sure given paths are temporarily associated with given
1197 @param package: the package to associate with should have been read in from the binary control file
1198 @type fullpaths: list
1199 @param fullpaths: the list of paths of the file being associated with the binary
1200 @type session: SQLAlchemy session
1201 @param session: Optional SQLAlchemy session. If this is passed, the caller
1202 is responsible for ensuring a transaction has begun and committing the
1203 results or rolling back based on the result code. If not passed, a commit
1204 will be performed at the end of the function
1206 @return: True upon success, False if there is a problem
1209 privatetrans = False
1212 session = DBConn().session()
1216 arch = get_architecture(package['Architecture'], session)
1217 arch_id = arch.arch_id
1219 # Remove any already existing recorded files for this package
1220 q = session.query(PendingContentAssociation)
1221 q = q.filter_by(package=package['Package'])
1222 q = q.filter_by(version=package['Version'])
1223 q = q.filter_by(architecture=arch_id)
1228 for fullpath in fullpaths:
1229 (path, file) = os.path.split(fullpath)
1231 if path.startswith( "./" ):
1234 filepath_id = get_or_set_contents_path_id(path, session)
1235 filename_id = get_or_set_contents_file_id(file, session)
1237 pathcache[fullpath] = (filepath_id, filename_id)
1239 for fullpath, dat in pathcache.items():
1240 pca = PendingContentAssociation()
1241 pca.package = package['Package']
1242 pca.version = package['Version']
1243 pca.filepath_id = dat[0]
1244 pca.filename_id = dat[1]
1245 pca.architecture = arch_id
1248 # Only commit if we set up the session ourself
1256 except Exception, e:
1257 traceback.print_exc()
1259 # Only rollback if we set up the session ourself
1266 __all__.append('insert_pending_content_paths')
1268 ################################################################################
1270 class Priority(object):
1271 def __init__(self, *args, **kwargs):
1274 def __eq__(self, val):
1275 if isinstance(val, str):
1276 return (self.priority == val)
1277 # This signals to use the normal comparison operator
1278 return NotImplemented
1280 def __ne__(self, val):
1281 if isinstance(val, str):
1282 return (self.priority != val)
1283 # This signals to use the normal comparison operator
1284 return NotImplemented
1287 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1289 __all__.append('Priority')
1292 def get_priority(priority, session=None):
1294 Returns Priority object for given C{priority name}.
1296 @type priority: string
1297 @param priority: The name of the priority
1299 @type session: Session
1300 @param session: Optional SQLA session object (a temporary one will be
1301 generated if not supplied)
1304 @return: Priority object for the given priority
1307 q = session.query(Priority).filter_by(priority=priority)
1311 except NoResultFound:
1314 __all__.append('get_priority')
1317 def get_priorities(session=None):
1319 Returns dictionary of priority names -> id mappings
1321 @type session: Session
1322 @param session: Optional SQL session object (a temporary one will be
1323 generated if not supplied)
1326 @return: dictionary of priority names -> id mappings
1330 q = session.query(Priority)
1332 ret[x.priority] = x.priority_id
1336 __all__.append('get_priorities')
1338 ################################################################################
1340 class Queue(object):
1341 def __init__(self, *args, **kwargs):
1345 return '<Queue %s>' % self.queue_name
1347 def autobuild_upload(self, changes, srcpath, session=None):
1349 Update queue_build database table used for incoming autobuild support.
1351 @type changes: Changes
1352 @param changes: changes object for the upload to process
1354 @type srcpath: string
1355 @param srcpath: path for the queue file entries/link destinations
1357 @type session: SQLAlchemy session
1358 @param session: Optional SQLAlchemy session. If this is passed, the
1359 caller is responsible for ensuring a transaction has begun and
1360 committing the results or rolling back based on the result code. If
1361 not passed, a commit will be performed at the end of the function,
1362 otherwise the caller is responsible for commiting.
1364 @rtype: NoneType or string
1365 @return: None if the operation failed, a string describing the error if not
1368 privatetrans = False
1370 session = DBConn().session()
1373 # TODO: Remove by moving queue config into the database
1376 for suitename in changes.changes["distribution"].keys():
1377 # TODO: Move into database as:
1378 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1379 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1380 # This also gets rid of the SecurityQueueBuild hack below
1381 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1385 s = get_suite(suitename, session)
1387 return "INTERNAL ERROR: Could not find suite %s" % suitename
1389 # TODO: Get from database as above
1390 dest_dir = conf["Dir::QueueBuild"]
1392 # TODO: Move into database as above
1393 if conf.FindB("Dinstall::SecurityQueueBuild"):
1394 dest_dir = os.path.join(dest_dir, suitename)
1396 for file_entry in changes.files.keys():
1397 src = os.path.join(srcpath, file_entry)
1398 dest = os.path.join(dest_dir, file_entry)
1400 # TODO: Move into database as above
1401 if conf.FindB("Dinstall::SecurityQueueBuild"):
1402 # Copy it since the original won't be readable by www-data
1404 utils.copy(src, dest)
1406 # Create a symlink to it
1407 os.symlink(src, dest)
1410 qb.suite_id = s.suite_id
1411 qb.queue_id = self.queue_id
1417 # If the .orig tarballs are in the pool, create a symlink to
1418 # them (if one doesn't already exist)
1419 for dsc_file in changes.dsc_files.keys():
1420 # Skip all files except orig tarballs
1421 from daklib.regexes import re_is_orig_source
1422 if not re_is_orig_source.match(dsc_file):
1424 # Skip orig files not identified in the pool
1425 if not (changes.orig_files.has_key(dsc_file) and
1426 changes.orig_files[dsc_file].has_key("id")):
1428 orig_file_id = changes.orig_files[dsc_file]["id"]
1429 dest = os.path.join(dest_dir, dsc_file)
1431 # If it doesn't exist, create a symlink
1432 if not os.path.exists(dest):
1433 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1434 {'id': orig_file_id})
1437 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1439 src = os.path.join(res[0], res[1])
1440 os.symlink(src, dest)
1442 # Add it to the list of packages for later processing by apt-ftparchive
1444 qb.suite_id = s.suite_id
1445 qb.queue_id = self.queue_id
1450 # If it does, update things to ensure it's not removed prematurely
1452 qb = get_queue_build(dest, s.suite_id, session)
1464 __all__.append('Queue')
1467 def get_or_set_queue(queuename, session=None):
1469 Returns Queue object for given C{queue name}, creating it if it does not
1472 @type queuename: string
1473 @param queuename: The name of the queue
1475 @type session: Session
1476 @param session: Optional SQLA session object (a temporary one will be
1477 generated if not supplied)
1480 @return: Queue object for the given queue
1483 q = session.query(Queue).filter_by(queue_name=queuename)
1487 except NoResultFound:
1489 queue.queue_name = queuename
1491 session.commit_or_flush()
1496 __all__.append('get_or_set_queue')
1498 ################################################################################
1500 class QueueBuild(object):
1501 def __init__(self, *args, **kwargs):
1505 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1507 __all__.append('QueueBuild')
1510 def get_queue_build(filename, suite, session=None):
1512 Returns QueueBuild object for given C{filename} and C{suite}.
1514 @type filename: string
1515 @param filename: The name of the file
1517 @type suiteid: int or str
1518 @param suiteid: Suite name or ID
1520 @type session: Session
1521 @param session: Optional SQLA session object (a temporary one will be
1522 generated if not supplied)
1525 @return: Queue object for the given queue
1528 if isinstance(suite, int):
1529 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1531 q = session.query(QueueBuild).filter_by(filename=filename)
1532 q = q.join(Suite).filter_by(suite_name=suite)
1536 except NoResultFound:
1539 __all__.append('get_queue_build')
1541 ################################################################################
1543 class Section(object):
1544 def __init__(self, *args, **kwargs):
1547 def __eq__(self, val):
1548 if isinstance(val, str):
1549 return (self.section == val)
1550 # This signals to use the normal comparison operator
1551 return NotImplemented
1553 def __ne__(self, val):
1554 if isinstance(val, str):
1555 return (self.section != val)
1556 # This signals to use the normal comparison operator
1557 return NotImplemented
1560 return '<Section %s>' % self.section
1562 __all__.append('Section')
1565 def get_section(section, session=None):
1567 Returns Section object for given C{section name}.
1569 @type section: string
1570 @param section: The name of the section
1572 @type session: Session
1573 @param session: Optional SQLA session object (a temporary one will be
1574 generated if not supplied)
1577 @return: Section object for the given section name
1580 q = session.query(Section).filter_by(section=section)
1584 except NoResultFound:
1587 __all__.append('get_section')
1590 def get_sections(session=None):
1592 Returns dictionary of section names -> id mappings
1594 @type session: Session
1595 @param session: Optional SQL session object (a temporary one will be
1596 generated if not supplied)
1599 @return: dictionary of section names -> id mappings
1603 q = session.query(Section)
1605 ret[x.section] = x.section_id
1609 __all__.append('get_sections')
1611 ################################################################################
1613 class DBSource(object):
1614 def __init__(self, *args, **kwargs):
1618 return '<DBSource %s (%s)>' % (self.source, self.version)
1620 __all__.append('DBSource')
1623 def source_exists(source, source_version, suites = ["any"], session=None):
1625 Ensure that source exists somewhere in the archive for the binary
1626 upload being processed.
1627 1. exact match => 1.0-3
1628 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1630 @type package: string
1631 @param package: package source name
1633 @type source_version: string
1634 @param source_version: expected source version
1637 @param suites: list of suites to check in, default I{any}
1639 @type session: Session
1640 @param session: Optional SQLA session object (a temporary one will be
1641 generated if not supplied)
1644 @return: returns 1 if a source with expected version is found, otherwise 0
1651 for suite in suites:
1652 q = session.query(DBSource).filter_by(source=source)
1654 # source must exist in suite X, or in some other suite that's
1655 # mapped to X, recursively... silent-maps are counted too,
1656 # unreleased-maps aren't.
1657 maps = cnf.ValueList("SuiteMappings")[:]
1659 maps = [ m.split() for m in maps ]
1660 maps = [ (x[1], x[2]) for x in maps
1661 if x[0] == "map" or x[0] == "silent-map" ]
1664 if x[1] in s and x[0] not in s:
1667 q = q.join(SrcAssociation).join(Suite)
1668 q = q.filter(Suite.suite_name.in_(s))
1670 # Reduce the query results to a list of version numbers
1671 ql = [ j.version for j in q.all() ]
1674 if source_version in ql:
1678 from daklib.regexes import re_bin_only_nmu
1679 orig_source_version = re_bin_only_nmu.sub('', source_version)
1680 if orig_source_version in ql:
1683 # No source found so return not ok
1688 __all__.append('source_exists')
1691 def get_suites_source_in(source, session=None):
1693 Returns list of Suite objects which given C{source} name is in
1696 @param source: DBSource package name to search for
1699 @return: list of Suite objects for the given source
1702 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1704 __all__.append('get_suites_source_in')
1707 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1709 Returns list of DBSource objects for given C{source} name and other parameters
1712 @param source: DBSource package name to search for
1714 @type source: str or None
1715 @param source: DBSource version name to search for or None if not applicable
1717 @type dm_upload_allowed: bool
1718 @param dm_upload_allowed: If None, no effect. If True or False, only
1719 return packages with that dm_upload_allowed setting
1721 @type session: Session
1722 @param session: Optional SQL session object (a temporary one will be
1723 generated if not supplied)
1726 @return: list of DBSource objects for the given name (may be empty)
1729 q = session.query(DBSource).filter_by(source=source)
1731 if version is not None:
1732 q = q.filter_by(version=version)
1734 if dm_upload_allowed is not None:
1735 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1739 __all__.append('get_sources_from_name')
1742 def get_source_in_suite(source, suite, session=None):
1744 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1746 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1747 - B{suite} - a suite name, eg. I{unstable}
1749 @type source: string
1750 @param source: source package name
1753 @param suite: the suite name
1756 @return: the version for I{source} in I{suite}
1760 q = session.query(SrcAssociation)
1761 q = q.join('source').filter_by(source=source)
1762 q = q.join('suite').filter_by(suite_name=suite)
1765 return q.one().source
1766 except NoResultFound:
1769 __all__.append('get_source_in_suite')
1771 ################################################################################
1773 class SrcAssociation(object):
1774 def __init__(self, *args, **kwargs):
1778 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1780 __all__.append('SrcAssociation')
1782 ################################################################################
1784 class SrcFormat(object):
1785 def __init__(self, *args, **kwargs):
1789 return '<SrcFormat %s>' % (self.format_name)
1791 __all__.append('SrcFormat')
1793 ################################################################################
1795 class SrcUploader(object):
1796 def __init__(self, *args, **kwargs):
1800 return '<SrcUploader %s>' % self.uploader_id
1802 __all__.append('SrcUploader')
1804 ################################################################################
1806 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1807 ('SuiteID', 'suite_id'),
1808 ('Version', 'version'),
1809 ('Origin', 'origin'),
1811 ('Description', 'description'),
1812 ('Untouchable', 'untouchable'),
1813 ('Announce', 'announce'),
1814 ('Codename', 'codename'),
1815 ('OverrideCodename', 'overridecodename'),
1816 ('ValidTime', 'validtime'),
1817 ('Priority', 'priority'),
1818 ('NotAutomatic', 'notautomatic'),
1819 ('CopyChanges', 'copychanges'),
1820 ('CopyDotDak', 'copydotdak'),
1821 ('CommentsDir', 'commentsdir'),
1822 ('OverrideSuite', 'overridesuite'),
1823 ('ChangelogBase', 'changelogbase')]
1826 class Suite(object):
1827 def __init__(self, *args, **kwargs):
1831 return '<Suite %s>' % self.suite_name
1833 def __eq__(self, val):
1834 if isinstance(val, str):
1835 return (self.suite_name == val)
1836 # This signals to use the normal comparison operator
1837 return NotImplemented
1839 def __ne__(self, val):
1840 if isinstance(val, str):
1841 return (self.suite_name != val)
1842 # This signals to use the normal comparison operator
1843 return NotImplemented
1847 for disp, field in SUITE_FIELDS:
1848 val = getattr(self, field, None)
1850 ret.append("%s: %s" % (disp, val))
1852 return "\n".join(ret)
1854 __all__.append('Suite')
1857 def get_suite_architecture(suite, architecture, session=None):
1859 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1863 @param suite: Suite name to search for
1865 @type architecture: str
1866 @param architecture: Architecture name to search for
1868 @type session: Session
1869 @param session: Optional SQL session object (a temporary one will be
1870 generated if not supplied)
1872 @rtype: SuiteArchitecture
1873 @return: the SuiteArchitecture object or None
1876 q = session.query(SuiteArchitecture)
1877 q = q.join(Architecture).filter_by(arch_string=architecture)
1878 q = q.join(Suite).filter_by(suite_name=suite)
1882 except NoResultFound:
1885 __all__.append('get_suite_architecture')
1888 def get_suite(suite, session=None):
1890 Returns Suite object for given C{suite name}.
1893 @param suite: The name of the suite
1895 @type session: Session
1896 @param session: Optional SQLA session object (a temporary one will be
1897 generated if not supplied)
1900 @return: Suite object for the requested suite name (None if not present)
1903 q = session.query(Suite).filter_by(suite_name=suite)
1907 except NoResultFound:
1910 __all__.append('get_suite')
1912 ################################################################################
1914 class SuiteArchitecture(object):
1915 def __init__(self, *args, **kwargs):
1919 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1921 __all__.append('SuiteArchitecture')
1924 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1926 Returns list of Architecture objects for given C{suite} name
1929 @param source: Suite name to search for
1931 @type skipsrc: boolean
1932 @param skipsrc: Whether to skip returning the 'source' architecture entry
1935 @type skipall: boolean
1936 @param skipall: Whether to skip returning the 'all' architecture entry
1939 @type session: Session
1940 @param session: Optional SQL session object (a temporary one will be
1941 generated if not supplied)
1944 @return: list of Architecture objects for the given name (may be empty)
1947 q = session.query(Architecture)
1948 q = q.join(SuiteArchitecture)
1949 q = q.join(Suite).filter_by(suite_name=suite)
1952 q = q.filter(Architecture.arch_string != 'source')
1955 q = q.filter(Architecture.arch_string != 'all')
1957 q = q.order_by('arch_string')
1961 __all__.append('get_suite_architectures')
1963 ################################################################################
1965 class SuiteSrcFormat(object):
1966 def __init__(self, *args, **kwargs):
1970 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1972 __all__.append('SuiteSrcFormat')
1975 def get_suite_src_formats(suite, session=None):
1977 Returns list of allowed SrcFormat for C{suite}.
1980 @param suite: Suite name to search for
1982 @type session: Session
1983 @param session: Optional SQL session object (a temporary one will be
1984 generated if not supplied)
1987 @return: the list of allowed source formats for I{suite}
1990 q = session.query(SrcFormat)
1991 q = q.join(SuiteSrcFormat)
1992 q = q.join(Suite).filter_by(suite_name=suite)
1993 q = q.order_by('format_name')
1997 __all__.append('get_suite_src_formats')
1999 ################################################################################
2002 def __init__(self, *args, **kwargs):
2005 def __eq__(self, val):
2006 if isinstance(val, str):
2007 return (self.uid == val)
2008 # This signals to use the normal comparison operator
2009 return NotImplemented
2011 def __ne__(self, val):
2012 if isinstance(val, str):
2013 return (self.uid != val)
2014 # This signals to use the normal comparison operator
2015 return NotImplemented
2018 return '<Uid %s (%s)>' % (self.uid, self.name)
2020 __all__.append('Uid')
2023 def add_database_user(uidname, session=None):
2025 Adds a database user
2027 @type uidname: string
2028 @param uidname: The uid of the user to add
2030 @type session: SQLAlchemy
2031 @param session: Optional SQL session object (a temporary one will be
2032 generated if not supplied). If not passed, a commit will be performed at
2033 the end of the function, otherwise the caller is responsible for commiting.
2036 @return: the uid object for the given uidname
2039 session.execute("CREATE USER :uid", {'uid': uidname})
2040 session.commit_or_flush()
2042 __all__.append('add_database_user')
2045 def get_or_set_uid(uidname, session=None):
2047 Returns uid object for given uidname.
2049 If no matching uidname is found, a row is inserted.
2051 @type uidname: string
2052 @param uidname: The uid to add
2054 @type session: SQLAlchemy
2055 @param session: Optional SQL session object (a temporary one will be
2056 generated if not supplied). If not passed, a commit will be performed at
2057 the end of the function, otherwise the caller is responsible for commiting.
2060 @return: the uid object for the given uidname
2063 q = session.query(Uid).filter_by(uid=uidname)
2067 except NoResultFound:
2071 session.commit_or_flush()
2076 __all__.append('get_or_set_uid')
2079 def get_uid_from_fingerprint(fpr, session=None):
2080 q = session.query(Uid)
2081 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2085 except NoResultFound:
2088 __all__.append('get_uid_from_fingerprint')
2090 ################################################################################
2092 class DBConn(Singleton):
2094 database module init.
2096 def __init__(self, *args, **kwargs):
2097 super(DBConn, self).__init__(*args, **kwargs)
2099 def _startup(self, *args, **kwargs):
2101 if kwargs.has_key('debug'):
2105 def __setuptables(self):
2106 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2107 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2108 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2109 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2110 self.tbl_component = Table('component', self.db_meta, autoload=True)
2111 self.tbl_config = Table('config', self.db_meta, autoload=True)
2112 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2113 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2114 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2115 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2116 self.tbl_files = Table('files', self.db_meta, autoload=True)
2117 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2118 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2119 self.tbl_location = Table('location', self.db_meta, autoload=True)
2120 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2121 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2122 self.tbl_override = Table('override', self.db_meta, autoload=True)
2123 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2124 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2125 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2126 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2127 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2128 self.tbl_section = Table('section', self.db_meta, autoload=True)
2129 self.tbl_source = Table('source', self.db_meta, autoload=True)
2130 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2131 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2132 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2133 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2134 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2135 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2136 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2138 def __setupmappers(self):
2139 mapper(Architecture, self.tbl_architecture,
2140 properties = dict(arch_id = self.tbl_architecture.c.id))
2142 mapper(Archive, self.tbl_archive,
2143 properties = dict(archive_id = self.tbl_archive.c.id,
2144 archive_name = self.tbl_archive.c.name))
2146 mapper(BinAssociation, self.tbl_bin_associations,
2147 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2148 suite_id = self.tbl_bin_associations.c.suite,
2149 suite = relation(Suite),
2150 binary_id = self.tbl_bin_associations.c.bin,
2151 binary = relation(DBBinary)))
2153 mapper(DBBinary, self.tbl_binaries,
2154 properties = dict(binary_id = self.tbl_binaries.c.id,
2155 package = self.tbl_binaries.c.package,
2156 version = self.tbl_binaries.c.version,
2157 maintainer_id = self.tbl_binaries.c.maintainer,
2158 maintainer = relation(Maintainer),
2159 source_id = self.tbl_binaries.c.source,
2160 source = relation(DBSource),
2161 arch_id = self.tbl_binaries.c.architecture,
2162 architecture = relation(Architecture),
2163 poolfile_id = self.tbl_binaries.c.file,
2164 poolfile = relation(PoolFile),
2165 binarytype = self.tbl_binaries.c.type,
2166 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2167 fingerprint = relation(Fingerprint),
2168 install_date = self.tbl_binaries.c.install_date,
2169 binassociations = relation(BinAssociation,
2170 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2172 mapper(Component, self.tbl_component,
2173 properties = dict(component_id = self.tbl_component.c.id,
2174 component_name = self.tbl_component.c.name))
2176 mapper(DBConfig, self.tbl_config,
2177 properties = dict(config_id = self.tbl_config.c.id))
2179 mapper(ContentAssociation, self.tbl_content_associations,
2180 properties = dict(ca_id = self.tbl_content_associations.c.id,
2181 filename_id = self.tbl_content_associations.c.filename,
2182 filename = relation(ContentFilename),
2183 filepath_id = self.tbl_content_associations.c.filepath,
2184 filepath = relation(ContentFilepath),
2185 binary_id = self.tbl_content_associations.c.binary_pkg,
2186 binary = relation(DBBinary)))
2189 mapper(ContentFilename, self.tbl_content_file_names,
2190 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2191 filename = self.tbl_content_file_names.c.file))
2193 mapper(ContentFilepath, self.tbl_content_file_paths,
2194 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2195 filepath = self.tbl_content_file_paths.c.path))
2197 mapper(DSCFile, self.tbl_dsc_files,
2198 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2199 source_id = self.tbl_dsc_files.c.source,
2200 source = relation(DBSource),
2201 poolfile_id = self.tbl_dsc_files.c.file,
2202 poolfile = relation(PoolFile)))
2204 mapper(PoolFile, self.tbl_files,
2205 properties = dict(file_id = self.tbl_files.c.id,
2206 filesize = self.tbl_files.c.size,
2207 location_id = self.tbl_files.c.location,
2208 location = relation(Location)))
2210 mapper(Fingerprint, self.tbl_fingerprint,
2211 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2212 uid_id = self.tbl_fingerprint.c.uid,
2213 uid = relation(Uid),
2214 keyring_id = self.tbl_fingerprint.c.keyring,
2215 keyring = relation(Keyring)))
2217 mapper(Keyring, self.tbl_keyrings,
2218 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2219 keyring_id = self.tbl_keyrings.c.id))
2221 mapper(Location, self.tbl_location,
2222 properties = dict(location_id = self.tbl_location.c.id,
2223 component_id = self.tbl_location.c.component,
2224 component = relation(Component),
2225 archive_id = self.tbl_location.c.archive,
2226 archive = relation(Archive),
2227 archive_type = self.tbl_location.c.type))
2229 mapper(Maintainer, self.tbl_maintainer,
2230 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2232 mapper(NewComment, self.tbl_new_comments,
2233 properties = dict(comment_id = self.tbl_new_comments.c.id))
2235 mapper(Override, self.tbl_override,
2236 properties = dict(suite_id = self.tbl_override.c.suite,
2237 suite = relation(Suite),
2238 component_id = self.tbl_override.c.component,
2239 component = relation(Component),
2240 priority_id = self.tbl_override.c.priority,
2241 priority = relation(Priority),
2242 section_id = self.tbl_override.c.section,
2243 section = relation(Section),
2244 overridetype_id = self.tbl_override.c.type,
2245 overridetype = relation(OverrideType)))
2247 mapper(OverrideType, self.tbl_override_type,
2248 properties = dict(overridetype = self.tbl_override_type.c.type,
2249 overridetype_id = self.tbl_override_type.c.id))
2251 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2252 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2253 filepath_id = self.tbl_pending_content_associations.c.filepath,
2254 filepath = relation(ContentFilepath),
2255 filename_id = self.tbl_pending_content_associations.c.filename,
2256 filename = relation(ContentFilename)))
2258 mapper(Priority, self.tbl_priority,
2259 properties = dict(priority_id = self.tbl_priority.c.id))
2261 mapper(Queue, self.tbl_queue,
2262 properties = dict(queue_id = self.tbl_queue.c.id))
2264 mapper(QueueBuild, self.tbl_queue_build,
2265 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2266 queue_id = self.tbl_queue_build.c.queue,
2267 queue = relation(Queue, backref='queuebuild')))
2269 mapper(Section, self.tbl_section,
2270 properties = dict(section_id = self.tbl_section.c.id))
2272 mapper(DBSource, self.tbl_source,
2273 properties = dict(source_id = self.tbl_source.c.id,
2274 version = self.tbl_source.c.version,
2275 maintainer_id = self.tbl_source.c.maintainer,
2276 maintainer = relation(Maintainer,
2277 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2278 poolfile_id = self.tbl_source.c.file,
2279 poolfile = relation(PoolFile),
2280 fingerprint_id = self.tbl_source.c.sig_fpr,
2281 fingerprint = relation(Fingerprint),
2282 changedby_id = self.tbl_source.c.changedby,
2283 changedby = relation(Maintainer,
2284 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2285 srcfiles = relation(DSCFile,
2286 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2287 srcassociations = relation(SrcAssociation,
2288 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2290 mapper(SrcAssociation, self.tbl_src_associations,
2291 properties = dict(sa_id = self.tbl_src_associations.c.id,
2292 suite_id = self.tbl_src_associations.c.suite,
2293 suite = relation(Suite),
2294 source_id = self.tbl_src_associations.c.source,
2295 source = relation(DBSource)))
2297 mapper(SrcFormat, self.tbl_src_format,
2298 properties = dict(src_format_id = self.tbl_src_format.c.id,
2299 format_name = self.tbl_src_format.c.format_name))
2301 mapper(SrcUploader, self.tbl_src_uploaders,
2302 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2303 source_id = self.tbl_src_uploaders.c.source,
2304 source = relation(DBSource,
2305 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2306 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2307 maintainer = relation(Maintainer,
2308 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2310 mapper(Suite, self.tbl_suite,
2311 properties = dict(suite_id = self.tbl_suite.c.id))
2313 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2314 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2315 suite = relation(Suite, backref='suitearchitectures'),
2316 arch_id = self.tbl_suite_architectures.c.architecture,
2317 architecture = relation(Architecture)))
2319 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2320 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2321 suite = relation(Suite, backref='suitesrcformats'),
2322 src_format_id = self.tbl_suite_src_formats.c.src_format,
2323 src_format = relation(SrcFormat)))
2325 mapper(Uid, self.tbl_uid,
2326 properties = dict(uid_id = self.tbl_uid.c.id,
2327 fingerprint = relation(Fingerprint)))
2329 ## Connection functions
2330 def __createconn(self):
2331 from config import Config
2335 connstr = "postgres://%s" % cnf["DB::Host"]
2336 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2337 connstr += ":%s" % cnf["DB::Port"]
2338 connstr += "/%s" % cnf["DB::Name"]
2341 connstr = "postgres:///%s" % cnf["DB::Name"]
2342 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2343 connstr += "?port=%s" % cnf["DB::Port"]
2345 self.db_pg = create_engine(connstr, echo=self.debug)
2346 self.db_meta = MetaData()
2347 self.db_meta.bind = self.db_pg
2348 self.db_smaker = sessionmaker(bind=self.db_pg,
2352 self.__setuptables()
2353 self.__setupmappers()
2356 return self.db_smaker()
2358 __all__.append('DBConn')