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 sqlalchemy import create_engine, Table, MetaData, select
41 from sqlalchemy.orm import sessionmaker, mapper, relation
43 # Don't remove this, we re-export the exceptions to scripts which import us
44 from sqlalchemy.exc import *
46 # Only import Config until Queue stuff is changed to store its config
48 from config import Config
49 from singleton import Singleton
50 from textutils import fix_maintainer
52 ################################################################################
54 __all__ = ['IntegrityError', 'SQLAlchemyError']
56 ################################################################################
58 class Architecture(object):
59 def __init__(self, *args, **kwargs):
62 def __eq__(self, val):
63 if isinstance(val, str):
64 return (self.arch_string== val)
65 # This signals to use the normal comparison operator
68 def __ne__(self, val):
69 if isinstance(val, str):
70 return (self.arch_string != val)
71 # This signals to use the normal comparison operator
75 return '<Architecture %s>' % self.arch_string
77 __all__.append('Architecture')
79 def get_architecture(architecture, session=None):
81 Returns database id for given C{architecture}.
83 @type architecture: string
84 @param architecture: The name of the architecture
86 @type session: Session
87 @param session: Optional SQLA session object (a temporary one will be
88 generated if not supplied)
91 @return: Architecture object for the given arch (None if not present)
95 session = DBConn().session()
96 q = session.query(Architecture).filter_by(arch_string=architecture)
101 __all__.append('get_architecture')
103 def get_architecture_suites(architecture, session=None):
105 Returns list of Suite objects for given C{architecture} name
108 @param source: Architecture name to search for
110 @type session: Session
111 @param session: Optional SQL session object (a temporary one will be
112 generated if not supplied)
115 @return: list of Suite objects for the given name (may be empty)
119 session = DBConn().session()
121 q = session.query(Suite)
122 q = q.join(SuiteArchitecture)
123 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
126 __all__.append('get_architecture_suites')
128 ################################################################################
130 class Archive(object):
131 def __init__(self, *args, **kwargs):
135 return '<Archive %s>' % self.name
137 __all__.append('Archive')
139 def get_archive(archive, session=None):
141 returns database id for given c{archive}.
143 @type archive: string
144 @param archive: the name of the arhive
146 @type session: Session
147 @param session: Optional SQLA session object (a temporary one will be
148 generated if not supplied)
151 @return: Archive object for the given name (None if not present)
154 archive = archive.lower()
156 session = DBConn().session()
157 q = session.query(Archive).filter_by(archive_name=archive)
162 __all__.append('get_archive')
164 ################################################################################
166 class BinAssociation(object):
167 def __init__(self, *args, **kwargs):
171 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
173 __all__.append('BinAssociation')
175 ################################################################################
177 class DBBinary(object):
178 def __init__(self, *args, **kwargs):
182 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
184 __all__.append('DBBinary')
186 def get_suites_binary_in(package, session=None):
188 Returns list of Suite objects which given C{package} name is in
191 @param source: DBBinary package name to search for
194 @return: list of Suite objects for the given package
198 session = DBConn().session()
200 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
202 __all__.append('get_suites_binary_in')
204 def get_binary_from_id(id, session=None):
206 Returns DBBinary object for given C{id}
209 @param id: Id of the required binary
211 @type session: Session
212 @param session: Optional SQLA session object (a temporary one will be
213 generated if not supplied)
216 @return: DBBinary object for the given binary (None if not present)
219 session = DBConn().session()
220 q = session.query(DBBinary).filter_by(binary_id=id)
225 __all__.append('get_binary_from_id')
227 def get_binaries_from_name(package, version=None, architecture=None, session=None):
229 Returns list of DBBinary objects for given C{package} name
232 @param package: DBBinary package name to search for
234 @type version: str or None
235 @param version: Version to search for (or None)
237 @type package: str, list or None
238 @param package: Architectures to limit to (or None if no limit)
240 @type session: Session
241 @param session: Optional SQL session object (a temporary one will be
242 generated if not supplied)
245 @return: list of DBBinary objects for the given name (may be empty)
248 session = DBConn().session()
250 q = session.query(DBBinary).filter_by(package=package)
252 if version is not None:
253 q = q.filter_by(version=version)
255 if architecture is not None:
256 if not isinstance(architecture, list):
257 architecture = [architecture]
258 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
262 __all__.append('get_binaries_from_name')
264 def get_binaries_from_source_id(source_id, session=None):
266 Returns list of DBBinary objects for given C{source_id}
269 @param source_id: source_id to search for
271 @type session: Session
272 @param session: Optional SQL session object (a temporary one will be
273 generated if not supplied)
276 @return: list of DBBinary objects for the given name (may be empty)
279 session = DBConn().session()
280 return session.query(DBBinary).filter_by(source_id=source_id).all()
282 __all__.append('get_binaries_from_source_id')
285 def get_binary_from_name_suite(package, suitename, session=None):
286 ### For dak examine-package
287 ### XXX: Doesn't use object API yet
289 session = DBConn().session()
291 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
292 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
293 WHERE b.package=:package
295 AND fi.location = l.id
296 AND l.component = c.id
299 AND su.suite_name=:suitename
300 ORDER BY b.version DESC"""
302 return session.execute(sql, {'package': package, 'suitename': suitename})
304 __all__.append('get_binary_from_name_suite')
306 def get_binary_components(package, suitename, arch, session=None):
307 # Check for packages that have moved from one component to another
308 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
309 WHERE b.package=:package AND s.suite_name=:suitename
310 AND (a.arch_string = :arch OR a.arch_string = 'all')
311 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
312 AND f.location = l.id
313 AND l.component = c.id
316 vals = {'package': package, 'suitename': suitename, 'arch': arch}
319 session = DBConn().session()
320 return session.execute(query, vals)
322 __all__.append('get_binary_components')
324 ################################################################################
326 class Component(object):
327 def __init__(self, *args, **kwargs):
330 def __eq__(self, val):
331 if isinstance(val, str):
332 return (self.component_name == val)
333 # This signals to use the normal comparison operator
334 return NotImplemented
336 def __ne__(self, val):
337 if isinstance(val, str):
338 return (self.component_name != val)
339 # This signals to use the normal comparison operator
340 return NotImplemented
343 return '<Component %s>' % self.component_name
346 __all__.append('Component')
348 def get_component(component, session=None):
350 Returns database id for given C{component}.
352 @type component: string
353 @param component: The name of the override type
356 @return: the database id for the given component
359 component = component.lower()
361 session = DBConn().session()
362 q = session.query(Component).filter_by(component_name=component)
367 __all__.append('get_component')
369 ################################################################################
371 class DBConfig(object):
372 def __init__(self, *args, **kwargs):
376 return '<DBConfig %s>' % self.name
378 __all__.append('DBConfig')
380 ################################################################################
382 class ContentFilename(object):
383 def __init__(self, *args, **kwargs):
387 return '<ContentFilename %s>' % self.filename
389 __all__.append('ContentFilename')
391 def get_or_set_contents_file_id(filename, session=None):
393 Returns database id for given filename.
395 If no matching file is found, a row is inserted.
397 @type filename: string
398 @param filename: The filename
399 @type session: SQLAlchemy
400 @param session: Optional SQL session object (a temporary one will be
401 generated if not supplied). If not passed, a commit will be performed at
402 the end of the function, otherwise the caller is responsible for commiting.
405 @return: the database id for the given component
409 session = DBConn().session()
413 q = session.query(ContentFilename).filter_by(filename=filename)
415 cf = ContentFilename()
416 cf.filename = filename
420 return cf.cafilename_id
422 return q.one().cafilename_id
425 traceback.print_exc()
428 __all__.append('get_or_set_contents_file_id')
430 def get_contents(suite, overridetype, section=None, session=None):
432 Returns contents for a suite / overridetype combination, limiting
433 to a section if not None.
436 @param suite: Suite object
438 @type overridetype: OverrideType
439 @param overridetype: OverrideType object
441 @type section: Section
442 @param section: Optional section object to limit results to
444 @type session: SQLAlchemy
445 @param session: Optional SQL session object (a temporary one will be
446 generated if not supplied)
449 @return: ResultsProxy object set up to return tuples of (filename, section,
454 session = DBConn().session()
456 # find me all of the contents for a given suite
457 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
461 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
462 JOIN content_file_names n ON (c.filename=n.id)
463 JOIN binaries b ON (b.id=c.binary_pkg)
464 JOIN override o ON (o.package=b.package)
465 JOIN section s ON (s.id=o.section)
466 WHERE o.suite = :suiteid AND o.type = :overridetypeid
467 AND b.type=:overridetypename"""
469 vals = {'suiteid': suite.suite_id,
470 'overridetypeid': overridetype.overridetype_id,
471 'overridetypename': overridetype.overridetype}
473 if section is not None:
474 contents_q += " AND s.id = :sectionid"
475 vals['sectionid'] = section.section_id
477 contents_q += " ORDER BY fn"
479 return session.execute(contents_q, vals)
481 __all__.append('get_contents')
483 ################################################################################
485 class ContentFilepath(object):
486 def __init__(self, *args, **kwargs):
490 return '<ContentFilepath %s>' % self.filepath
492 __all__.append('ContentFilepath')
494 def get_or_set_contents_path_id(filepath, session):
496 Returns database id for given path.
498 If no matching file is found, a row is inserted.
500 @type filename: string
501 @param filename: The filepath
502 @type session: SQLAlchemy
503 @param session: Optional SQL session object (a temporary one will be
504 generated if not supplied). If not passed, a commit will be performed at
505 the end of the function, otherwise the caller is responsible for commiting.
508 @return: the database id for the given path
512 session = DBConn().session()
516 q = session.query(ContentFilepath).filter_by(filepath=filepath)
518 cf = ContentFilepath()
519 cf.filepath = filepath
523 return cf.cafilepath_id
525 return q.one().cafilepath_id
528 traceback.print_exc()
531 __all__.append('get_or_set_contents_path_id')
533 ################################################################################
535 class ContentAssociation(object):
536 def __init__(self, *args, **kwargs):
540 return '<ContentAssociation %s>' % self.ca_id
542 __all__.append('ContentAssociation')
544 def insert_content_paths(binary_id, fullpaths, session=None):
546 Make sure given path is associated with given binary id
549 @param binary_id: the id of the binary
550 @type fullpaths: list
551 @param fullpaths: the list of paths of the file being associated with the binary
552 @type session: SQLAlchemy session
553 @param session: Optional SQLAlchemy session. If this is passed, the caller
554 is responsible for ensuring a transaction has begun and committing the
555 results or rolling back based on the result code. If not passed, a commit
556 will be performed at the end of the function, otherwise the caller is
557 responsible for commiting.
559 @return: True upon success
565 session = DBConn().session()
569 for fullpath in fullpaths:
570 (path, file) = os.path.split(fullpath)
572 # Get the necessary IDs ...
573 ca = ContentAssociation()
574 ca.binary_id = binary_id
575 ca.filename_id = get_or_set_contents_file_id(file)
576 ca.filepath_id = get_or_set_contents_path_id(path)
579 # Only commit if we set up the session ourself
585 traceback.print_exc()
587 # Only rollback if we set up the session ourself
593 __all__.append('insert_content_paths')
595 ################################################################################
597 class DSCFile(object):
598 def __init__(self, *args, **kwargs):
602 return '<DSCFile %s>' % self.dscfile_id
604 __all__.append('DSCFile')
606 ################################################################################
608 class PoolFile(object):
609 def __init__(self, *args, **kwargs):
613 return '<PoolFile %s>' % self.filename
615 __all__.append('PoolFile')
617 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
620 (ValidFileFound [boolean or None], PoolFile object or None)
622 @type filename: string
623 @param filename: the filename of the file to check against the DB
626 @param filesize: the size of the file to check against the DB
629 @param md5sum: the md5sum of the file to check against the DB
631 @type location_id: int
632 @param location_id: the id of the location to look in
635 @return: Tuple of length 2.
636 If more than one file found with that name:
638 If valid pool file found: (True, PoolFile object)
639 If valid pool file not found:
640 (False, None) if no file found
641 (False, PoolFile object) if file found with size/md5sum mismatch
645 session = DBConn().session()
647 q = session.query(PoolFile).filter_by(filename=filename)
648 q = q.join(Location).filter_by(location_id=location_id)
656 if obj.md5sum != md5sum or obj.filesize != filesize:
661 __all__.append('check_poolfile')
664 def get_poolfile_by_name(filename, location_id=None, session=None):
666 Returns an array of PoolFile objects for the given filename and
667 (optionally) location_id
669 @type filename: string
670 @param filename: the filename of the file to check against the DB
672 @type location_id: int
673 @param location_id: the id of the location to look in (optional)
676 @return: array of PoolFile objects
680 session = DBConn().session()
682 q = session.query(PoolFile).filter_by(filename=filename)
684 if location_id is not None:
685 q = q.join(Location).filter_by(location_id=location_id)
689 __all__.append('get_poolfile_by_name')
691 def get_poolfile_like_name(filename, session=None):
693 Returns an array of PoolFile objects which are like the given name
695 @type filename: string
696 @param filename: the filename of the file to check against the DB
699 @return: array of PoolFile objects
703 session = DBConn().session()
705 # TODO: There must be a way of properly using bind parameters with %FOO%
706 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
710 __all__.append('get_poolfile_like_name')
712 ################################################################################
714 class Fingerprint(object):
715 def __init__(self, *args, **kwargs):
719 return '<Fingerprint %s>' % self.fingerprint
721 __all__.append('Fingerprint')
723 def get_or_set_fingerprint(fpr, session=None):
725 Returns Fingerprint object for given fpr.
727 If no matching fpr is found, a row is inserted.
730 @param fpr: The fpr to find / add
732 @type session: SQLAlchemy
733 @param session: Optional SQL session object (a temporary one will be
734 generated if not supplied). If not passed, a commit will be performed at
735 the end of the function, otherwise the caller is responsible for commiting.
736 A flush will be performed either way.
739 @return: the Fingerprint object for the given fpr
743 session = DBConn().session()
747 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
749 fingerprint = Fingerprint()
750 fingerprint.fingerprint = fpr
751 session.add(fingerprint)
761 traceback.print_exc()
764 __all__.append('get_or_set_fingerprint')
766 ################################################################################
768 class Keyring(object):
769 def __init__(self, *args, **kwargs):
773 return '<Keyring %s>' % self.keyring_name
775 __all__.append('Keyring')
777 ################################################################################
779 class Location(object):
780 def __init__(self, *args, **kwargs):
784 return '<Location %s (%s)>' % (self.path, self.location_id)
786 __all__.append('Location')
788 def get_location(location, component=None, archive=None, session=None):
790 Returns Location object for the given combination of location, component
793 @type location: string
794 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
796 @type component: string
797 @param component: the component name (if None, no restriction applied)
799 @type archive: string
800 @param archive_id: the archive name (if None, no restriction applied)
802 @rtype: Location / None
803 @return: Either a Location object or None if one can't be found
807 session = DBConn().session()
809 q = session.query(Location).filter_by(path=location)
811 if archive is not None:
812 q = q.join(Archive).filter_by(archive_name=archive)
814 if component is not None:
815 q = q.join(Component).filter_by(component_name=component)
822 __all__.append('get_location')
824 ################################################################################
826 class Maintainer(object):
827 def __init__(self, *args, **kwargs):
831 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
833 def get_split_maintainer(self):
834 if not hasattr(self, 'name') or self.name is None:
835 return ('', '', '', '')
837 return fix_maintainer(self.name.strip())
839 __all__.append('Maintainer')
841 def get_or_set_maintainer(name, session=None):
843 Returns Maintainer object for given maintainer name.
845 If no matching maintainer name is found, a row is inserted.
848 @param name: The maintainer name to add
850 @type session: SQLAlchemy
851 @param session: Optional SQL session object (a temporary one will be
852 generated if not supplied). If not passed, a commit will be performed at
853 the end of the function, otherwise the caller is responsible for commiting.
854 A flush will be performed either way.
857 @return: the Maintainer object for the given maintainer
861 session = DBConn().session()
865 q = session.query(Maintainer).filter_by(name=name)
867 maintainer = Maintainer()
868 maintainer.name = name
869 session.add(maintainer)
879 traceback.print_exc()
882 __all__.append('get_or_set_maintainer')
884 ################################################################################
886 class NewComment(object):
887 def __init__(self, *args, **kwargs):
891 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
893 __all__.append('NewComment')
895 def has_new_comment(package, version, session=None):
897 Returns true if the given combination of C{package}, C{version} has a comment.
899 @type package: string
900 @param package: name of the package
902 @type version: string
903 @param version: package version
905 @type session: Session
906 @param session: Optional SQLA session object (a temporary one will be
907 generated if not supplied)
914 session = DBConn().session()
916 q = session.query(NewComment)
917 q = q.filter_by(package=package)
918 q = q.filter_by(version=version)
921 __all__.append('has_new_comment')
923 def get_new_comments(package=None, version=None, comment_id=None, session=None):
925 Returns (possibly empty) list of NewComment objects for the given
928 @type package: string (optional)
929 @param package: name of the package
931 @type version: string (optional)
932 @param version: package version
934 @type comment_id: int (optional)
935 @param comment_id: An id of a comment
937 @type session: Session
938 @param session: Optional SQLA session object (a temporary one will be
939 generated if not supplied)
942 @return: A (possibly empty) list of NewComment objects will be returned
947 session = DBConn().session()
949 q = session.query(NewComment)
950 if package is not None: q = q.filter_by(package=package)
951 if version is not None: q = q.filter_by(version=version)
952 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
956 __all__.append('get_new_comments')
958 ################################################################################
960 class Override(object):
961 def __init__(self, *args, **kwargs):
965 return '<Override %s (%s)>' % (self.package, self.suite_id)
967 __all__.append('Override')
969 def get_override(package, suite=None, component=None, overridetype=None, session=None):
971 Returns Override object for the given parameters
973 @type package: string
974 @param package: The name of the package
976 @type suite: string, list or None
977 @param suite: The name of the suite (or suites if a list) to limit to. If
978 None, don't limit. Defaults to None.
980 @type component: string, list or None
981 @param component: The name of the component (or components if a list) to
982 limit to. If None, don't limit. Defaults to None.
984 @type overridetype: string, list or None
985 @param overridetype: The name of the overridetype (or overridetypes if a list) to
986 limit to. If None, don't limit. Defaults to None.
988 @type session: Session
989 @param session: Optional SQLA session object (a temporary one will be
990 generated if not supplied)
993 @return: A (possibly empty) list of Override objects will be returned
997 session = DBConn().session()
999 q = session.query(Override)
1000 q = q.filter_by(package=package)
1002 if suite is not None:
1003 if not isinstance(suite, list): suite = [suite]
1004 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1006 if component is not None:
1007 if not isinstance(component, list): component = [component]
1008 q = q.join(Component).filter(Component.component_name.in_(component))
1010 if overridetype is not None:
1011 if not isinstance(overridetype, list): overridetype = [overridetype]
1012 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1016 __all__.append('get_override')
1019 ################################################################################
1021 class OverrideType(object):
1022 def __init__(self, *args, **kwargs):
1026 return '<OverrideType %s>' % self.overridetype
1028 __all__.append('OverrideType')
1030 def get_override_type(override_type, session=None):
1032 Returns OverrideType object for given C{override type}.
1034 @type override_type: string
1035 @param override_type: The name of the override type
1037 @type session: Session
1038 @param session: Optional SQLA session object (a temporary one will be
1039 generated if not supplied)
1042 @return: the database id for the given override type
1046 session = DBConn().session()
1047 q = session.query(OverrideType).filter_by(overridetype=override_type)
1052 __all__.append('get_override_type')
1054 ################################################################################
1056 class PendingContentAssociation(object):
1057 def __init__(self, *args, **kwargs):
1061 return '<PendingContentAssociation %s>' % self.pca_id
1063 __all__.append('PendingContentAssociation')
1065 def insert_pending_content_paths(package, fullpaths, session=None):
1067 Make sure given paths are temporarily associated with given
1071 @param package: the package to associate with should have been read in from the binary control file
1072 @type fullpaths: list
1073 @param fullpaths: the list of paths of the file being associated with the binary
1074 @type session: SQLAlchemy session
1075 @param session: Optional SQLAlchemy session. If this is passed, the caller
1076 is responsible for ensuring a transaction has begun and committing the
1077 results or rolling back based on the result code. If not passed, a commit
1078 will be performed at the end of the function
1080 @return: True upon success, False if there is a problem
1083 privatetrans = False
1086 session = DBConn().session()
1090 arch = get_architecture(package['Architecture'], session)
1091 arch_id = arch.arch_id
1093 # Remove any already existing recorded files for this package
1094 q = session.query(PendingContentAssociation)
1095 q = q.filter_by(package=package['Package'])
1096 q = q.filter_by(version=package['Version'])
1097 q = q.filter_by(architecture=arch_id)
1101 for fullpath in fullpaths:
1102 (path, file) = os.path.split(fullpath)
1104 if path.startswith( "./" ):
1107 pca = PendingContentAssociation()
1108 pca.package = package['Package']
1109 pca.version = package['Version']
1110 pca.filename_id = get_or_set_contents_file_id(file, session)
1111 pca.filepath_id = get_or_set_contents_path_id(path, session)
1112 pca.architecture = arch_id
1115 # Only commit if we set up the session ourself
1121 traceback.print_exc()
1123 # Only rollback if we set up the session ourself
1129 __all__.append('insert_pending_content_paths')
1131 ################################################################################
1133 class Priority(object):
1134 def __init__(self, *args, **kwargs):
1137 def __eq__(self, val):
1138 if isinstance(val, str):
1139 return (self.priority == val)
1140 # This signals to use the normal comparison operator
1141 return NotImplemented
1143 def __ne__(self, val):
1144 if isinstance(val, str):
1145 return (self.priority != val)
1146 # This signals to use the normal comparison operator
1147 return NotImplemented
1150 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1152 __all__.append('Priority')
1154 def get_priority(priority, session=None):
1156 Returns Priority object for given C{priority name}.
1158 @type priority: string
1159 @param priority: The name of the priority
1161 @type session: Session
1162 @param session: Optional SQLA session object (a temporary one will be
1163 generated if not supplied)
1166 @return: Priority object for the given priority
1170 session = DBConn().session()
1171 q = session.query(Priority).filter_by(priority=priority)
1176 __all__.append('get_priority')
1178 ################################################################################
1180 class Queue(object):
1181 def __init__(self, *args, **kwargs):
1185 return '<Queue %s>' % self.queue_name
1187 def autobuild_upload(self, changes, srcpath, session=None):
1189 Update queue_build database table used for incoming autobuild support.
1191 @type changes: Changes
1192 @param changes: changes object for the upload to process
1194 @type srcpath: string
1195 @param srcpath: path for the queue file entries/link destinations
1197 @type session: SQLAlchemy session
1198 @param session: Optional SQLAlchemy session. If this is passed, the
1199 caller is responsible for ensuring a transaction has begun and
1200 committing the results or rolling back based on the result code. If
1201 not passed, a commit will be performed at the end of the function,
1202 otherwise the caller is responsible for commiting.
1204 @rtype: NoneType or string
1205 @return: None if the operation failed, a string describing the error if not
1210 session = DBConn().session()
1213 # TODO: Remove by moving queue config into the database
1216 for suitename in changes.changes["distribution"].keys():
1217 # TODO: Move into database as:
1218 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1219 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1220 # This also gets rid of the SecurityQueueBuild hack below
1221 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1225 s = get_suite(suitename, session)
1227 return "INTERNAL ERROR: Could not find suite %s" % suitename
1229 # TODO: Get from database as above
1230 dest_dir = conf["Dir::QueueBuild"]
1232 # TODO: Move into database as above
1233 if conf.FindB("Dinstall::SecurityQueueBuild"):
1234 dest_dir = os.path.join(dest_dir, suitename)
1236 for file_entry in changes.files.keys():
1237 src = os.path.join(srcpath, file_entry)
1238 dest = os.path.join(dest_dir, file_entry)
1240 # TODO: Move into database as above
1241 if Cnf.FindB("Dinstall::SecurityQueueBuild"):
1242 # Copy it since the original won't be readable by www-data
1243 utils.copy(src, dest)
1245 # Create a symlink to it
1246 os.symlink(src, dest)
1249 qb.suite_id = s.suite_id
1250 qb.queue_id = self.queue_id
1256 # If the .orig.tar.gz is in the pool, create a symlink to
1257 # it (if one doesn't already exist)
1258 if changes.orig_tar_id:
1259 # Determine the .orig.tar.gz file name
1260 for dsc_file in changes.dsc_files.keys():
1261 if dsc_file.endswith(".orig.tar.gz"):
1264 dest = os.path.join(dest_dir, filename)
1266 # If it doesn't exist, create a symlink
1267 if not os.path.exists(dest):
1268 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1269 {'id': changes.orig_tar_id})
1272 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1274 src = os.path.join(res[0], res[1])
1275 os.symlink(src, dest)
1277 # Add it to the list of packages for later processing by apt-ftparchive
1279 qb.suite_id = s.suite_id
1280 qb.queue_id = self.queue_id
1285 # If it does, update things to ensure it's not removed prematurely
1287 qb = get_queue_build(dest, suite_id, session)
1298 __all__.append('Queue')
1300 def get_queue(queuename, session=None):
1302 Returns Queue object for given C{queue name}.
1304 @type queuename: string
1305 @param queuename: The name of the queue
1307 @type session: Session
1308 @param session: Optional SQLA session object (a temporary one will be
1309 generated if not supplied)
1312 @return: Queue object for the given queue
1316 session = DBConn().session()
1317 q = session.query(Queue).filter_by(queue_name=queuename)
1322 __all__.append('get_queue')
1324 ################################################################################
1326 class QueueBuild(object):
1327 def __init__(self, *args, **kwargs):
1331 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1333 __all__.append('QueueBuild')
1335 def get_queue_build(filename, suite_id, session=None):
1337 Returns QueueBuild object for given C{filename} and C{suite id}.
1339 @type filename: string
1340 @param filename: The name of the file
1343 @param suiteid: Suite ID
1345 @type session: Session
1346 @param session: Optional SQLA session object (a temporary one will be
1347 generated if not supplied)
1350 @return: Queue object for the given queue
1354 session = DBConn().session()
1355 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite_id)
1360 __all__.append('get_queue_build')
1362 ################################################################################
1364 class Section(object):
1365 def __init__(self, *args, **kwargs):
1368 def __eq__(self, val):
1369 if isinstance(val, str):
1370 return (self.section == val)
1371 # This signals to use the normal comparison operator
1372 return NotImplemented
1374 def __ne__(self, val):
1375 if isinstance(val, str):
1376 return (self.section != val)
1377 # This signals to use the normal comparison operator
1378 return NotImplemented
1381 return '<Section %s>' % self.section
1383 __all__.append('Section')
1385 def get_section(section, session=None):
1387 Returns Section object for given C{section name}.
1389 @type section: string
1390 @param section: The name of the section
1392 @type session: Session
1393 @param session: Optional SQLA session object (a temporary one will be
1394 generated if not supplied)
1397 @return: Section object for the given section name
1401 session = DBConn().session()
1402 q = session.query(Section).filter_by(section=section)
1407 __all__.append('get_section')
1409 ################################################################################
1411 class DBSource(object):
1412 def __init__(self, *args, **kwargs):
1416 return '<DBSource %s (%s)>' % (self.source, self.version)
1418 __all__.append('DBSource')
1420 def source_exists(source, source_version, suites = ["any"], session=None):
1422 Ensure that source exists somewhere in the archive for the binary
1423 upload being processed.
1424 1. exact match => 1.0-3
1425 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1427 @type package: string
1428 @param package: package source name
1430 @type source_version: string
1431 @param source_version: expected source version
1434 @param suites: list of suites to check in, default I{any}
1436 @type session: Session
1437 @param session: Optional SQLA session object (a temporary one will be
1438 generated if not supplied)
1441 @return: returns 1 if a source with expected version is found, otherwise 0
1446 session = DBConn().session()
1450 for suite in suites:
1451 q = session.query(DBSource).filter_by(source=source)
1453 # source must exist in suite X, or in some other suite that's
1454 # mapped to X, recursively... silent-maps are counted too,
1455 # unreleased-maps aren't.
1456 maps = cnf.ValueList("SuiteMappings")[:]
1458 maps = [ m.split() for m in maps ]
1459 maps = [ (x[1], x[2]) for x in maps
1460 if x[0] == "map" or x[0] == "silent-map" ]
1463 if x[1] in s and x[0] not in s:
1466 q = q.join(SrcAssociation).join(Suite)
1467 q = q.filter(Suite.suite_name.in_(s))
1469 # Reduce the query results to a list of version numbers
1470 ql = [ j.version for j in q.all() ]
1473 if source_version in ql:
1477 from daklib.regexes import re_bin_only_nmu
1478 orig_source_version = re_bin_only_nmu.sub('', source_version)
1479 if orig_source_version in ql:
1482 # No source found so return not ok
1488 __all__.append('source_exists')
1490 def get_suites_source_in(source, session=None):
1492 Returns list of Suite objects which given C{source} name is in
1495 @param source: DBSource package name to search for
1498 @return: list of Suite objects for the given source
1502 session = DBConn().session()
1504 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1506 __all__.append('get_suites_source_in')
1508 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1510 Returns list of DBSource objects for given C{source} name and other parameters
1513 @param source: DBSource package name to search for
1515 @type source: str or None
1516 @param source: DBSource version name to search for or None if not applicable
1518 @type dm_upload_allowed: bool
1519 @param dm_upload_allowed: If None, no effect. If True or False, only
1520 return packages with that dm_upload_allowed setting
1522 @type session: Session
1523 @param session: Optional SQL session object (a temporary one will be
1524 generated if not supplied)
1527 @return: list of DBSource objects for the given name (may be empty)
1530 session = DBConn().session()
1532 q = session.query(DBSource).filter_by(source=source)
1534 if version is not None:
1535 q = q.filter_by(version=version)
1537 if dm_upload_allowed is not None:
1538 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1542 __all__.append('get_sources_from_name')
1544 def get_source_in_suite(source, suite, session=None):
1546 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1548 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1549 - B{suite} - a suite name, eg. I{unstable}
1551 @type source: string
1552 @param source: source package name
1555 @param suite: the suite name
1558 @return: the version for I{source} in I{suite}
1562 session = DBConn().session()
1563 q = session.query(SrcAssociation)
1564 q = q.join('source').filter_by(source=source)
1565 q = q.join('suite').filter_by(suite_name=suite)
1568 # ???: Maybe we should just return the SrcAssociation object instead
1569 return q.one().source
1571 __all__.append('get_source_in_suite')
1573 ################################################################################
1575 class SrcAssociation(object):
1576 def __init__(self, *args, **kwargs):
1580 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1582 __all__.append('SrcAssociation')
1584 ################################################################################
1586 class SrcUploader(object):
1587 def __init__(self, *args, **kwargs):
1591 return '<SrcUploader %s>' % self.uploader_id
1593 __all__.append('SrcUploader')
1595 ################################################################################
1597 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1598 ('SuiteID', 'suite_id'),
1599 ('Version', 'version'),
1600 ('Origin', 'origin'),
1602 ('Description', 'description'),
1603 ('Untouchable', 'untouchable'),
1604 ('Announce', 'announce'),
1605 ('Codename', 'codename'),
1606 ('OverrideCodename', 'overridecodename'),
1607 ('ValidTime', 'validtime'),
1608 ('Priority', 'priority'),
1609 ('NotAutomatic', 'notautomatic'),
1610 ('CopyChanges', 'copychanges'),
1611 ('CopyDotDak', 'copydotdak'),
1612 ('CommentsDir', 'commentsdir'),
1613 ('OverrideSuite', 'overridesuite'),
1614 ('ChangelogBase', 'changelogbase')]
1617 class Suite(object):
1618 def __init__(self, *args, **kwargs):
1622 return '<Suite %s>' % self.suite_name
1624 def __eq__(self, val):
1625 if isinstance(val, str):
1626 return (self.suite_name == val)
1627 # This signals to use the normal comparison operator
1628 return NotImplemented
1630 def __ne__(self, val):
1631 if isinstance(val, str):
1632 return (self.suite_name != val)
1633 # This signals to use the normal comparison operator
1634 return NotImplemented
1638 for disp, field in SUITE_FIELDS:
1639 val = getattr(self, field, None)
1641 ret.append("%s: %s" % (disp, val))
1643 return "\n".join(ret)
1645 __all__.append('Suite')
1647 def get_suite_architecture(suite, architecture, session=None):
1649 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1653 @param suite: Suite name to search for
1655 @type architecture: str
1656 @param architecture: Architecture name to search for
1658 @type session: Session
1659 @param session: Optional SQL session object (a temporary one will be
1660 generated if not supplied)
1662 @rtype: SuiteArchitecture
1663 @return: the SuiteArchitecture object or None
1667 session = DBConn().session()
1669 q = session.query(SuiteArchitecture)
1670 q = q.join(Architecture).filter_by(arch_string=architecture)
1671 q = q.join(Suite).filter_by(suite_name=suite)
1676 __all__.append('get_suite_architecture')
1678 def get_suite(suite, session=None):
1680 Returns Suite object for given C{suite name}.
1683 @param suite: The name of the suite
1685 @type session: Session
1686 @param session: Optional SQLA session object (a temporary one will be
1687 generated if not supplied)
1690 @return: Suite object for the requested suite name (None if not presenT)
1694 session = DBConn().session()
1695 q = session.query(Suite).filter_by(suite_name=suite)
1700 __all__.append('get_suite')
1702 ################################################################################
1704 class SuiteArchitecture(object):
1705 def __init__(self, *args, **kwargs):
1709 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1711 __all__.append('SuiteArchitecture')
1713 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1715 Returns list of Architecture objects for given C{suite} name
1718 @param source: Suite name to search for
1720 @type skipsrc: boolean
1721 @param skipsrc: Whether to skip returning the 'source' architecture entry
1724 @type skipall: boolean
1725 @param skipall: Whether to skip returning the 'all' architecture entry
1728 @type session: Session
1729 @param session: Optional SQL session object (a temporary one will be
1730 generated if not supplied)
1733 @return: list of Architecture objects for the given name (may be empty)
1737 session = DBConn().session()
1739 q = session.query(Architecture)
1740 q = q.join(SuiteArchitecture)
1741 q = q.join(Suite).filter_by(suite_name=suite)
1743 q = q.filter(Architecture.arch_string != 'source')
1745 q = q.filter(Architecture.arch_string != 'all')
1746 q = q.order_by('arch_string')
1749 __all__.append('get_suite_architectures')
1751 ################################################################################
1754 def __init__(self, *args, **kwargs):
1757 def __eq__(self, val):
1758 if isinstance(val, str):
1759 return (self.uid == val)
1760 # This signals to use the normal comparison operator
1761 return NotImplemented
1763 def __ne__(self, val):
1764 if isinstance(val, str):
1765 return (self.uid != val)
1766 # This signals to use the normal comparison operator
1767 return NotImplemented
1770 return '<Uid %s (%s)>' % (self.uid, self.name)
1772 __all__.append('Uid')
1774 def add_database_user(uidname, session=None):
1776 Adds a database user
1778 @type uidname: string
1779 @param uidname: The uid of the user to add
1781 @type session: SQLAlchemy
1782 @param session: Optional SQL session object (a temporary one will be
1783 generated if not supplied). If not passed, a commit will be performed at
1784 the end of the function, otherwise the caller is responsible for commiting.
1787 @return: the uid object for the given uidname
1789 privatetrans = False
1791 session = DBConn().session()
1795 session.execute("CREATE USER :uid", {'uid': uidname})
1799 traceback.print_exc()
1802 __all__.append('add_database_user')
1804 def get_or_set_uid(uidname, session=None):
1806 Returns uid object for given uidname.
1808 If no matching uidname is found, a row is inserted.
1810 @type uidname: string
1811 @param uidname: The uid to add
1813 @type session: SQLAlchemy
1814 @param session: Optional SQL session object (a temporary one will be
1815 generated if not supplied). If not passed, a commit will be performed at
1816 the end of the function, otherwise the caller is responsible for commiting.
1819 @return: the uid object for the given uidname
1821 privatetrans = False
1823 session = DBConn().session()
1827 q = session.query(Uid).filter_by(uid=uidname)
1841 traceback.print_exc()
1844 __all__.append('get_or_set_uid')
1847 def get_uid_from_fingerprint(fpr, session=None):
1849 session = DBConn().session()
1851 q = session.query(Uid)
1852 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1859 __all__.append('get_uid_from_fingerprint')
1861 ################################################################################
1863 class DBConn(Singleton):
1865 database module init.
1867 def __init__(self, *args, **kwargs):
1868 super(DBConn, self).__init__(*args, **kwargs)
1870 def _startup(self, *args, **kwargs):
1872 if kwargs.has_key('debug'):
1876 def __setuptables(self):
1877 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1878 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1879 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1880 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1881 self.tbl_component = Table('component', self.db_meta, autoload=True)
1882 self.tbl_config = Table('config', self.db_meta, autoload=True)
1883 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1884 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1885 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1886 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1887 self.tbl_files = Table('files', self.db_meta, autoload=True)
1888 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1889 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1890 self.tbl_location = Table('location', self.db_meta, autoload=True)
1891 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
1892 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
1893 self.tbl_override = Table('override', self.db_meta, autoload=True)
1894 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
1895 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
1896 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
1897 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
1898 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
1899 self.tbl_section = Table('section', self.db_meta, autoload=True)
1900 self.tbl_source = Table('source', self.db_meta, autoload=True)
1901 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
1902 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
1903 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
1904 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
1905 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
1907 def __setupmappers(self):
1908 mapper(Architecture, self.tbl_architecture,
1909 properties = dict(arch_id = self.tbl_architecture.c.id))
1911 mapper(Archive, self.tbl_archive,
1912 properties = dict(archive_id = self.tbl_archive.c.id,
1913 archive_name = self.tbl_archive.c.name))
1915 mapper(BinAssociation, self.tbl_bin_associations,
1916 properties = dict(ba_id = self.tbl_bin_associations.c.id,
1917 suite_id = self.tbl_bin_associations.c.suite,
1918 suite = relation(Suite),
1919 binary_id = self.tbl_bin_associations.c.bin,
1920 binary = relation(DBBinary)))
1922 mapper(DBBinary, self.tbl_binaries,
1923 properties = dict(binary_id = self.tbl_binaries.c.id,
1924 package = self.tbl_binaries.c.package,
1925 version = self.tbl_binaries.c.version,
1926 maintainer_id = self.tbl_binaries.c.maintainer,
1927 maintainer = relation(Maintainer),
1928 source_id = self.tbl_binaries.c.source,
1929 source = relation(DBSource),
1930 arch_id = self.tbl_binaries.c.architecture,
1931 architecture = relation(Architecture),
1932 poolfile_id = self.tbl_binaries.c.file,
1933 poolfile = relation(PoolFile),
1934 binarytype = self.tbl_binaries.c.type,
1935 fingerprint_id = self.tbl_binaries.c.sig_fpr,
1936 fingerprint = relation(Fingerprint),
1937 install_date = self.tbl_binaries.c.install_date,
1938 binassociations = relation(BinAssociation,
1939 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
1941 mapper(Component, self.tbl_component,
1942 properties = dict(component_id = self.tbl_component.c.id,
1943 component_name = self.tbl_component.c.name))
1945 mapper(DBConfig, self.tbl_config,
1946 properties = dict(config_id = self.tbl_config.c.id))
1948 mapper(ContentAssociation, self.tbl_content_associations,
1949 properties = dict(ca_id = self.tbl_content_associations.c.id,
1950 filename_id = self.tbl_content_associations.c.filename,
1951 filename = relation(ContentFilename),
1952 filepath_id = self.tbl_content_associations.c.filepath,
1953 filepath = relation(ContentFilepath),
1954 binary_id = self.tbl_content_associations.c.binary_pkg,
1955 binary = relation(DBBinary)))
1958 mapper(ContentFilename, self.tbl_content_file_names,
1959 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
1960 filename = self.tbl_content_file_names.c.file))
1962 mapper(ContentFilepath, self.tbl_content_file_paths,
1963 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
1964 filepath = self.tbl_content_file_paths.c.path))
1966 mapper(DSCFile, self.tbl_dsc_files,
1967 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
1968 source_id = self.tbl_dsc_files.c.source,
1969 source = relation(DBSource),
1970 poolfile_id = self.tbl_dsc_files.c.file,
1971 poolfile = relation(PoolFile)))
1973 mapper(PoolFile, self.tbl_files,
1974 properties = dict(file_id = self.tbl_files.c.id,
1975 filesize = self.tbl_files.c.size,
1976 location_id = self.tbl_files.c.location,
1977 location = relation(Location)))
1979 mapper(Fingerprint, self.tbl_fingerprint,
1980 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
1981 uid_id = self.tbl_fingerprint.c.uid,
1982 uid = relation(Uid),
1983 keyring_id = self.tbl_fingerprint.c.keyring,
1984 keyring = relation(Keyring)))
1986 mapper(Keyring, self.tbl_keyrings,
1987 properties = dict(keyring_name = self.tbl_keyrings.c.name,
1988 keyring_id = self.tbl_keyrings.c.id))
1990 mapper(Location, self.tbl_location,
1991 properties = dict(location_id = self.tbl_location.c.id,
1992 component_id = self.tbl_location.c.component,
1993 component = relation(Component),
1994 archive_id = self.tbl_location.c.archive,
1995 archive = relation(Archive),
1996 archive_type = self.tbl_location.c.type))
1998 mapper(Maintainer, self.tbl_maintainer,
1999 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2001 mapper(NewComment, self.tbl_new_comments,
2002 properties = dict(comment_id = self.tbl_new_comments.c.id))
2004 mapper(Override, self.tbl_override,
2005 properties = dict(suite_id = self.tbl_override.c.suite,
2006 suite = relation(Suite),
2007 component_id = self.tbl_override.c.component,
2008 component = relation(Component),
2009 priority_id = self.tbl_override.c.priority,
2010 priority = relation(Priority),
2011 section_id = self.tbl_override.c.section,
2012 section = relation(Section),
2013 overridetype_id = self.tbl_override.c.type,
2014 overridetype = relation(OverrideType)))
2016 mapper(OverrideType, self.tbl_override_type,
2017 properties = dict(overridetype = self.tbl_override_type.c.type,
2018 overridetype_id = self.tbl_override_type.c.id))
2020 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2021 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2022 filepath_id = self.tbl_pending_content_associations.c.filepath,
2023 filepath = relation(ContentFilepath),
2024 filename_id = self.tbl_pending_content_associations.c.filename,
2025 filename = relation(ContentFilename)))
2027 mapper(Priority, self.tbl_priority,
2028 properties = dict(priority_id = self.tbl_priority.c.id))
2030 mapper(Queue, self.tbl_queue,
2031 properties = dict(queue_id = self.tbl_queue.c.id))
2033 mapper(QueueBuild, self.tbl_queue_build,
2034 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2035 queue_id = self.tbl_queue_build.c.queue,
2036 queue = relation(Queue, backref='queuebuild')))
2038 mapper(Section, self.tbl_section,
2039 properties = dict(section_id = self.tbl_section.c.id))
2041 mapper(DBSource, self.tbl_source,
2042 properties = dict(source_id = self.tbl_source.c.id,
2043 version = self.tbl_source.c.version,
2044 maintainer_id = self.tbl_source.c.maintainer,
2045 maintainer = relation(Maintainer,
2046 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2047 poolfile_id = self.tbl_source.c.file,
2048 poolfile = relation(PoolFile),
2049 fingerprint_id = self.tbl_source.c.sig_fpr,
2050 fingerprint = relation(Fingerprint),
2051 changedby_id = self.tbl_source.c.changedby,
2052 changedby = relation(Maintainer,
2053 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2054 srcfiles = relation(DSCFile,
2055 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2056 srcassociations = relation(SrcAssociation,
2057 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2059 mapper(SrcAssociation, self.tbl_src_associations,
2060 properties = dict(sa_id = self.tbl_src_associations.c.id,
2061 suite_id = self.tbl_src_associations.c.suite,
2062 suite = relation(Suite),
2063 source_id = self.tbl_src_associations.c.source,
2064 source = relation(DBSource)))
2066 mapper(SrcUploader, self.tbl_src_uploaders,
2067 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2068 source_id = self.tbl_src_uploaders.c.source,
2069 source = relation(DBSource,
2070 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2071 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2072 maintainer = relation(Maintainer,
2073 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2075 mapper(Suite, self.tbl_suite,
2076 properties = dict(suite_id = self.tbl_suite.c.id))
2078 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2079 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2080 suite = relation(Suite, backref='suitearchitectures'),
2081 arch_id = self.tbl_suite_architectures.c.architecture,
2082 architecture = relation(Architecture)))
2084 mapper(Uid, self.tbl_uid,
2085 properties = dict(uid_id = self.tbl_uid.c.id,
2086 fingerprint = relation(Fingerprint)))
2088 ## Connection functions
2089 def __createconn(self):
2090 from config import Config
2094 connstr = "postgres://%s" % cnf["DB::Host"]
2095 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2096 connstr += ":%s" % cnf["DB::Port"]
2097 connstr += "/%s" % cnf["DB::Name"]
2100 connstr = "postgres:///%s" % cnf["DB::Name"]
2101 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2102 connstr += "?port=%s" % cnf["DB::Port"]
2104 self.db_pg = create_engine(connstr, echo=self.debug)
2105 self.db_meta = MetaData()
2106 self.db_meta.bind = self.db_pg
2107 self.db_smaker = sessionmaker(bind=self.db_pg,
2111 self.__setuptables()
2112 self.__setupmappers()
2115 return self.db_smaker()
2117 __all__.append('DBConn')