5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from inspect import getargspec
42 from sqlalchemy import create_engine, Table, MetaData
43 from sqlalchemy.orm import sessionmaker, mapper, relation
45 # Don't remove this, we re-export the exceptions to scripts which import us
46 from sqlalchemy.exc import *
47 from sqlalchemy.orm.exc import NoResultFound
49 # Only import Config until Queue stuff is changed to store its config
51 from config import Config
52 from singleton import Singleton
53 from textutils import fix_maintainer
55 ################################################################################
57 __all__ = ['IntegrityError', 'SQLAlchemyError']
59 ################################################################################
61 def session_wrapper(fn):
63 Wrapper around common ".., session=None):" handling. If the wrapped
64 function is called without passing 'session', we create a local one
65 and destroy it when the function ends.
67 Also attaches a commit_or_flush method to the session; if we created a
68 local session, this is a synonym for session.commit(), otherwise it is a
69 synonym for session.flush().
72 def wrapped(*args, **kwargs):
73 private_transaction = False
75 # Find the session object
76 session = kwargs.get('session')
79 if len(args) <= len(getargspec(fn)[0]) - 1:
80 # No session specified as last argument or in kwargs
81 private_transaction = True
82 session = kwargs['session'] = DBConn().session()
84 # Session is last argument in args
88 session = args[-1] = DBConn().session()
89 private_transaction = True
91 if private_transaction:
92 session.commit_or_flush = session.commit
94 session.commit_or_flush = session.flush
97 return fn(*args, **kwargs)
99 if private_transaction:
100 # We created a session; close it.
103 wrapped.__doc__ = fn.__doc__
104 wrapped.func_name = fn.func_name
108 ################################################################################
110 class Architecture(object):
111 def __init__(self, *args, **kwargs):
114 def __eq__(self, val):
115 if isinstance(val, str):
116 return (self.arch_string== val)
117 # This signals to use the normal comparison operator
118 return NotImplemented
120 def __ne__(self, val):
121 if isinstance(val, str):
122 return (self.arch_string != val)
123 # This signals to use the normal comparison operator
124 return NotImplemented
127 return '<Architecture %s>' % self.arch_string
129 __all__.append('Architecture')
132 def get_architecture(architecture, session=None):
134 Returns database id for given C{architecture}.
136 @type architecture: string
137 @param architecture: The name of the architecture
139 @type session: Session
140 @param session: Optional SQLA session object (a temporary one will be
141 generated if not supplied)
144 @return: Architecture object for the given arch (None if not present)
147 q = session.query(Architecture).filter_by(arch_string=architecture)
151 except NoResultFound:
154 __all__.append('get_architecture')
157 def get_architecture_suites(architecture, session=None):
159 Returns list of Suite objects for given C{architecture} name
162 @param source: Architecture name to search for
164 @type session: Session
165 @param session: Optional SQL session object (a temporary one will be
166 generated if not supplied)
169 @return: list of Suite objects for the given name (may be empty)
172 q = session.query(Suite)
173 q = q.join(SuiteArchitecture)
174 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
180 __all__.append('get_architecture_suites')
182 ################################################################################
184 class Archive(object):
185 def __init__(self, *args, **kwargs):
189 return '<Archive %s>' % self.archive_name
191 __all__.append('Archive')
194 def get_archive(archive, session=None):
196 returns database id for given C{archive}.
198 @type archive: string
199 @param archive: the name of the arhive
201 @type session: Session
202 @param session: Optional SQLA session object (a temporary one will be
203 generated if not supplied)
206 @return: Archive object for the given name (None if not present)
209 archive = archive.lower()
211 q = session.query(Archive).filter_by(archive_name=archive)
215 except NoResultFound:
218 __all__.append('get_archive')
220 ################################################################################
222 class BinAssociation(object):
223 def __init__(self, *args, **kwargs):
227 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
229 __all__.append('BinAssociation')
231 ################################################################################
233 class DBBinary(object):
234 def __init__(self, *args, **kwargs):
238 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
240 __all__.append('DBBinary')
243 def get_suites_binary_in(package, session=None):
245 Returns list of Suite objects which given C{package} name is in
248 @param source: DBBinary package name to search for
251 @return: list of Suite objects for the given package
254 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
256 __all__.append('get_suites_binary_in')
259 def get_binary_from_id(id, session=None):
261 Returns DBBinary object for given C{id}
264 @param id: Id of the required binary
266 @type session: Session
267 @param session: Optional SQLA session object (a temporary one will be
268 generated if not supplied)
271 @return: DBBinary object for the given binary (None if not present)
274 q = session.query(DBBinary).filter_by(binary_id=id)
278 except NoResultFound:
281 __all__.append('get_binary_from_id')
284 def get_binaries_from_name(package, version=None, architecture=None, session=None):
286 Returns list of DBBinary objects for given C{package} name
289 @param package: DBBinary package name to search for
291 @type version: str or None
292 @param version: Version to search for (or None)
294 @type package: str, list or None
295 @param package: Architectures to limit to (or None if no limit)
297 @type session: Session
298 @param session: Optional SQL session object (a temporary one will be
299 generated if not supplied)
302 @return: list of DBBinary objects for the given name (may be empty)
305 q = session.query(DBBinary).filter_by(package=package)
307 if version is not None:
308 q = q.filter_by(version=version)
310 if architecture is not None:
311 if not isinstance(architecture, list):
312 architecture = [architecture]
313 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
319 __all__.append('get_binaries_from_name')
322 def get_binaries_from_source_id(source_id, session=None):
324 Returns list of DBBinary objects for given C{source_id}
327 @param source_id: source_id to search for
329 @type session: Session
330 @param session: Optional SQL session object (a temporary one will be
331 generated if not supplied)
334 @return: list of DBBinary objects for the given name (may be empty)
337 return session.query(DBBinary).filter_by(source_id=source_id).all()
339 __all__.append('get_binaries_from_source_id')
342 def get_binary_from_name_suite(package, suitename, session=None):
343 ### For dak examine-package
344 ### XXX: Doesn't use object API yet
346 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
347 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
348 WHERE b.package=:package
350 AND fi.location = l.id
351 AND l.component = c.id
354 AND su.suite_name=:suitename
355 ORDER BY b.version DESC"""
357 return session.execute(sql, {'package': package, 'suitename': suitename})
359 __all__.append('get_binary_from_name_suite')
362 def get_binary_components(package, suitename, arch, session=None):
363 # Check for packages that have moved from one component to another
364 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
365 WHERE b.package=:package AND s.suite_name=:suitename
366 AND (a.arch_string = :arch OR a.arch_string = 'all')
367 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
368 AND f.location = l.id
369 AND l.component = c.id
372 vals = {'package': package, 'suitename': suitename, 'arch': arch}
374 return session.execute(query, vals)
376 __all__.append('get_binary_components')
378 ################################################################################
380 class BinaryACL(object):
381 def __init__(self, *args, **kwargs):
384 __all__.append('BinaryACL')
386 ################################################################################
388 class BinaryACLMap(object):
389 def __init__(self, *args, **kwargs):
392 __all__.append('BinaryACLMap')
394 ################################################################################
396 class Component(object):
397 def __init__(self, *args, **kwargs):
400 def __eq__(self, val):
401 if isinstance(val, str):
402 return (self.component_name == val)
403 # This signals to use the normal comparison operator
404 return NotImplemented
406 def __ne__(self, val):
407 if isinstance(val, str):
408 return (self.component_name != val)
409 # This signals to use the normal comparison operator
410 return NotImplemented
413 return '<Component %s>' % self.component_name
416 __all__.append('Component')
419 def get_component(component, session=None):
421 Returns database id for given C{component}.
423 @type component: string
424 @param component: The name of the override type
427 @return: the database id for the given component
430 component = component.lower()
432 q = session.query(Component).filter_by(component_name=component)
436 except NoResultFound:
439 __all__.append('get_component')
441 ################################################################################
443 class DBConfig(object):
444 def __init__(self, *args, **kwargs):
448 return '<DBConfig %s>' % self.name
450 __all__.append('DBConfig')
452 ################################################################################
454 class ContentFilename(object):
455 def __init__(self, *args, **kwargs):
459 return '<ContentFilename %s>' % self.filename
461 __all__.append('ContentFilename')
464 def get_or_set_contents_file_id(filename, session=None):
466 Returns database id for given filename.
468 If no matching file is found, a row is inserted.
470 @type filename: string
471 @param filename: The filename
472 @type session: SQLAlchemy
473 @param session: Optional SQL session object (a temporary one will be
474 generated if not supplied). If not passed, a commit will be performed at
475 the end of the function, otherwise the caller is responsible for commiting.
478 @return: the database id for the given component
481 q = session.query(ContentFilename).filter_by(filename=filename)
484 ret = q.one().cafilename_id
485 except NoResultFound:
486 cf = ContentFilename()
487 cf.filename = filename
489 session.commit_or_flush()
490 ret = cf.cafilename_id
494 __all__.append('get_or_set_contents_file_id')
497 def get_contents(suite, overridetype, section=None, session=None):
499 Returns contents for a suite / overridetype combination, limiting
500 to a section if not None.
503 @param suite: Suite object
505 @type overridetype: OverrideType
506 @param overridetype: OverrideType object
508 @type section: Section
509 @param section: Optional section object to limit results to
511 @type session: SQLAlchemy
512 @param session: Optional SQL session object (a temporary one will be
513 generated if not supplied)
516 @return: ResultsProxy object set up to return tuples of (filename, section,
520 # find me all of the contents for a given suite
521 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
525 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
526 JOIN content_file_names n ON (c.filename=n.id)
527 JOIN binaries b ON (b.id=c.binary_pkg)
528 JOIN override o ON (o.package=b.package)
529 JOIN section s ON (s.id=o.section)
530 WHERE o.suite = :suiteid AND o.type = :overridetypeid
531 AND b.type=:overridetypename"""
533 vals = {'suiteid': suite.suite_id,
534 'overridetypeid': overridetype.overridetype_id,
535 'overridetypename': overridetype.overridetype}
537 if section is not None:
538 contents_q += " AND s.id = :sectionid"
539 vals['sectionid'] = section.section_id
541 contents_q += " ORDER BY fn"
543 return session.execute(contents_q, vals)
545 __all__.append('get_contents')
547 ################################################################################
549 class ContentFilepath(object):
550 def __init__(self, *args, **kwargs):
554 return '<ContentFilepath %s>' % self.filepath
556 __all__.append('ContentFilepath')
559 def get_or_set_contents_path_id(filepath, session=None):
561 Returns database id for given path.
563 If no matching file is found, a row is inserted.
565 @type filename: string
566 @param filename: The filepath
567 @type session: SQLAlchemy
568 @param session: Optional SQL session object (a temporary one will be
569 generated if not supplied). If not passed, a commit will be performed at
570 the end of the function, otherwise the caller is responsible for commiting.
573 @return: the database id for the given path
576 q = session.query(ContentFilepath).filter_by(filepath=filepath)
579 ret = q.one().cafilepath_id
580 except NoResultFound:
581 cf = ContentFilepath()
582 cf.filepath = filepath
584 session.commit_or_flush()
585 ret = cf.cafilepath_id
589 __all__.append('get_or_set_contents_path_id')
591 ################################################################################
593 class ContentAssociation(object):
594 def __init__(self, *args, **kwargs):
598 return '<ContentAssociation %s>' % self.ca_id
600 __all__.append('ContentAssociation')
602 def insert_content_paths(binary_id, fullpaths, session=None):
604 Make sure given path is associated with given binary id
607 @param binary_id: the id of the binary
608 @type fullpaths: list
609 @param fullpaths: the list of paths of the file being associated with the binary
610 @type session: SQLAlchemy session
611 @param session: Optional SQLAlchemy session. If this is passed, the caller
612 is responsible for ensuring a transaction has begun and committing the
613 results or rolling back based on the result code. If not passed, a commit
614 will be performed at the end of the function, otherwise the caller is
615 responsible for commiting.
617 @return: True upon success
622 session = DBConn().session()
628 for fullpath in fullpaths:
629 # Get the necessary IDs ...
630 (path, file) = os.path.split(fullpath)
632 filepath_id = get_or_set_contents_path_id(path, session)
633 filename_id = get_or_set_contents_file_id(file, session)
635 pathcache[fullpath] = (filepath_id, filename_id)
637 for fullpath, dat in pathcache.items():
638 ca = ContentAssociation()
639 ca.binary_id = binary_id
640 ca.filepath_id = dat[0]
641 ca.filename_id = dat[1]
644 # Only commit if we set up the session ourself
654 traceback.print_exc()
656 # Only rollback if we set up the session ourself
663 __all__.append('insert_content_paths')
665 ################################################################################
667 class DSCFile(object):
668 def __init__(self, *args, **kwargs):
672 return '<DSCFile %s>' % self.dscfile_id
674 __all__.append('DSCFile')
677 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
679 Returns a list of DSCFiles which may be empty
681 @type dscfile_id: int (optional)
682 @param dscfile_id: the dscfile_id of the DSCFiles to find
684 @type source_id: int (optional)
685 @param source_id: the source id related to the DSCFiles to find
687 @type poolfile_id: int (optional)
688 @param poolfile_id: the poolfile id related to the DSCFiles to find
691 @return: Possibly empty list of DSCFiles
694 q = session.query(DSCFile)
696 if dscfile_id is not None:
697 q = q.filter_by(dscfile_id=dscfile_id)
699 if source_id is not None:
700 q = q.filter_by(source_id=source_id)
702 if poolfile_id is not None:
703 q = q.filter_by(poolfile_id=poolfile_id)
707 __all__.append('get_dscfiles')
709 ################################################################################
711 class PoolFile(object):
712 def __init__(self, *args, **kwargs):
716 return '<PoolFile %s>' % self.filename
718 __all__.append('PoolFile')
721 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
724 (ValidFileFound [boolean or None], PoolFile object or None)
726 @type filename: string
727 @param filename: the filename of the file to check against the DB
730 @param filesize: the size of the file to check against the DB
733 @param md5sum: the md5sum of the file to check against the DB
735 @type location_id: int
736 @param location_id: the id of the location to look in
739 @return: Tuple of length 2.
740 If more than one file found with that name:
742 If valid pool file found: (True, PoolFile object)
743 If valid pool file not found:
744 (False, None) if no file found
745 (False, PoolFile object) if file found with size/md5sum mismatch
748 q = session.query(PoolFile).filter_by(filename=filename)
749 q = q.join(Location).filter_by(location_id=location_id)
759 if obj.md5sum != md5sum or obj.filesize != filesize:
767 __all__.append('check_poolfile')
770 def get_poolfile_by_id(file_id, session=None):
772 Returns a PoolFile objects or None for the given id
775 @param file_id: the id of the file to look for
777 @rtype: PoolFile or None
778 @return: either the PoolFile object or None
781 q = session.query(PoolFile).filter_by(file_id=file_id)
785 except NoResultFound:
788 __all__.append('get_poolfile_by_id')
792 def get_poolfile_by_name(filename, location_id=None, session=None):
794 Returns an array of PoolFile objects for the given filename and
795 (optionally) location_id
797 @type filename: string
798 @param filename: the filename of the file to check against the DB
800 @type location_id: int
801 @param location_id: the id of the location to look in (optional)
804 @return: array of PoolFile objects
807 q = session.query(PoolFile).filter_by(filename=filename)
809 if location_id is not None:
810 q = q.join(Location).filter_by(location_id=location_id)
814 __all__.append('get_poolfile_by_name')
817 def get_poolfile_like_name(filename, session=None):
819 Returns an array of PoolFile objects which are like the given name
821 @type filename: string
822 @param filename: the filename of the file to check against the DB
825 @return: array of PoolFile objects
828 # TODO: There must be a way of properly using bind parameters with %FOO%
829 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
833 __all__.append('get_poolfile_like_name')
835 ################################################################################
837 class Fingerprint(object):
838 def __init__(self, *args, **kwargs):
842 return '<Fingerprint %s>' % self.fingerprint
844 __all__.append('Fingerprint')
847 def get_or_set_fingerprint(fpr, session=None):
849 Returns Fingerprint object for given fpr.
851 If no matching fpr is found, a row is inserted.
854 @param fpr: The fpr to find / add
856 @type session: SQLAlchemy
857 @param session: Optional SQL session object (a temporary one will be
858 generated if not supplied). If not passed, a commit will be performed at
859 the end of the function, otherwise the caller is responsible for commiting.
860 A flush will be performed either way.
863 @return: the Fingerprint object for the given fpr
866 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
870 except NoResultFound:
871 fingerprint = Fingerprint()
872 fingerprint.fingerprint = fpr
873 session.add(fingerprint)
874 session.commit_or_flush()
879 __all__.append('get_or_set_fingerprint')
881 ################################################################################
883 class Keyring(object):
884 def __init__(self, *args, **kwargs):
888 return '<Keyring %s>' % self.keyring_name
890 __all__.append('Keyring')
893 def get_or_set_keyring(keyring, session=None):
895 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
896 and return the new Keyring
897 If C{keyring} already has an entry, simply return the existing Keyring
899 @type keyring: string
900 @param keyring: the keyring name
903 @return: the Keyring object for this keyring
906 q = session.query(Keyring).filter_by(keyring_name=keyring)
910 except NoResultFound:
911 obj = Keyring(keyring_name=keyring)
913 session.commit_or_flush()
916 __all__.append('get_or_set_keyring')
918 ################################################################################
920 class Location(object):
921 def __init__(self, *args, **kwargs):
925 return '<Location %s (%s)>' % (self.path, self.location_id)
927 __all__.append('Location')
930 def get_location(location, component=None, archive=None, session=None):
932 Returns Location object for the given combination of location, component
935 @type location: string
936 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
938 @type component: string
939 @param component: the component name (if None, no restriction applied)
941 @type archive: string
942 @param archive_id: the archive name (if None, no restriction applied)
944 @rtype: Location / None
945 @return: Either a Location object or None if one can't be found
948 q = session.query(Location).filter_by(path=location)
950 if archive is not None:
951 q = q.join(Archive).filter_by(archive_name=archive)
953 if component is not None:
954 q = q.join(Component).filter_by(component_name=component)
958 except NoResultFound:
961 __all__.append('get_location')
963 ################################################################################
965 class Maintainer(object):
966 def __init__(self, *args, **kwargs):
970 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
972 def get_split_maintainer(self):
973 if not hasattr(self, 'name') or self.name is None:
974 return ('', '', '', '')
976 return fix_maintainer(self.name.strip())
978 __all__.append('Maintainer')
981 def get_or_set_maintainer(name, session=None):
983 Returns Maintainer object for given maintainer name.
985 If no matching maintainer name is found, a row is inserted.
988 @param name: The maintainer name to add
990 @type session: SQLAlchemy
991 @param session: Optional SQL session object (a temporary one will be
992 generated if not supplied). If not passed, a commit will be performed at
993 the end of the function, otherwise the caller is responsible for commiting.
994 A flush will be performed either way.
997 @return: the Maintainer object for the given maintainer
1000 q = session.query(Maintainer).filter_by(name=name)
1003 except NoResultFound:
1004 maintainer = Maintainer()
1005 maintainer.name = name
1006 session.add(maintainer)
1007 session.commit_or_flush()
1012 __all__.append('get_or_set_maintainer')
1015 def get_maintainer(maintainer_id, session=None):
1017 Return the name of the maintainer behind C{maintainer_id} or None if that
1018 maintainer_id is invalid.
1020 @type maintainer_id: int
1021 @param maintainer_id: the id of the maintainer
1024 @return: the Maintainer with this C{maintainer_id}
1027 return session.query(Maintainer).get(maintainer_id)
1029 __all__.append('get_maintainer')
1031 ################################################################################
1033 class NewComment(object):
1034 def __init__(self, *args, **kwargs):
1038 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1040 __all__.append('NewComment')
1043 def has_new_comment(package, version, session=None):
1045 Returns true if the given combination of C{package}, C{version} has a comment.
1047 @type package: string
1048 @param package: name of the package
1050 @type version: string
1051 @param version: package version
1053 @type session: Session
1054 @param session: Optional SQLA session object (a temporary one will be
1055 generated if not supplied)
1061 q = session.query(NewComment)
1062 q = q.filter_by(package=package)
1063 q = q.filter_by(version=version)
1065 return bool(q.count() > 0)
1067 __all__.append('has_new_comment')
1070 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1072 Returns (possibly empty) list of NewComment objects for the given
1075 @type package: string (optional)
1076 @param package: name of the package
1078 @type version: string (optional)
1079 @param version: package version
1081 @type comment_id: int (optional)
1082 @param comment_id: An id of a comment
1084 @type session: Session
1085 @param session: Optional SQLA session object (a temporary one will be
1086 generated if not supplied)
1089 @return: A (possibly empty) list of NewComment objects will be returned
1092 q = session.query(NewComment)
1093 if package is not None: q = q.filter_by(package=package)
1094 if version is not None: q = q.filter_by(version=version)
1095 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1099 __all__.append('get_new_comments')
1101 ################################################################################
1103 class Override(object):
1104 def __init__(self, *args, **kwargs):
1108 return '<Override %s (%s)>' % (self.package, self.suite_id)
1110 __all__.append('Override')
1113 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1115 Returns Override object for the given parameters
1117 @type package: string
1118 @param package: The name of the package
1120 @type suite: string, list or None
1121 @param suite: The name of the suite (or suites if a list) to limit to. If
1122 None, don't limit. Defaults to None.
1124 @type component: string, list or None
1125 @param component: The name of the component (or components if a list) to
1126 limit to. If None, don't limit. Defaults to None.
1128 @type overridetype: string, list or None
1129 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1130 limit to. If None, don't limit. Defaults to None.
1132 @type session: Session
1133 @param session: Optional SQLA session object (a temporary one will be
1134 generated if not supplied)
1137 @return: A (possibly empty) list of Override objects will be returned
1140 q = session.query(Override)
1141 q = q.filter_by(package=package)
1143 if suite is not None:
1144 if not isinstance(suite, list): suite = [suite]
1145 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1147 if component is not None:
1148 if not isinstance(component, list): component = [component]
1149 q = q.join(Component).filter(Component.component_name.in_(component))
1151 if overridetype is not None:
1152 if not isinstance(overridetype, list): overridetype = [overridetype]
1153 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1157 __all__.append('get_override')
1160 ################################################################################
1162 class OverrideType(object):
1163 def __init__(self, *args, **kwargs):
1167 return '<OverrideType %s>' % self.overridetype
1169 __all__.append('OverrideType')
1172 def get_override_type(override_type, session=None):
1174 Returns OverrideType object for given C{override type}.
1176 @type override_type: string
1177 @param override_type: The name of the override type
1179 @type session: Session
1180 @param session: Optional SQLA session object (a temporary one will be
1181 generated if not supplied)
1184 @return: the database id for the given override type
1187 q = session.query(OverrideType).filter_by(overridetype=override_type)
1191 except NoResultFound:
1194 __all__.append('get_override_type')
1196 ################################################################################
1198 class PendingContentAssociation(object):
1199 def __init__(self, *args, **kwargs):
1203 return '<PendingContentAssociation %s>' % self.pca_id
1205 __all__.append('PendingContentAssociation')
1207 def insert_pending_content_paths(package, fullpaths, session=None):
1209 Make sure given paths are temporarily associated with given
1213 @param package: the package to associate with should have been read in from the binary control file
1214 @type fullpaths: list
1215 @param fullpaths: the list of paths of the file being associated with the binary
1216 @type session: SQLAlchemy session
1217 @param session: Optional SQLAlchemy session. If this is passed, the caller
1218 is responsible for ensuring a transaction has begun and committing the
1219 results or rolling back based on the result code. If not passed, a commit
1220 will be performed at the end of the function
1222 @return: True upon success, False if there is a problem
1225 privatetrans = False
1228 session = DBConn().session()
1232 arch = get_architecture(package['Architecture'], session)
1233 arch_id = arch.arch_id
1235 # Remove any already existing recorded files for this package
1236 q = session.query(PendingContentAssociation)
1237 q = q.filter_by(package=package['Package'])
1238 q = q.filter_by(version=package['Version'])
1239 q = q.filter_by(architecture=arch_id)
1244 for fullpath in fullpaths:
1245 (path, file) = os.path.split(fullpath)
1247 if path.startswith( "./" ):
1250 filepath_id = get_or_set_contents_path_id(path, session)
1251 filename_id = get_or_set_contents_file_id(file, session)
1253 pathcache[fullpath] = (filepath_id, filename_id)
1255 for fullpath, dat in pathcache.items():
1256 pca = PendingContentAssociation()
1257 pca.package = package['Package']
1258 pca.version = package['Version']
1259 pca.filepath_id = dat[0]
1260 pca.filename_id = dat[1]
1261 pca.architecture = arch_id
1264 # Only commit if we set up the session ourself
1272 except Exception, e:
1273 traceback.print_exc()
1275 # Only rollback if we set up the session ourself
1282 __all__.append('insert_pending_content_paths')
1284 ################################################################################
1286 class Priority(object):
1287 def __init__(self, *args, **kwargs):
1290 def __eq__(self, val):
1291 if isinstance(val, str):
1292 return (self.priority == val)
1293 # This signals to use the normal comparison operator
1294 return NotImplemented
1296 def __ne__(self, val):
1297 if isinstance(val, str):
1298 return (self.priority != val)
1299 # This signals to use the normal comparison operator
1300 return NotImplemented
1303 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1305 __all__.append('Priority')
1308 def get_priority(priority, session=None):
1310 Returns Priority object for given C{priority name}.
1312 @type priority: string
1313 @param priority: The name of the priority
1315 @type session: Session
1316 @param session: Optional SQLA session object (a temporary one will be
1317 generated if not supplied)
1320 @return: Priority object for the given priority
1323 q = session.query(Priority).filter_by(priority=priority)
1327 except NoResultFound:
1330 __all__.append('get_priority')
1333 def get_priorities(session=None):
1335 Returns dictionary of priority names -> id mappings
1337 @type session: Session
1338 @param session: Optional SQL session object (a temporary one will be
1339 generated if not supplied)
1342 @return: dictionary of priority names -> id mappings
1346 q = session.query(Priority)
1348 ret[x.priority] = x.priority_id
1352 __all__.append('get_priorities')
1354 ################################################################################
1356 class Queue(object):
1357 def __init__(self, *args, **kwargs):
1361 return '<Queue %s>' % self.queue_name
1363 def autobuild_upload(self, changes, srcpath, session=None):
1365 Update queue_build database table used for incoming autobuild support.
1367 @type changes: Changes
1368 @param changes: changes object for the upload to process
1370 @type srcpath: string
1371 @param srcpath: path for the queue file entries/link destinations
1373 @type session: SQLAlchemy session
1374 @param session: Optional SQLAlchemy session. If this is passed, the
1375 caller is responsible for ensuring a transaction has begun and
1376 committing the results or rolling back based on the result code. If
1377 not passed, a commit will be performed at the end of the function,
1378 otherwise the caller is responsible for commiting.
1380 @rtype: NoneType or string
1381 @return: None if the operation failed, a string describing the error if not
1384 privatetrans = False
1386 session = DBConn().session()
1389 # TODO: Remove by moving queue config into the database
1392 for suitename in changes.changes["distribution"].keys():
1393 # TODO: Move into database as:
1394 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1395 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1396 # This also gets rid of the SecurityQueueBuild hack below
1397 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1401 s = get_suite(suitename, session)
1403 return "INTERNAL ERROR: Could not find suite %s" % suitename
1405 # TODO: Get from database as above
1406 dest_dir = conf["Dir::QueueBuild"]
1408 # TODO: Move into database as above
1409 if conf.FindB("Dinstall::SecurityQueueBuild"):
1410 dest_dir = os.path.join(dest_dir, suitename)
1412 for file_entry in changes.files.keys():
1413 src = os.path.join(srcpath, file_entry)
1414 dest = os.path.join(dest_dir, file_entry)
1416 # TODO: Move into database as above
1417 if conf.FindB("Dinstall::SecurityQueueBuild"):
1418 # Copy it since the original won't be readable by www-data
1420 utils.copy(src, dest)
1422 # Create a symlink to it
1423 os.symlink(src, dest)
1426 qb.suite_id = s.suite_id
1427 qb.queue_id = self.queue_id
1433 # If the .orig tarballs are in the pool, create a symlink to
1434 # them (if one doesn't already exist)
1435 for dsc_file in changes.dsc_files.keys():
1436 # Skip all files except orig tarballs
1437 from daklib.regexes import re_is_orig_source
1438 if not re_is_orig_source.match(dsc_file):
1440 # Skip orig files not identified in the pool
1441 if not (changes.orig_files.has_key(dsc_file) and
1442 changes.orig_files[dsc_file].has_key("id")):
1444 orig_file_id = changes.orig_files[dsc_file]["id"]
1445 dest = os.path.join(dest_dir, dsc_file)
1447 # If it doesn't exist, create a symlink
1448 if not os.path.exists(dest):
1449 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1450 {'id': orig_file_id})
1453 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1455 src = os.path.join(res[0], res[1])
1456 os.symlink(src, dest)
1458 # Add it to the list of packages for later processing by apt-ftparchive
1460 qb.suite_id = s.suite_id
1461 qb.queue_id = self.queue_id
1466 # If it does, update things to ensure it's not removed prematurely
1468 qb = get_queue_build(dest, s.suite_id, session)
1480 __all__.append('Queue')
1483 def get_or_set_queue(queuename, session=None):
1485 Returns Queue object for given C{queue name}, creating it if it does not
1488 @type queuename: string
1489 @param queuename: The name of the queue
1491 @type session: Session
1492 @param session: Optional SQLA session object (a temporary one will be
1493 generated if not supplied)
1496 @return: Queue object for the given queue
1499 q = session.query(Queue).filter_by(queue_name=queuename)
1503 except NoResultFound:
1505 queue.queue_name = queuename
1507 session.commit_or_flush()
1512 __all__.append('get_or_set_queue')
1514 ################################################################################
1516 class QueueBuild(object):
1517 def __init__(self, *args, **kwargs):
1521 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1523 __all__.append('QueueBuild')
1526 def get_queue_build(filename, suite, session=None):
1528 Returns QueueBuild object for given C{filename} and C{suite}.
1530 @type filename: string
1531 @param filename: The name of the file
1533 @type suiteid: int or str
1534 @param suiteid: Suite name or ID
1536 @type session: Session
1537 @param session: Optional SQLA session object (a temporary one will be
1538 generated if not supplied)
1541 @return: Queue object for the given queue
1544 if isinstance(suite, int):
1545 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1547 q = session.query(QueueBuild).filter_by(filename=filename)
1548 q = q.join(Suite).filter_by(suite_name=suite)
1552 except NoResultFound:
1555 __all__.append('get_queue_build')
1557 ################################################################################
1559 class Section(object):
1560 def __init__(self, *args, **kwargs):
1563 def __eq__(self, val):
1564 if isinstance(val, str):
1565 return (self.section == val)
1566 # This signals to use the normal comparison operator
1567 return NotImplemented
1569 def __ne__(self, val):
1570 if isinstance(val, str):
1571 return (self.section != val)
1572 # This signals to use the normal comparison operator
1573 return NotImplemented
1576 return '<Section %s>' % self.section
1578 __all__.append('Section')
1581 def get_section(section, session=None):
1583 Returns Section object for given C{section name}.
1585 @type section: string
1586 @param section: The name of the section
1588 @type session: Session
1589 @param session: Optional SQLA session object (a temporary one will be
1590 generated if not supplied)
1593 @return: Section object for the given section name
1596 q = session.query(Section).filter_by(section=section)
1600 except NoResultFound:
1603 __all__.append('get_section')
1606 def get_sections(session=None):
1608 Returns dictionary of section names -> id mappings
1610 @type session: Session
1611 @param session: Optional SQL session object (a temporary one will be
1612 generated if not supplied)
1615 @return: dictionary of section names -> id mappings
1619 q = session.query(Section)
1621 ret[x.section] = x.section_id
1625 __all__.append('get_sections')
1627 ################################################################################
1629 class DBSource(object):
1630 def __init__(self, *args, **kwargs):
1634 return '<DBSource %s (%s)>' % (self.source, self.version)
1636 __all__.append('DBSource')
1639 def source_exists(source, source_version, suites = ["any"], session=None):
1641 Ensure that source exists somewhere in the archive for the binary
1642 upload being processed.
1643 1. exact match => 1.0-3
1644 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1646 @type package: string
1647 @param package: package source name
1649 @type source_version: string
1650 @param source_version: expected source version
1653 @param suites: list of suites to check in, default I{any}
1655 @type session: Session
1656 @param session: Optional SQLA session object (a temporary one will be
1657 generated if not supplied)
1660 @return: returns 1 if a source with expected version is found, otherwise 0
1667 for suite in suites:
1668 q = session.query(DBSource).filter_by(source=source)
1670 # source must exist in suite X, or in some other suite that's
1671 # mapped to X, recursively... silent-maps are counted too,
1672 # unreleased-maps aren't.
1673 maps = cnf.ValueList("SuiteMappings")[:]
1675 maps = [ m.split() for m in maps ]
1676 maps = [ (x[1], x[2]) for x in maps
1677 if x[0] == "map" or x[0] == "silent-map" ]
1680 if x[1] in s and x[0] not in s:
1683 q = q.join(SrcAssociation).join(Suite)
1684 q = q.filter(Suite.suite_name.in_(s))
1686 # Reduce the query results to a list of version numbers
1687 ql = [ j.version for j in q.all() ]
1690 if source_version in ql:
1694 from daklib.regexes import re_bin_only_nmu
1695 orig_source_version = re_bin_only_nmu.sub('', source_version)
1696 if orig_source_version in ql:
1699 # No source found so return not ok
1704 __all__.append('source_exists')
1707 def get_suites_source_in(source, session=None):
1709 Returns list of Suite objects which given C{source} name is in
1712 @param source: DBSource package name to search for
1715 @return: list of Suite objects for the given source
1718 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1720 __all__.append('get_suites_source_in')
1723 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1725 Returns list of DBSource objects for given C{source} name and other parameters
1728 @param source: DBSource package name to search for
1730 @type source: str or None
1731 @param source: DBSource version name to search for or None if not applicable
1733 @type dm_upload_allowed: bool
1734 @param dm_upload_allowed: If None, no effect. If True or False, only
1735 return packages with that dm_upload_allowed setting
1737 @type session: Session
1738 @param session: Optional SQL session object (a temporary one will be
1739 generated if not supplied)
1742 @return: list of DBSource objects for the given name (may be empty)
1745 q = session.query(DBSource).filter_by(source=source)
1747 if version is not None:
1748 q = q.filter_by(version=version)
1750 if dm_upload_allowed is not None:
1751 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1755 __all__.append('get_sources_from_name')
1758 def get_source_in_suite(source, suite, session=None):
1760 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1762 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1763 - B{suite} - a suite name, eg. I{unstable}
1765 @type source: string
1766 @param source: source package name
1769 @param suite: the suite name
1772 @return: the version for I{source} in I{suite}
1776 q = session.query(SrcAssociation)
1777 q = q.join('source').filter_by(source=source)
1778 q = q.join('suite').filter_by(suite_name=suite)
1781 return q.one().source
1782 except NoResultFound:
1785 __all__.append('get_source_in_suite')
1787 ################################################################################
1789 class SourceACL(object):
1790 def __init__(self, *args, **kwargs):
1793 __all__.append('SourceACL')
1795 ################################################################################
1797 class SrcAssociation(object):
1798 def __init__(self, *args, **kwargs):
1802 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1804 __all__.append('SrcAssociation')
1806 ################################################################################
1808 class SrcFormat(object):
1809 def __init__(self, *args, **kwargs):
1813 return '<SrcFormat %s>' % (self.format_name)
1815 __all__.append('SrcFormat')
1817 ################################################################################
1819 class SrcUploader(object):
1820 def __init__(self, *args, **kwargs):
1824 return '<SrcUploader %s>' % self.uploader_id
1826 __all__.append('SrcUploader')
1828 ################################################################################
1830 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1831 ('SuiteID', 'suite_id'),
1832 ('Version', 'version'),
1833 ('Origin', 'origin'),
1835 ('Description', 'description'),
1836 ('Untouchable', 'untouchable'),
1837 ('Announce', 'announce'),
1838 ('Codename', 'codename'),
1839 ('OverrideCodename', 'overridecodename'),
1840 ('ValidTime', 'validtime'),
1841 ('Priority', 'priority'),
1842 ('NotAutomatic', 'notautomatic'),
1843 ('CopyChanges', 'copychanges'),
1844 ('CopyDotDak', 'copydotdak'),
1845 ('CommentsDir', 'commentsdir'),
1846 ('OverrideSuite', 'overridesuite'),
1847 ('ChangelogBase', 'changelogbase')]
1850 class Suite(object):
1851 def __init__(self, *args, **kwargs):
1855 return '<Suite %s>' % self.suite_name
1857 def __eq__(self, val):
1858 if isinstance(val, str):
1859 return (self.suite_name == val)
1860 # This signals to use the normal comparison operator
1861 return NotImplemented
1863 def __ne__(self, val):
1864 if isinstance(val, str):
1865 return (self.suite_name != val)
1866 # This signals to use the normal comparison operator
1867 return NotImplemented
1871 for disp, field in SUITE_FIELDS:
1872 val = getattr(self, field, None)
1874 ret.append("%s: %s" % (disp, val))
1876 return "\n".join(ret)
1878 __all__.append('Suite')
1881 def get_suite_architecture(suite, architecture, session=None):
1883 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1887 @param suite: Suite name to search for
1889 @type architecture: str
1890 @param architecture: Architecture name to search for
1892 @type session: Session
1893 @param session: Optional SQL session object (a temporary one will be
1894 generated if not supplied)
1896 @rtype: SuiteArchitecture
1897 @return: the SuiteArchitecture object or None
1900 q = session.query(SuiteArchitecture)
1901 q = q.join(Architecture).filter_by(arch_string=architecture)
1902 q = q.join(Suite).filter_by(suite_name=suite)
1906 except NoResultFound:
1909 __all__.append('get_suite_architecture')
1912 def get_suite(suite, session=None):
1914 Returns Suite object for given C{suite name}.
1917 @param suite: The name of the suite
1919 @type session: Session
1920 @param session: Optional SQLA session object (a temporary one will be
1921 generated if not supplied)
1924 @return: Suite object for the requested suite name (None if not present)
1927 q = session.query(Suite).filter_by(suite_name=suite)
1931 except NoResultFound:
1934 __all__.append('get_suite')
1936 ################################################################################
1938 class SuiteArchitecture(object):
1939 def __init__(self, *args, **kwargs):
1943 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1945 __all__.append('SuiteArchitecture')
1948 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1950 Returns list of Architecture objects for given C{suite} name
1953 @param source: Suite name to search for
1955 @type skipsrc: boolean
1956 @param skipsrc: Whether to skip returning the 'source' architecture entry
1959 @type skipall: boolean
1960 @param skipall: Whether to skip returning the 'all' architecture entry
1963 @type session: Session
1964 @param session: Optional SQL session object (a temporary one will be
1965 generated if not supplied)
1968 @return: list of Architecture objects for the given name (may be empty)
1971 q = session.query(Architecture)
1972 q = q.join(SuiteArchitecture)
1973 q = q.join(Suite).filter_by(suite_name=suite)
1976 q = q.filter(Architecture.arch_string != 'source')
1979 q = q.filter(Architecture.arch_string != 'all')
1981 q = q.order_by('arch_string')
1985 __all__.append('get_suite_architectures')
1987 ################################################################################
1989 class SuiteSrcFormat(object):
1990 def __init__(self, *args, **kwargs):
1994 return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1996 __all__.append('SuiteSrcFormat')
1999 def get_suite_src_formats(suite, session=None):
2001 Returns list of allowed SrcFormat for C{suite}.
2004 @param suite: Suite name to search for
2006 @type session: Session
2007 @param session: Optional SQL session object (a temporary one will be
2008 generated if not supplied)
2011 @return: the list of allowed source formats for I{suite}
2014 q = session.query(SrcFormat)
2015 q = q.join(SuiteSrcFormat)
2016 q = q.join(Suite).filter_by(suite_name=suite)
2017 q = q.order_by('format_name')
2021 __all__.append('get_suite_src_formats')
2023 ################################################################################
2026 def __init__(self, *args, **kwargs):
2029 def __eq__(self, val):
2030 if isinstance(val, str):
2031 return (self.uid == val)
2032 # This signals to use the normal comparison operator
2033 return NotImplemented
2035 def __ne__(self, val):
2036 if isinstance(val, str):
2037 return (self.uid != val)
2038 # This signals to use the normal comparison operator
2039 return NotImplemented
2042 return '<Uid %s (%s)>' % (self.uid, self.name)
2044 __all__.append('Uid')
2047 def add_database_user(uidname, session=None):
2049 Adds a database user
2051 @type uidname: string
2052 @param uidname: The uid of the user 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 session.execute("CREATE USER :uid", {'uid': uidname})
2064 session.commit_or_flush()
2066 __all__.append('add_database_user')
2069 def get_or_set_uid(uidname, session=None):
2071 Returns uid object for given uidname.
2073 If no matching uidname is found, a row is inserted.
2075 @type uidname: string
2076 @param uidname: The uid to add
2078 @type session: SQLAlchemy
2079 @param session: Optional SQL session object (a temporary one will be
2080 generated if not supplied). If not passed, a commit will be performed at
2081 the end of the function, otherwise the caller is responsible for commiting.
2084 @return: the uid object for the given uidname
2087 q = session.query(Uid).filter_by(uid=uidname)
2091 except NoResultFound:
2095 session.commit_or_flush()
2100 __all__.append('get_or_set_uid')
2103 def get_uid_from_fingerprint(fpr, session=None):
2104 q = session.query(Uid)
2105 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2109 except NoResultFound:
2112 __all__.append('get_uid_from_fingerprint')
2114 ################################################################################
2116 class UploadBlock(object):
2117 def __init__(self, *args, **kwargs):
2120 __all__.append('UploadBlock')
2122 ################################################################################
2124 class DBConn(Singleton):
2126 database module init.
2128 def __init__(self, *args, **kwargs):
2129 super(DBConn, self).__init__(*args, **kwargs)
2131 def _startup(self, *args, **kwargs):
2133 if kwargs.has_key('debug'):
2137 def __setuptables(self):
2138 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2139 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2140 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2141 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2142 self.tbl_binary_acl = Table('binary_acl', self.db_meta, autoload=True)
2143 self.tbl_binary_acl_map = Table('binary_acl_map', self.db_meta, autoload=True)
2144 self.tbl_component = Table('component', self.db_meta, autoload=True)
2145 self.tbl_config = Table('config', self.db_meta, autoload=True)
2146 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2147 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2148 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2149 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2150 self.tbl_files = Table('files', self.db_meta, autoload=True)
2151 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2152 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2153 self.tbl_location = Table('location', self.db_meta, autoload=True)
2154 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2155 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2156 self.tbl_override = Table('override', self.db_meta, autoload=True)
2157 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2158 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2159 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2160 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2161 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2162 self.tbl_section = Table('section', self.db_meta, autoload=True)
2163 self.tbl_source = Table('source', self.db_meta, autoload=True)
2164 self.tbl_source_acl = Table('source_acl', self.db_meta, autoload=True)
2165 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2166 self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2167 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2168 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2169 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2170 self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2171 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2172 self.tbl_upload_blocks = Table('upload_blocks', self.db_meta, autoload=True)
2174 def __setupmappers(self):
2175 mapper(Architecture, self.tbl_architecture,
2176 properties = dict(arch_id = self.tbl_architecture.c.id))
2178 mapper(Archive, self.tbl_archive,
2179 properties = dict(archive_id = self.tbl_archive.c.id,
2180 archive_name = self.tbl_archive.c.name))
2182 mapper(BinAssociation, self.tbl_bin_associations,
2183 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2184 suite_id = self.tbl_bin_associations.c.suite,
2185 suite = relation(Suite),
2186 binary_id = self.tbl_bin_associations.c.bin,
2187 binary = relation(DBBinary)))
2189 mapper(DBBinary, self.tbl_binaries,
2190 properties = dict(binary_id = self.tbl_binaries.c.id,
2191 package = self.tbl_binaries.c.package,
2192 version = self.tbl_binaries.c.version,
2193 maintainer_id = self.tbl_binaries.c.maintainer,
2194 maintainer = relation(Maintainer),
2195 source_id = self.tbl_binaries.c.source,
2196 source = relation(DBSource),
2197 arch_id = self.tbl_binaries.c.architecture,
2198 architecture = relation(Architecture),
2199 poolfile_id = self.tbl_binaries.c.file,
2200 poolfile = relation(PoolFile),
2201 binarytype = self.tbl_binaries.c.type,
2202 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2203 fingerprint = relation(Fingerprint),
2204 install_date = self.tbl_binaries.c.install_date,
2205 binassociations = relation(BinAssociation,
2206 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2208 mapper(BinaryACL, self.tbl_binary_acl,
2209 properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2211 mapper(BinaryACLMap, self.tbl_binary_acl_map,
2212 properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id))
2214 mapper(Component, self.tbl_component,
2215 properties = dict(component_id = self.tbl_component.c.id,
2216 component_name = self.tbl_component.c.name))
2218 mapper(DBConfig, self.tbl_config,
2219 properties = dict(config_id = self.tbl_config.c.id))
2221 mapper(ContentAssociation, self.tbl_content_associations,
2222 properties = dict(ca_id = self.tbl_content_associations.c.id,
2223 filename_id = self.tbl_content_associations.c.filename,
2224 filename = relation(ContentFilename),
2225 filepath_id = self.tbl_content_associations.c.filepath,
2226 filepath = relation(ContentFilepath),
2227 binary_id = self.tbl_content_associations.c.binary_pkg,
2228 binary = relation(DBBinary)))
2231 mapper(ContentFilename, self.tbl_content_file_names,
2232 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2233 filename = self.tbl_content_file_names.c.file))
2235 mapper(ContentFilepath, self.tbl_content_file_paths,
2236 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2237 filepath = self.tbl_content_file_paths.c.path))
2239 mapper(DSCFile, self.tbl_dsc_files,
2240 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2241 source_id = self.tbl_dsc_files.c.source,
2242 source = relation(DBSource),
2243 poolfile_id = self.tbl_dsc_files.c.file,
2244 poolfile = relation(PoolFile)))
2246 mapper(PoolFile, self.tbl_files,
2247 properties = dict(file_id = self.tbl_files.c.id,
2248 filesize = self.tbl_files.c.size,
2249 location_id = self.tbl_files.c.location,
2250 location = relation(Location)))
2252 mapper(Fingerprint, self.tbl_fingerprint,
2253 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2254 uid_id = self.tbl_fingerprint.c.uid,
2255 uid = relation(Uid),
2256 keyring_id = self.tbl_fingerprint.c.keyring,
2257 keyring = relation(Keyring)))
2259 mapper(Keyring, self.tbl_keyrings,
2260 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2261 keyring_id = self.tbl_keyrings.c.id))
2263 mapper(Location, self.tbl_location,
2264 properties = dict(location_id = self.tbl_location.c.id,
2265 component_id = self.tbl_location.c.component,
2266 component = relation(Component),
2267 archive_id = self.tbl_location.c.archive,
2268 archive = relation(Archive),
2269 archive_type = self.tbl_location.c.type))
2271 mapper(Maintainer, self.tbl_maintainer,
2272 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2274 mapper(NewComment, self.tbl_new_comments,
2275 properties = dict(comment_id = self.tbl_new_comments.c.id))
2277 mapper(Override, self.tbl_override,
2278 properties = dict(suite_id = self.tbl_override.c.suite,
2279 suite = relation(Suite),
2280 component_id = self.tbl_override.c.component,
2281 component = relation(Component),
2282 priority_id = self.tbl_override.c.priority,
2283 priority = relation(Priority),
2284 section_id = self.tbl_override.c.section,
2285 section = relation(Section),
2286 overridetype_id = self.tbl_override.c.type,
2287 overridetype = relation(OverrideType)))
2289 mapper(OverrideType, self.tbl_override_type,
2290 properties = dict(overridetype = self.tbl_override_type.c.type,
2291 overridetype_id = self.tbl_override_type.c.id))
2293 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2294 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2295 filepath_id = self.tbl_pending_content_associations.c.filepath,
2296 filepath = relation(ContentFilepath),
2297 filename_id = self.tbl_pending_content_associations.c.filename,
2298 filename = relation(ContentFilename)))
2300 mapper(Priority, self.tbl_priority,
2301 properties = dict(priority_id = self.tbl_priority.c.id))
2303 mapper(Queue, self.tbl_queue,
2304 properties = dict(queue_id = self.tbl_queue.c.id))
2306 mapper(QueueBuild, self.tbl_queue_build,
2307 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2308 queue_id = self.tbl_queue_build.c.queue,
2309 queue = relation(Queue, backref='queuebuild')))
2311 mapper(Section, self.tbl_section,
2312 properties = dict(section_id = self.tbl_section.c.id))
2314 mapper(DBSource, self.tbl_source,
2315 properties = dict(source_id = self.tbl_source.c.id,
2316 version = self.tbl_source.c.version,
2317 maintainer_id = self.tbl_source.c.maintainer,
2318 maintainer = relation(Maintainer,
2319 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2320 poolfile_id = self.tbl_source.c.file,
2321 poolfile = relation(PoolFile),
2322 fingerprint_id = self.tbl_source.c.sig_fpr,
2323 fingerprint = relation(Fingerprint),
2324 changedby_id = self.tbl_source.c.changedby,
2325 changedby = relation(Maintainer,
2326 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2327 srcfiles = relation(DSCFile,
2328 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2329 srcassociations = relation(SrcAssociation,
2330 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2332 mapper(SourceACL, self.tbl_source_acl,
2333 properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2335 mapper(SrcAssociation, self.tbl_src_associations,
2336 properties = dict(sa_id = self.tbl_src_associations.c.id,
2337 suite_id = self.tbl_src_associations.c.suite,
2338 suite = relation(Suite),
2339 source_id = self.tbl_src_associations.c.source,
2340 source = relation(DBSource)))
2342 mapper(SrcFormat, self.tbl_src_format,
2343 properties = dict(src_format_id = self.tbl_src_format.c.id,
2344 format_name = self.tbl_src_format.c.format_name))
2346 mapper(SrcUploader, self.tbl_src_uploaders,
2347 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2348 source_id = self.tbl_src_uploaders.c.source,
2349 source = relation(DBSource,
2350 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2351 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2352 maintainer = relation(Maintainer,
2353 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2355 mapper(Suite, self.tbl_suite,
2356 properties = dict(suite_id = self.tbl_suite.c.id))
2358 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2359 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2360 suite = relation(Suite, backref='suitearchitectures'),
2361 arch_id = self.tbl_suite_architectures.c.architecture,
2362 architecture = relation(Architecture)))
2364 mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2365 properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2366 suite = relation(Suite, backref='suitesrcformats'),
2367 src_format_id = self.tbl_suite_src_formats.c.src_format,
2368 src_format = relation(SrcFormat)))
2370 mapper(Uid, self.tbl_uid,
2371 properties = dict(uid_id = self.tbl_uid.c.id,
2372 fingerprint = relation(Fingerprint)))
2374 mapper(UploadBlock, self.tbl_upload_blocks,
2375 properties = dict(upload_block_id = self.tbl_upload_blocks.c.id))
2377 ## Connection functions
2378 def __createconn(self):
2379 from config import Config
2383 connstr = "postgres://%s" % cnf["DB::Host"]
2384 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2385 connstr += ":%s" % cnf["DB::Port"]
2386 connstr += "/%s" % cnf["DB::Name"]
2389 connstr = "postgres:///%s" % cnf["DB::Name"]
2390 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2391 connstr += "?port=%s" % cnf["DB::Port"]
2393 self.db_pg = create_engine(connstr, echo=self.debug)
2394 self.db_meta = MetaData()
2395 self.db_meta.bind = self.db_pg
2396 self.db_smaker = sessionmaker(bind=self.db_pg,
2400 self.__setuptables()
2401 self.__setupmappers()
2404 return self.db_smaker()
2406 __all__.append('DBConn')