5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from inspect import getargspec
42 from sqlalchemy import create_engine, Table, MetaData, select
43 from sqlalchemy.orm import sessionmaker, mapper, relation
45 # Don't remove this, we re-export the exceptions to scripts which import us
46 from sqlalchemy.exc import *
47 from sqlalchemy.orm.exc import NoResultFound
49 # Only import Config until Queue stuff is changed to store its config
51 from config import Config
52 from singleton import Singleton
53 from textutils import fix_maintainer
55 ################################################################################
57 __all__ = ['IntegrityError', 'SQLAlchemyError']
59 ################################################################################
61 def session_wrapper(fn):
63 Wrapper around common ".., session=None):" handling. If the wrapped
64 function is called without passing 'session', we create a local one
65 and destroy it when the function ends.
68 def wrapped(*args, **kwargs):
69 private_transaction = False
70 session = kwargs.get('session')
72 # No session specified as last argument or in kwargs, create one.
73 if session is None and len(args) <= len(getargspec(fn)[0]) - 1:
74 private_transaction = True
75 kwargs['session'] = DBConn().session()
78 return fn(*args, **kwargs)
80 if private_transaction:
81 # We created a session; close it.
82 kwargs['session'].close()
84 wrapped.__doc__ = fn.__doc__
85 wrapped.func_name = fn.func_name
89 ################################################################################
91 class Architecture(object):
92 def __init__(self, *args, **kwargs):
95 def __eq__(self, val):
96 if isinstance(val, str):
97 return (self.arch_string== val)
98 # This signals to use the normal comparison operator
101 def __ne__(self, val):
102 if isinstance(val, str):
103 return (self.arch_string != val)
104 # This signals to use the normal comparison operator
105 return NotImplemented
108 return '<Architecture %s>' % self.arch_string
110 __all__.append('Architecture')
113 def get_architecture(architecture, session=None):
115 Returns database id for given C{architecture}.
117 @type architecture: string
118 @param architecture: The name of the architecture
120 @type session: Session
121 @param session: Optional SQLA session object (a temporary one will be
122 generated if not supplied)
125 @return: Architecture object for the given arch (None if not present)
128 q = session.query(Architecture).filter_by(arch_string=architecture)
132 except NoResultFound:
135 __all__.append('get_architecture')
138 def get_architecture_suites(architecture, session=None):
140 Returns list of Suite objects for given C{architecture} name
143 @param source: Architecture name to search for
145 @type session: Session
146 @param session: Optional SQL session object (a temporary one will be
147 generated if not supplied)
150 @return: list of Suite objects for the given name (may be empty)
153 q = session.query(Suite)
154 q = q.join(SuiteArchitecture)
155 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
161 __all__.append('get_architecture_suites')
163 ################################################################################
165 class Archive(object):
166 def __init__(self, *args, **kwargs):
170 return '<Archive %s>' % self.archive_name
172 __all__.append('Archive')
175 def get_archive(archive, session=None):
177 returns database id for given C{archive}.
179 @type archive: string
180 @param archive: the name of the arhive
182 @type session: Session
183 @param session: Optional SQLA session object (a temporary one will be
184 generated if not supplied)
187 @return: Archive object for the given name (None if not present)
190 archive = archive.lower()
192 q = session.query(Archive).filter_by(archive_name=archive)
196 except NoResultFound:
199 __all__.append('get_archive')
201 ################################################################################
203 class BinAssociation(object):
204 def __init__(self, *args, **kwargs):
208 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
210 __all__.append('BinAssociation')
212 ################################################################################
214 class DBBinary(object):
215 def __init__(self, *args, **kwargs):
219 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
221 __all__.append('DBBinary')
224 def get_suites_binary_in(package, session=None):
226 Returns list of Suite objects which given C{package} name is in
229 @param source: DBBinary package name to search for
232 @return: list of Suite objects for the given package
235 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
237 __all__.append('get_suites_binary_in')
240 def get_binary_from_id(id, session=None):
242 Returns DBBinary object for given C{id}
245 @param id: Id of the required binary
247 @type session: Session
248 @param session: Optional SQLA session object (a temporary one will be
249 generated if not supplied)
252 @return: DBBinary object for the given binary (None if not present)
255 q = session.query(DBBinary).filter_by(binary_id=id)
259 except NoResultFound:
262 __all__.append('get_binary_from_id')
265 def get_binaries_from_name(package, version=None, architecture=None, session=None):
267 Returns list of DBBinary objects for given C{package} name
270 @param package: DBBinary package name to search for
272 @type version: str or None
273 @param version: Version to search for (or None)
275 @type package: str, list or None
276 @param package: Architectures to limit to (or None if no limit)
278 @type session: Session
279 @param session: Optional SQL session object (a temporary one will be
280 generated if not supplied)
283 @return: list of DBBinary objects for the given name (may be empty)
286 q = session.query(DBBinary).filter_by(package=package)
288 if version is not None:
289 q = q.filter_by(version=version)
291 if architecture is not None:
292 if not isinstance(architecture, list):
293 architecture = [architecture]
294 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
300 __all__.append('get_binaries_from_name')
303 def get_binaries_from_source_id(source_id, session=None):
305 Returns list of DBBinary objects for given C{source_id}
308 @param source_id: source_id to search for
310 @type session: Session
311 @param session: Optional SQL session object (a temporary one will be
312 generated if not supplied)
315 @return: list of DBBinary objects for the given name (may be empty)
318 return session.query(DBBinary).filter_by(source_id=source_id).all()
320 __all__.append('get_binaries_from_source_id')
323 def get_binary_from_name_suite(package, suitename, session=None):
324 ### For dak examine-package
325 ### XXX: Doesn't use object API yet
327 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
328 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
329 WHERE b.package=:package
331 AND fi.location = l.id
332 AND l.component = c.id
335 AND su.suite_name=:suitename
336 ORDER BY b.version DESC"""
338 return session.execute(sql, {'package': package, 'suitename': suitename})
340 __all__.append('get_binary_from_name_suite')
343 def get_binary_components(package, suitename, arch, session=None):
344 # Check for packages that have moved from one component to another
345 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
346 WHERE b.package=:package AND s.suite_name=:suitename
347 AND (a.arch_string = :arch OR a.arch_string = 'all')
348 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
349 AND f.location = l.id
350 AND l.component = c.id
353 vals = {'package': package, 'suitename': suitename, 'arch': arch}
355 return session.execute(query, vals)
357 __all__.append('get_binary_components')
359 ################################################################################
361 class Component(object):
362 def __init__(self, *args, **kwargs):
365 def __eq__(self, val):
366 if isinstance(val, str):
367 return (self.component_name == val)
368 # This signals to use the normal comparison operator
369 return NotImplemented
371 def __ne__(self, val):
372 if isinstance(val, str):
373 return (self.component_name != val)
374 # This signals to use the normal comparison operator
375 return NotImplemented
378 return '<Component %s>' % self.component_name
381 __all__.append('Component')
384 def get_component(component, session=None):
386 Returns database id for given C{component}.
388 @type component: string
389 @param component: The name of the override type
392 @return: the database id for the given component
395 component = component.lower()
397 q = session.query(Component).filter_by(component_name=component)
401 except NoResultFound:
404 __all__.append('get_component')
406 ################################################################################
408 class DBConfig(object):
409 def __init__(self, *args, **kwargs):
413 return '<DBConfig %s>' % self.name
415 __all__.append('DBConfig')
417 ################################################################################
419 class ContentFilename(object):
420 def __init__(self, *args, **kwargs):
424 return '<ContentFilename %s>' % self.filename
426 __all__.append('ContentFilename')
428 def get_or_set_contents_file_id(filename, session=None):
430 Returns database id for given filename.
432 If no matching file is found, a row is inserted.
434 @type filename: string
435 @param filename: The filename
436 @type session: SQLAlchemy
437 @param session: Optional SQL session object (a temporary one will be
438 generated if not supplied). If not passed, a commit will be performed at
439 the end of the function, otherwise the caller is responsible for commiting.
442 @return: the database id for the given component
446 session = DBConn().session()
449 q = session.query(ContentFilename).filter_by(filename=filename)
452 ret = q.one().cafilename_id
453 except NoResultFound:
454 cf = ContentFilename()
455 cf.filename = filename
461 ret = cf.cafilename_id
468 __all__.append('get_or_set_contents_file_id')
471 def get_contents(suite, overridetype, section=None, session=None):
473 Returns contents for a suite / overridetype combination, limiting
474 to a section if not None.
477 @param suite: Suite object
479 @type overridetype: OverrideType
480 @param overridetype: OverrideType object
482 @type section: Section
483 @param section: Optional section object to limit results to
485 @type session: SQLAlchemy
486 @param session: Optional SQL session object (a temporary one will be
487 generated if not supplied)
490 @return: ResultsProxy object set up to return tuples of (filename, section,
494 # find me all of the contents for a given suite
495 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
499 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
500 JOIN content_file_names n ON (c.filename=n.id)
501 JOIN binaries b ON (b.id=c.binary_pkg)
502 JOIN override o ON (o.package=b.package)
503 JOIN section s ON (s.id=o.section)
504 WHERE o.suite = :suiteid AND o.type = :overridetypeid
505 AND b.type=:overridetypename"""
507 vals = {'suiteid': suite.suite_id,
508 'overridetypeid': overridetype.overridetype_id,
509 'overridetypename': overridetype.overridetype}
511 if section is not None:
512 contents_q += " AND s.id = :sectionid"
513 vals['sectionid'] = section.section_id
515 contents_q += " ORDER BY fn"
517 return session.execute(contents_q, vals)
519 __all__.append('get_contents')
521 ################################################################################
523 class ContentFilepath(object):
524 def __init__(self, *args, **kwargs):
528 return '<ContentFilepath %s>' % self.filepath
530 __all__.append('ContentFilepath')
532 def get_or_set_contents_path_id(filepath, session=None):
534 Returns database id for given path.
536 If no matching file is found, a row is inserted.
538 @type filename: string
539 @param filename: The filepath
540 @type session: SQLAlchemy
541 @param session: Optional SQL session object (a temporary one will be
542 generated if not supplied). If not passed, a commit will be performed at
543 the end of the function, otherwise the caller is responsible for commiting.
546 @return: the database id for the given path
550 session = DBConn().session()
553 q = session.query(ContentFilepath).filter_by(filepath=filepath)
556 ret = q.one().cafilepath_id
557 except NoResultFound:
558 cf = ContentFilepath()
559 cf.filepath = filepath
565 ret = cf.cafilepath_id
572 __all__.append('get_or_set_contents_path_id')
574 ################################################################################
576 class ContentAssociation(object):
577 def __init__(self, *args, **kwargs):
581 return '<ContentAssociation %s>' % self.ca_id
583 __all__.append('ContentAssociation')
585 def insert_content_paths(binary_id, fullpaths, session=None):
587 Make sure given path is associated with given binary id
590 @param binary_id: the id of the binary
591 @type fullpaths: list
592 @param fullpaths: the list of paths of the file being associated with the binary
593 @type session: SQLAlchemy session
594 @param session: Optional SQLAlchemy session. If this is passed, the caller
595 is responsible for ensuring a transaction has begun and committing the
596 results or rolling back based on the result code. If not passed, a commit
597 will be performed at the end of the function, otherwise the caller is
598 responsible for commiting.
600 @return: True upon success
605 session = DBConn().session()
611 for fullpath in fullpaths:
612 # Get the necessary IDs ...
613 (path, file) = os.path.split(fullpath)
615 filepath_id = get_or_set_contents_path_id(path, session)
616 filename_id = get_or_set_contents_file_id(file, session)
618 pathcache[fullpath] = (filepath_id, filename_id)
620 for fullpath, dat in pathcache.items():
621 ca = ContentAssociation()
622 ca.binary_id = binary_id
623 ca.filepath_id = dat[0]
624 ca.filename_id = dat[1]
627 # Only commit if we set up the session ourself
637 traceback.print_exc()
639 # Only rollback if we set up the session ourself
646 __all__.append('insert_content_paths')
648 ################################################################################
650 class DSCFile(object):
651 def __init__(self, *args, **kwargs):
655 return '<DSCFile %s>' % self.dscfile_id
657 __all__.append('DSCFile')
660 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
662 Returns a list of DSCFiles which may be empty
664 @type dscfile_id: int (optional)
665 @param dscfile_id: the dscfile_id of the DSCFiles to find
667 @type source_id: int (optional)
668 @param source_id: the source id related to the DSCFiles to find
670 @type poolfile_id: int (optional)
671 @param poolfile_id: the poolfile id related to the DSCFiles to find
674 @return: Possibly empty list of DSCFiles
677 q = session.query(DSCFile)
679 if dscfile_id is not None:
680 q = q.filter_by(dscfile_id=dscfile_id)
682 if source_id is not None:
683 q = q.filter_by(source_id=source_id)
685 if poolfile_id is not None:
686 q = q.filter_by(poolfile_id=poolfile_id)
690 __all__.append('get_dscfiles')
692 ################################################################################
694 class PoolFile(object):
695 def __init__(self, *args, **kwargs):
699 return '<PoolFile %s>' % self.filename
701 __all__.append('PoolFile')
704 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
707 (ValidFileFound [boolean or None], PoolFile object or None)
709 @type filename: string
710 @param filename: the filename of the file to check against the DB
713 @param filesize: the size of the file to check against the DB
716 @param md5sum: the md5sum of the file to check against the DB
718 @type location_id: int
719 @param location_id: the id of the location to look in
722 @return: Tuple of length 2.
723 If more than one file found with that name:
725 If valid pool file found: (True, PoolFile object)
726 If valid pool file not found:
727 (False, None) if no file found
728 (False, PoolFile object) if file found with size/md5sum mismatch
731 q = session.query(PoolFile).filter_by(filename=filename)
732 q = q.join(Location).filter_by(location_id=location_id)
742 if obj.md5sum != md5sum or obj.filesize != filesize:
750 __all__.append('check_poolfile')
753 def get_poolfile_by_id(file_id, session=None):
755 Returns a PoolFile objects or None for the given id
758 @param file_id: the id of the file to look for
760 @rtype: PoolFile or None
761 @return: either the PoolFile object or None
764 q = session.query(PoolFile).filter_by(file_id=file_id)
768 except NoResultFound:
771 __all__.append('get_poolfile_by_id')
775 def get_poolfile_by_name(filename, location_id=None, session=None):
777 Returns an array of PoolFile objects for the given filename and
778 (optionally) location_id
780 @type filename: string
781 @param filename: the filename of the file to check against the DB
783 @type location_id: int
784 @param location_id: the id of the location to look in (optional)
787 @return: array of PoolFile objects
790 q = session.query(PoolFile).filter_by(filename=filename)
792 if location_id is not None:
793 q = q.join(Location).filter_by(location_id=location_id)
797 __all__.append('get_poolfile_by_name')
800 def get_poolfile_like_name(filename, session=None):
802 Returns an array of PoolFile objects which are like the given name
804 @type filename: string
805 @param filename: the filename of the file to check against the DB
808 @return: array of PoolFile objects
811 # TODO: There must be a way of properly using bind parameters with %FOO%
812 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
816 __all__.append('get_poolfile_like_name')
818 ################################################################################
820 class Fingerprint(object):
821 def __init__(self, *args, **kwargs):
825 return '<Fingerprint %s>' % self.fingerprint
827 __all__.append('Fingerprint')
829 def get_or_set_fingerprint(fpr, session=None):
831 Returns Fingerprint object for given fpr.
833 If no matching fpr is found, a row is inserted.
836 @param fpr: The fpr to find / add
838 @type session: SQLAlchemy
839 @param session: Optional SQL session object (a temporary one will be
840 generated if not supplied). If not passed, a commit will be performed at
841 the end of the function, otherwise the caller is responsible for commiting.
842 A flush will be performed either way.
845 @return: the Fingerprint object for the given fpr
849 session = DBConn().session()
852 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
856 except NoResultFound:
857 fingerprint = Fingerprint()
858 fingerprint.fingerprint = fpr
859 session.add(fingerprint)
871 __all__.append('get_or_set_fingerprint')
873 ################################################################################
875 class Keyring(object):
876 def __init__(self, *args, **kwargs):
880 return '<Keyring %s>' % self.keyring_name
882 __all__.append('Keyring')
884 def get_or_set_keyring(keyring, session=None):
886 If C{keyring} does not have an entry in the C{keyrings} table yet, create one
887 and return the new Keyring
888 If C{keyring} already has an entry, simply return the existing Keyring
890 @type keyring: string
891 @param keyring: the keyring name
894 @return: the Keyring object for this keyring
899 session = DBConn().session()
903 obj = session.query(Keyring).filter_by(keyring_name=keyring).first()
906 obj = Keyring(keyring_name=keyring)
918 __all__.append('get_or_set_keyring')
920 ################################################################################
922 class Location(object):
923 def __init__(self, *args, **kwargs):
927 return '<Location %s (%s)>' % (self.path, self.location_id)
929 __all__.append('Location')
932 def get_location(location, component=None, archive=None, session=None):
934 Returns Location object for the given combination of location, component
937 @type location: string
938 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
940 @type component: string
941 @param component: the component name (if None, no restriction applied)
943 @type archive: string
944 @param archive_id: the archive name (if None, no restriction applied)
946 @rtype: Location / None
947 @return: Either a Location object or None if one can't be found
950 q = session.query(Location).filter_by(path=location)
952 if archive is not None:
953 q = q.join(Archive).filter_by(archive_name=archive)
955 if component is not None:
956 q = q.join(Component).filter_by(component_name=component)
960 except NoResultFound:
963 __all__.append('get_location')
965 ################################################################################
967 class Maintainer(object):
968 def __init__(self, *args, **kwargs):
972 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
974 def get_split_maintainer(self):
975 if not hasattr(self, 'name') or self.name is None:
976 return ('', '', '', '')
978 return fix_maintainer(self.name.strip())
980 __all__.append('Maintainer')
982 def get_or_set_maintainer(name, session=None):
984 Returns Maintainer object for given maintainer name.
986 If no matching maintainer name is found, a row is inserted.
989 @param name: The maintainer name to add
991 @type session: SQLAlchemy
992 @param session: Optional SQL session object (a temporary one will be
993 generated if not supplied). If not passed, a commit will be performed at
994 the end of the function, otherwise the caller is responsible for commiting.
995 A flush will be performed either way.
998 @return: the Maintainer object for the given maintainer
1000 privatetrans = False
1002 session = DBConn().session()
1005 q = session.query(Maintainer).filter_by(name=name)
1008 except NoResultFound:
1009 maintainer = Maintainer()
1010 maintainer.name = name
1011 session.add(maintainer)
1023 __all__.append('get_or_set_maintainer')
1025 def get_maintainer(maintainer_id, session=None):
1027 Return the name of the maintainer behind C{maintainer_id} or None if that
1028 maintainer_id is invalid.
1030 @type maintainer_id: int
1031 @param maintainer_id: the id of the maintainer
1034 @return: the Maintainer with this C{maintainer_id}
1037 privatetrans = False
1039 session = DBConn().session()
1043 return session.query(Maintainer).get(maintainer_id)
1048 __all__.append('get_maintainer')
1050 ################################################################################
1052 class NewComment(object):
1053 def __init__(self, *args, **kwargs):
1057 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1059 __all__.append('NewComment')
1062 def has_new_comment(package, version, session=None):
1064 Returns true if the given combination of C{package}, C{version} has a comment.
1066 @type package: string
1067 @param package: name of the package
1069 @type version: string
1070 @param version: package version
1072 @type session: Session
1073 @param session: Optional SQLA session object (a temporary one will be
1074 generated if not supplied)
1080 q = session.query(NewComment)
1081 q = q.filter_by(package=package)
1082 q = q.filter_by(version=version)
1084 return bool(q.count() > 0)
1086 __all__.append('has_new_comment')
1089 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1091 Returns (possibly empty) list of NewComment objects for the given
1094 @type package: string (optional)
1095 @param package: name of the package
1097 @type version: string (optional)
1098 @param version: package version
1100 @type comment_id: int (optional)
1101 @param comment_id: An id of a comment
1103 @type session: Session
1104 @param session: Optional SQLA session object (a temporary one will be
1105 generated if not supplied)
1108 @return: A (possibly empty) list of NewComment objects will be returned
1111 q = session.query(NewComment)
1112 if package is not None: q = q.filter_by(package=package)
1113 if version is not None: q = q.filter_by(version=version)
1114 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1118 __all__.append('get_new_comments')
1120 ################################################################################
1122 class Override(object):
1123 def __init__(self, *args, **kwargs):
1127 return '<Override %s (%s)>' % (self.package, self.suite_id)
1129 __all__.append('Override')
1132 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1134 Returns Override object for the given parameters
1136 @type package: string
1137 @param package: The name of the package
1139 @type suite: string, list or None
1140 @param suite: The name of the suite (or suites if a list) to limit to. If
1141 None, don't limit. Defaults to None.
1143 @type component: string, list or None
1144 @param component: The name of the component (or components if a list) to
1145 limit to. If None, don't limit. Defaults to None.
1147 @type overridetype: string, list or None
1148 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1149 limit to. If None, don't limit. Defaults to None.
1151 @type session: Session
1152 @param session: Optional SQLA session object (a temporary one will be
1153 generated if not supplied)
1156 @return: A (possibly empty) list of Override objects will be returned
1159 q = session.query(Override)
1160 q = q.filter_by(package=package)
1162 if suite is not None:
1163 if not isinstance(suite, list): suite = [suite]
1164 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1166 if component is not None:
1167 if not isinstance(component, list): component = [component]
1168 q = q.join(Component).filter(Component.component_name.in_(component))
1170 if overridetype is not None:
1171 if not isinstance(overridetype, list): overridetype = [overridetype]
1172 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1176 __all__.append('get_override')
1179 ################################################################################
1181 class OverrideType(object):
1182 def __init__(self, *args, **kwargs):
1186 return '<OverrideType %s>' % self.overridetype
1188 __all__.append('OverrideType')
1191 def get_override_type(override_type, session=None):
1193 Returns OverrideType object for given C{override type}.
1195 @type override_type: string
1196 @param override_type: The name of the override type
1198 @type session: Session
1199 @param session: Optional SQLA session object (a temporary one will be
1200 generated if not supplied)
1203 @return: the database id for the given override type
1206 q = session.query(OverrideType).filter_by(overridetype=override_type)
1210 except NoResultFound:
1213 __all__.append('get_override_type')
1215 ################################################################################
1217 class PendingContentAssociation(object):
1218 def __init__(self, *args, **kwargs):
1222 return '<PendingContentAssociation %s>' % self.pca_id
1224 __all__.append('PendingContentAssociation')
1226 def insert_pending_content_paths(package, fullpaths, session=None):
1228 Make sure given paths are temporarily associated with given
1232 @param package: the package to associate with should have been read in from the binary control file
1233 @type fullpaths: list
1234 @param fullpaths: the list of paths of the file being associated with the binary
1235 @type session: SQLAlchemy session
1236 @param session: Optional SQLAlchemy session. If this is passed, the caller
1237 is responsible for ensuring a transaction has begun and committing the
1238 results or rolling back based on the result code. If not passed, a commit
1239 will be performed at the end of the function
1241 @return: True upon success, False if there is a problem
1244 privatetrans = False
1247 session = DBConn().session()
1251 arch = get_architecture(package['Architecture'], session)
1252 arch_id = arch.arch_id
1254 # Remove any already existing recorded files for this package
1255 q = session.query(PendingContentAssociation)
1256 q = q.filter_by(package=package['Package'])
1257 q = q.filter_by(version=package['Version'])
1258 q = q.filter_by(architecture=arch_id)
1263 for fullpath in fullpaths:
1264 (path, file) = os.path.split(fullpath)
1266 if path.startswith( "./" ):
1269 filepath_id = get_or_set_contents_path_id(path, session)
1270 filename_id = get_or_set_contents_file_id(file, session)
1272 pathcache[fullpath] = (filepath_id, filename_id)
1274 for fullpath, dat in pathcache.items():
1275 pca = PendingContentAssociation()
1276 pca.package = package['Package']
1277 pca.version = package['Version']
1278 pca.filepath_id = dat[0]
1279 pca.filename_id = dat[1]
1280 pca.architecture = arch_id
1283 # Only commit if we set up the session ourself
1291 except Exception, e:
1292 traceback.print_exc()
1294 # Only rollback if we set up the session ourself
1301 __all__.append('insert_pending_content_paths')
1303 ################################################################################
1305 class Priority(object):
1306 def __init__(self, *args, **kwargs):
1309 def __eq__(self, val):
1310 if isinstance(val, str):
1311 return (self.priority == val)
1312 # This signals to use the normal comparison operator
1313 return NotImplemented
1315 def __ne__(self, val):
1316 if isinstance(val, str):
1317 return (self.priority != val)
1318 # This signals to use the normal comparison operator
1319 return NotImplemented
1322 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1324 __all__.append('Priority')
1327 def get_priority(priority, session=None):
1329 Returns Priority object for given C{priority name}.
1331 @type priority: string
1332 @param priority: The name of the priority
1334 @type session: Session
1335 @param session: Optional SQLA session object (a temporary one will be
1336 generated if not supplied)
1339 @return: Priority object for the given priority
1342 q = session.query(Priority).filter_by(priority=priority)
1346 except NoResultFound:
1349 __all__.append('get_priority')
1352 def get_priorities(session=None):
1354 Returns dictionary of priority names -> id mappings
1356 @type session: Session
1357 @param session: Optional SQL session object (a temporary one will be
1358 generated if not supplied)
1361 @return: dictionary of priority names -> id mappings
1365 q = session.query(Priority)
1367 ret[x.priority] = x.priority_id
1371 __all__.append('get_priorities')
1373 ################################################################################
1375 class Queue(object):
1376 def __init__(self, *args, **kwargs):
1380 return '<Queue %s>' % self.queue_name
1382 def autobuild_upload(self, changes, srcpath, session=None):
1384 Update queue_build database table used for incoming autobuild support.
1386 @type changes: Changes
1387 @param changes: changes object for the upload to process
1389 @type srcpath: string
1390 @param srcpath: path for the queue file entries/link destinations
1392 @type session: SQLAlchemy session
1393 @param session: Optional SQLAlchemy session. If this is passed, the
1394 caller is responsible for ensuring a transaction has begun and
1395 committing the results or rolling back based on the result code. If
1396 not passed, a commit will be performed at the end of the function,
1397 otherwise the caller is responsible for commiting.
1399 @rtype: NoneType or string
1400 @return: None if the operation failed, a string describing the error if not
1403 privatetrans = False
1405 session = DBConn().session()
1408 # TODO: Remove by moving queue config into the database
1411 for suitename in changes.changes["distribution"].keys():
1412 # TODO: Move into database as:
1413 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1414 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1415 # This also gets rid of the SecurityQueueBuild hack below
1416 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1420 s = get_suite(suitename, session)
1422 return "INTERNAL ERROR: Could not find suite %s" % suitename
1424 # TODO: Get from database as above
1425 dest_dir = conf["Dir::QueueBuild"]
1427 # TODO: Move into database as above
1428 if conf.FindB("Dinstall::SecurityQueueBuild"):
1429 dest_dir = os.path.join(dest_dir, suitename)
1431 for file_entry in changes.files.keys():
1432 src = os.path.join(srcpath, file_entry)
1433 dest = os.path.join(dest_dir, file_entry)
1435 # TODO: Move into database as above
1436 if conf.FindB("Dinstall::SecurityQueueBuild"):
1437 # Copy it since the original won't be readable by www-data
1439 utils.copy(src, dest)
1441 # Create a symlink to it
1442 os.symlink(src, dest)
1445 qb.suite_id = s.suite_id
1446 qb.queue_id = self.queue_id
1452 # If the .orig.tar.gz is in the pool, create a symlink to
1453 # it (if one doesn't already exist)
1454 if changes.orig_tar_id:
1455 # Determine the .orig.tar.gz file name
1456 for dsc_file in changes.dsc_files.keys():
1457 if dsc_file.endswith(".orig.tar.gz"):
1460 dest = os.path.join(dest_dir, filename)
1462 # If it doesn't exist, create a symlink
1463 if not os.path.exists(dest):
1464 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1465 {'id': changes.orig_tar_id})
1468 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1470 src = os.path.join(res[0], res[1])
1471 os.symlink(src, dest)
1473 # Add it to the list of packages for later processing by apt-ftparchive
1475 qb.suite_id = s.suite_id
1476 qb.queue_id = self.queue_id
1481 # If it does, update things to ensure it's not removed prematurely
1483 qb = get_queue_build(dest, s.suite_id, session)
1495 __all__.append('Queue')
1498 def get_queue(queuename, session=None):
1500 Returns Queue object for given C{queue name}.
1502 @type queuename: string
1503 @param queuename: The name of the queue
1505 @type session: Session
1506 @param session: Optional SQLA session object (a temporary one will be
1507 generated if not supplied)
1510 @return: Queue object for the given queue
1513 q = session.query(Queue).filter_by(queue_name=queuename)
1517 except NoResultFound:
1520 __all__.append('get_queue')
1522 ################################################################################
1524 class QueueBuild(object):
1525 def __init__(self, *args, **kwargs):
1529 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1531 __all__.append('QueueBuild')
1534 def get_queue_build(filename, suite, session=None):
1536 Returns QueueBuild object for given C{filename} and C{suite}.
1538 @type filename: string
1539 @param filename: The name of the file
1541 @type suiteid: int or str
1542 @param suiteid: Suite name or ID
1544 @type session: Session
1545 @param session: Optional SQLA session object (a temporary one will be
1546 generated if not supplied)
1549 @return: Queue object for the given queue
1552 if isinstance(suite, int):
1553 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1555 q = session.query(QueueBuild).filter_by(filename=filename)
1556 q = q.join(Suite).filter_by(suite_name=suite)
1560 except NoResultFound:
1563 __all__.append('get_queue_build')
1565 ################################################################################
1567 class Section(object):
1568 def __init__(self, *args, **kwargs):
1571 def __eq__(self, val):
1572 if isinstance(val, str):
1573 return (self.section == val)
1574 # This signals to use the normal comparison operator
1575 return NotImplemented
1577 def __ne__(self, val):
1578 if isinstance(val, str):
1579 return (self.section != val)
1580 # This signals to use the normal comparison operator
1581 return NotImplemented
1584 return '<Section %s>' % self.section
1586 __all__.append('Section')
1589 def get_section(section, session=None):
1591 Returns Section object for given C{section name}.
1593 @type section: string
1594 @param section: The name of the section
1596 @type session: Session
1597 @param session: Optional SQLA session object (a temporary one will be
1598 generated if not supplied)
1601 @return: Section object for the given section name
1604 q = session.query(Section).filter_by(section=section)
1608 except NoResultFound:
1611 __all__.append('get_section')
1614 def get_sections(session=None):
1616 Returns dictionary of section names -> id mappings
1618 @type session: Session
1619 @param session: Optional SQL session object (a temporary one will be
1620 generated if not supplied)
1623 @return: dictionary of section names -> id mappings
1627 q = session.query(Section)
1629 ret[x.section] = x.section_id
1633 __all__.append('get_sections')
1635 ################################################################################
1637 class DBSource(object):
1638 def __init__(self, *args, **kwargs):
1642 return '<DBSource %s (%s)>' % (self.source, self.version)
1644 __all__.append('DBSource')
1647 def source_exists(source, source_version, suites = ["any"], session=None):
1649 Ensure that source exists somewhere in the archive for the binary
1650 upload being processed.
1651 1. exact match => 1.0-3
1652 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1654 @type package: string
1655 @param package: package source name
1657 @type source_version: string
1658 @param source_version: expected source version
1661 @param suites: list of suites to check in, default I{any}
1663 @type session: Session
1664 @param session: Optional SQLA session object (a temporary one will be
1665 generated if not supplied)
1668 @return: returns 1 if a source with expected version is found, otherwise 0
1675 for suite in suites:
1676 q = session.query(DBSource).filter_by(source=source)
1678 # source must exist in suite X, or in some other suite that's
1679 # mapped to X, recursively... silent-maps are counted too,
1680 # unreleased-maps aren't.
1681 maps = cnf.ValueList("SuiteMappings")[:]
1683 maps = [ m.split() for m in maps ]
1684 maps = [ (x[1], x[2]) for x in maps
1685 if x[0] == "map" or x[0] == "silent-map" ]
1688 if x[1] in s and x[0] not in s:
1691 q = q.join(SrcAssociation).join(Suite)
1692 q = q.filter(Suite.suite_name.in_(s))
1694 # Reduce the query results to a list of version numbers
1695 ql = [ j.version for j in q.all() ]
1698 if source_version in ql:
1702 from daklib.regexes import re_bin_only_nmu
1703 orig_source_version = re_bin_only_nmu.sub('', source_version)
1704 if orig_source_version in ql:
1707 # No source found so return not ok
1712 __all__.append('source_exists')
1715 def get_suites_source_in(source, session=None):
1717 Returns list of Suite objects which given C{source} name is in
1720 @param source: DBSource package name to search for
1723 @return: list of Suite objects for the given source
1726 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1728 __all__.append('get_suites_source_in')
1731 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1733 Returns list of DBSource objects for given C{source} name and other parameters
1736 @param source: DBSource package name to search for
1738 @type source: str or None
1739 @param source: DBSource version name to search for or None if not applicable
1741 @type dm_upload_allowed: bool
1742 @param dm_upload_allowed: If None, no effect. If True or False, only
1743 return packages with that dm_upload_allowed setting
1745 @type session: Session
1746 @param session: Optional SQL session object (a temporary one will be
1747 generated if not supplied)
1750 @return: list of DBSource objects for the given name (may be empty)
1753 q = session.query(DBSource).filter_by(source=source)
1755 if version is not None:
1756 q = q.filter_by(version=version)
1758 if dm_upload_allowed is not None:
1759 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1763 __all__.append('get_sources_from_name')
1766 def get_source_in_suite(source, suite, session=None):
1768 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1770 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1771 - B{suite} - a suite name, eg. I{unstable}
1773 @type source: string
1774 @param source: source package name
1777 @param suite: the suite name
1780 @return: the version for I{source} in I{suite}
1784 q = session.query(SrcAssociation)
1785 q = q.join('source').filter_by(source=source)
1786 q = q.join('suite').filter_by(suite_name=suite)
1789 return q.one().source
1790 except NoResultFound:
1793 __all__.append('get_source_in_suite')
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 SrcUploader(object):
1809 def __init__(self, *args, **kwargs):
1813 return '<SrcUploader %s>' % self.uploader_id
1815 __all__.append('SrcUploader')
1817 ################################################################################
1819 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1820 ('SuiteID', 'suite_id'),
1821 ('Version', 'version'),
1822 ('Origin', 'origin'),
1824 ('Description', 'description'),
1825 ('Untouchable', 'untouchable'),
1826 ('Announce', 'announce'),
1827 ('Codename', 'codename'),
1828 ('OverrideCodename', 'overridecodename'),
1829 ('ValidTime', 'validtime'),
1830 ('Priority', 'priority'),
1831 ('NotAutomatic', 'notautomatic'),
1832 ('CopyChanges', 'copychanges'),
1833 ('CopyDotDak', 'copydotdak'),
1834 ('CommentsDir', 'commentsdir'),
1835 ('OverrideSuite', 'overridesuite'),
1836 ('ChangelogBase', 'changelogbase')]
1839 class Suite(object):
1840 def __init__(self, *args, **kwargs):
1844 return '<Suite %s>' % self.suite_name
1846 def __eq__(self, val):
1847 if isinstance(val, str):
1848 return (self.suite_name == val)
1849 # This signals to use the normal comparison operator
1850 return NotImplemented
1852 def __ne__(self, val):
1853 if isinstance(val, str):
1854 return (self.suite_name != val)
1855 # This signals to use the normal comparison operator
1856 return NotImplemented
1860 for disp, field in SUITE_FIELDS:
1861 val = getattr(self, field, None)
1863 ret.append("%s: %s" % (disp, val))
1865 return "\n".join(ret)
1867 __all__.append('Suite')
1870 def get_suite_architecture(suite, architecture, session=None):
1872 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1876 @param suite: Suite name to search for
1878 @type architecture: str
1879 @param architecture: Architecture name to search for
1881 @type session: Session
1882 @param session: Optional SQL session object (a temporary one will be
1883 generated if not supplied)
1885 @rtype: SuiteArchitecture
1886 @return: the SuiteArchitecture object or None
1889 q = session.query(SuiteArchitecture)
1890 q = q.join(Architecture).filter_by(arch_string=architecture)
1891 q = q.join(Suite).filter_by(suite_name=suite)
1895 except NoResultFound:
1898 __all__.append('get_suite_architecture')
1901 def get_suite(suite, session=None):
1903 Returns Suite object for given C{suite name}.
1906 @param suite: The name of the suite
1908 @type session: Session
1909 @param session: Optional SQLA session object (a temporary one will be
1910 generated if not supplied)
1913 @return: Suite object for the requested suite name (None if not present)
1916 q = session.query(Suite).filter_by(suite_name=suite)
1920 except NoResultFound:
1923 __all__.append('get_suite')
1925 ################################################################################
1927 class SuiteArchitecture(object):
1928 def __init__(self, *args, **kwargs):
1932 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1934 __all__.append('SuiteArchitecture')
1937 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1939 Returns list of Architecture objects for given C{suite} name
1942 @param source: Suite name to search for
1944 @type skipsrc: boolean
1945 @param skipsrc: Whether to skip returning the 'source' architecture entry
1948 @type skipall: boolean
1949 @param skipall: Whether to skip returning the 'all' architecture entry
1952 @type session: Session
1953 @param session: Optional SQL session object (a temporary one will be
1954 generated if not supplied)
1957 @return: list of Architecture objects for the given name (may be empty)
1960 q = session.query(Architecture)
1961 q = q.join(SuiteArchitecture)
1962 q = q.join(Suite).filter_by(suite_name=suite)
1965 q = q.filter(Architecture.arch_string != 'source')
1968 q = q.filter(Architecture.arch_string != 'all')
1970 q = q.order_by('arch_string')
1974 __all__.append('get_suite_architectures')
1976 ################################################################################
1979 def __init__(self, *args, **kwargs):
1982 def __eq__(self, val):
1983 if isinstance(val, str):
1984 return (self.uid == val)
1985 # This signals to use the normal comparison operator
1986 return NotImplemented
1988 def __ne__(self, val):
1989 if isinstance(val, str):
1990 return (self.uid != val)
1991 # This signals to use the normal comparison operator
1992 return NotImplemented
1995 return '<Uid %s (%s)>' % (self.uid, self.name)
1997 __all__.append('Uid')
1999 def add_database_user(uidname, session=None):
2001 Adds a database user
2003 @type uidname: string
2004 @param uidname: The uid of the user to add
2006 @type session: SQLAlchemy
2007 @param session: Optional SQL session object (a temporary one will be
2008 generated if not supplied). If not passed, a commit will be performed at
2009 the end of the function, otherwise the caller is responsible for commiting.
2012 @return: the uid object for the given uidname
2015 privatetrans = False
2017 session = DBConn().session()
2020 session.execute("CREATE USER :uid", {'uid': uidname})
2026 __all__.append('add_database_user')
2028 def get_or_set_uid(uidname, session=None):
2030 Returns uid object for given uidname.
2032 If no matching uidname is found, a row is inserted.
2034 @type uidname: string
2035 @param uidname: The uid to add
2037 @type session: SQLAlchemy
2038 @param session: Optional SQL session object (a temporary one will be
2039 generated if not supplied). If not passed, a commit will be performed at
2040 the end of the function, otherwise the caller is responsible for commiting.
2043 @return: the uid object for the given uidname
2046 privatetrans = False
2048 session = DBConn().session()
2051 q = session.query(Uid).filter_by(uid=uidname)
2055 except NoResultFound:
2070 __all__.append('get_or_set_uid')
2073 def get_uid_from_fingerprint(fpr, session=None):
2074 q = session.query(Uid)
2075 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2079 except NoResultFound:
2082 __all__.append('get_uid_from_fingerprint')
2084 ################################################################################
2086 class DBConn(Singleton):
2088 database module init.
2090 def __init__(self, *args, **kwargs):
2091 super(DBConn, self).__init__(*args, **kwargs)
2093 def _startup(self, *args, **kwargs):
2095 if kwargs.has_key('debug'):
2099 def __setuptables(self):
2100 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2101 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2102 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2103 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2104 self.tbl_component = Table('component', self.db_meta, autoload=True)
2105 self.tbl_config = Table('config', self.db_meta, autoload=True)
2106 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2107 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2108 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2109 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2110 self.tbl_files = Table('files', self.db_meta, autoload=True)
2111 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2112 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2113 self.tbl_location = Table('location', self.db_meta, autoload=True)
2114 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2115 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2116 self.tbl_override = Table('override', self.db_meta, autoload=True)
2117 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2118 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2119 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2120 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2121 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2122 self.tbl_section = Table('section', self.db_meta, autoload=True)
2123 self.tbl_source = Table('source', self.db_meta, autoload=True)
2124 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2125 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2126 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2127 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2128 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2130 def __setupmappers(self):
2131 mapper(Architecture, self.tbl_architecture,
2132 properties = dict(arch_id = self.tbl_architecture.c.id))
2134 mapper(Archive, self.tbl_archive,
2135 properties = dict(archive_id = self.tbl_archive.c.id,
2136 archive_name = self.tbl_archive.c.name))
2138 mapper(BinAssociation, self.tbl_bin_associations,
2139 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2140 suite_id = self.tbl_bin_associations.c.suite,
2141 suite = relation(Suite),
2142 binary_id = self.tbl_bin_associations.c.bin,
2143 binary = relation(DBBinary)))
2145 mapper(DBBinary, self.tbl_binaries,
2146 properties = dict(binary_id = self.tbl_binaries.c.id,
2147 package = self.tbl_binaries.c.package,
2148 version = self.tbl_binaries.c.version,
2149 maintainer_id = self.tbl_binaries.c.maintainer,
2150 maintainer = relation(Maintainer),
2151 source_id = self.tbl_binaries.c.source,
2152 source = relation(DBSource),
2153 arch_id = self.tbl_binaries.c.architecture,
2154 architecture = relation(Architecture),
2155 poolfile_id = self.tbl_binaries.c.file,
2156 poolfile = relation(PoolFile),
2157 binarytype = self.tbl_binaries.c.type,
2158 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2159 fingerprint = relation(Fingerprint),
2160 install_date = self.tbl_binaries.c.install_date,
2161 binassociations = relation(BinAssociation,
2162 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2164 mapper(Component, self.tbl_component,
2165 properties = dict(component_id = self.tbl_component.c.id,
2166 component_name = self.tbl_component.c.name))
2168 mapper(DBConfig, self.tbl_config,
2169 properties = dict(config_id = self.tbl_config.c.id))
2171 mapper(ContentAssociation, self.tbl_content_associations,
2172 properties = dict(ca_id = self.tbl_content_associations.c.id,
2173 filename_id = self.tbl_content_associations.c.filename,
2174 filename = relation(ContentFilename),
2175 filepath_id = self.tbl_content_associations.c.filepath,
2176 filepath = relation(ContentFilepath),
2177 binary_id = self.tbl_content_associations.c.binary_pkg,
2178 binary = relation(DBBinary)))
2181 mapper(ContentFilename, self.tbl_content_file_names,
2182 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2183 filename = self.tbl_content_file_names.c.file))
2185 mapper(ContentFilepath, self.tbl_content_file_paths,
2186 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2187 filepath = self.tbl_content_file_paths.c.path))
2189 mapper(DSCFile, self.tbl_dsc_files,
2190 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2191 source_id = self.tbl_dsc_files.c.source,
2192 source = relation(DBSource),
2193 poolfile_id = self.tbl_dsc_files.c.file,
2194 poolfile = relation(PoolFile)))
2196 mapper(PoolFile, self.tbl_files,
2197 properties = dict(file_id = self.tbl_files.c.id,
2198 filesize = self.tbl_files.c.size,
2199 location_id = self.tbl_files.c.location,
2200 location = relation(Location)))
2202 mapper(Fingerprint, self.tbl_fingerprint,
2203 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2204 uid_id = self.tbl_fingerprint.c.uid,
2205 uid = relation(Uid),
2206 keyring_id = self.tbl_fingerprint.c.keyring,
2207 keyring = relation(Keyring)))
2209 mapper(Keyring, self.tbl_keyrings,
2210 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2211 keyring_id = self.tbl_keyrings.c.id))
2213 mapper(Location, self.tbl_location,
2214 properties = dict(location_id = self.tbl_location.c.id,
2215 component_id = self.tbl_location.c.component,
2216 component = relation(Component),
2217 archive_id = self.tbl_location.c.archive,
2218 archive = relation(Archive),
2219 archive_type = self.tbl_location.c.type))
2221 mapper(Maintainer, self.tbl_maintainer,
2222 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2224 mapper(NewComment, self.tbl_new_comments,
2225 properties = dict(comment_id = self.tbl_new_comments.c.id))
2227 mapper(Override, self.tbl_override,
2228 properties = dict(suite_id = self.tbl_override.c.suite,
2229 suite = relation(Suite),
2230 component_id = self.tbl_override.c.component,
2231 component = relation(Component),
2232 priority_id = self.tbl_override.c.priority,
2233 priority = relation(Priority),
2234 section_id = self.tbl_override.c.section,
2235 section = relation(Section),
2236 overridetype_id = self.tbl_override.c.type,
2237 overridetype = relation(OverrideType)))
2239 mapper(OverrideType, self.tbl_override_type,
2240 properties = dict(overridetype = self.tbl_override_type.c.type,
2241 overridetype_id = self.tbl_override_type.c.id))
2243 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2244 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2245 filepath_id = self.tbl_pending_content_associations.c.filepath,
2246 filepath = relation(ContentFilepath),
2247 filename_id = self.tbl_pending_content_associations.c.filename,
2248 filename = relation(ContentFilename)))
2250 mapper(Priority, self.tbl_priority,
2251 properties = dict(priority_id = self.tbl_priority.c.id))
2253 mapper(Queue, self.tbl_queue,
2254 properties = dict(queue_id = self.tbl_queue.c.id))
2256 mapper(QueueBuild, self.tbl_queue_build,
2257 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2258 queue_id = self.tbl_queue_build.c.queue,
2259 queue = relation(Queue, backref='queuebuild')))
2261 mapper(Section, self.tbl_section,
2262 properties = dict(section_id = self.tbl_section.c.id))
2264 mapper(DBSource, self.tbl_source,
2265 properties = dict(source_id = self.tbl_source.c.id,
2266 version = self.tbl_source.c.version,
2267 maintainer_id = self.tbl_source.c.maintainer,
2268 maintainer = relation(Maintainer,
2269 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2270 poolfile_id = self.tbl_source.c.file,
2271 poolfile = relation(PoolFile),
2272 fingerprint_id = self.tbl_source.c.sig_fpr,
2273 fingerprint = relation(Fingerprint),
2274 changedby_id = self.tbl_source.c.changedby,
2275 changedby = relation(Maintainer,
2276 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2277 srcfiles = relation(DSCFile,
2278 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2279 srcassociations = relation(SrcAssociation,
2280 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2282 mapper(SrcAssociation, self.tbl_src_associations,
2283 properties = dict(sa_id = self.tbl_src_associations.c.id,
2284 suite_id = self.tbl_src_associations.c.suite,
2285 suite = relation(Suite),
2286 source_id = self.tbl_src_associations.c.source,
2287 source = relation(DBSource)))
2289 mapper(SrcUploader, self.tbl_src_uploaders,
2290 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2291 source_id = self.tbl_src_uploaders.c.source,
2292 source = relation(DBSource,
2293 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2294 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2295 maintainer = relation(Maintainer,
2296 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2298 mapper(Suite, self.tbl_suite,
2299 properties = dict(suite_id = self.tbl_suite.c.id))
2301 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2302 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2303 suite = relation(Suite, backref='suitearchitectures'),
2304 arch_id = self.tbl_suite_architectures.c.architecture,
2305 architecture = relation(Architecture)))
2307 mapper(Uid, self.tbl_uid,
2308 properties = dict(uid_id = self.tbl_uid.c.id,
2309 fingerprint = relation(Fingerprint)))
2311 ## Connection functions
2312 def __createconn(self):
2313 from config import Config
2317 connstr = "postgres://%s" % cnf["DB::Host"]
2318 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2319 connstr += ":%s" % cnf["DB::Port"]
2320 connstr += "/%s" % cnf["DB::Name"]
2323 connstr = "postgres:///%s" % cnf["DB::Name"]
2324 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2325 connstr += "?port=%s" % cnf["DB::Port"]
2327 self.db_pg = create_engine(connstr, echo=self.debug)
2328 self.db_meta = MetaData()
2329 self.db_meta.bind = self.db_pg
2330 self.db_smaker = sessionmaker(bind=self.db_pg,
2334 self.__setuptables()
2335 self.__setupmappers()
2338 return self.db_smaker()
2340 __all__.append('DBConn')