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 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
608 Returns a list of DSCFiles which may be empty
610 @type dscfile_id: int (optional)
611 @param dscfile_id: the dscfile_id of the DSCFiles to find
613 @type source_id: int (optional)
614 @param source_id: the source id related to the DSCFiles to find
616 @type poolfile_id: int (optional)
617 @param poolfile_id: the poolfile id related to the DSCFiles to find
620 @return: Possibly empty list of DSCFiles
624 session = DBConn().session()
626 q = session.query(DSCFile)
628 if dscfile_id is not None:
629 q = q.filter_by(dscfile_id=dscfile_id)
631 if source_id is not None:
632 q = q.filter_by(source_id=source_id)
634 if poolfile_id is not None:
635 q = q.filter_by(poolfile_id=poolfile_id)
639 __all__.append('get_dscfiles')
641 ################################################################################
643 class PoolFile(object):
644 def __init__(self, *args, **kwargs):
648 return '<PoolFile %s>' % self.filename
650 __all__.append('PoolFile')
652 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
655 (ValidFileFound [boolean or None], PoolFile object or None)
657 @type filename: string
658 @param filename: the filename of the file to check against the DB
661 @param filesize: the size of the file to check against the DB
664 @param md5sum: the md5sum of the file to check against the DB
666 @type location_id: int
667 @param location_id: the id of the location to look in
670 @return: Tuple of length 2.
671 If more than one file found with that name:
673 If valid pool file found: (True, PoolFile object)
674 If valid pool file not found:
675 (False, None) if no file found
676 (False, PoolFile object) if file found with size/md5sum mismatch
680 session = DBConn().session()
682 q = session.query(PoolFile).filter_by(filename=filename)
683 q = q.join(Location).filter_by(location_id=location_id)
691 if obj.md5sum != md5sum or obj.filesize != filesize:
696 __all__.append('check_poolfile')
698 def get_poolfile_by_id(file_id, session=None):
700 Returns a PoolFile objects or None for the given id
703 @param file_id: the id of the file to look for
705 @rtype: PoolFile or None
706 @return: either the PoolFile object or None
710 session = DBConn().session()
712 q = session.query(PoolFile).filter_by(file_id=file_id)
719 __all__.append('get_poolfile_by_id')
722 def get_poolfile_by_name(filename, location_id=None, session=None):
724 Returns an array of PoolFile objects for the given filename and
725 (optionally) location_id
727 @type filename: string
728 @param filename: the filename of the file to check against the DB
730 @type location_id: int
731 @param location_id: the id of the location to look in (optional)
734 @return: array of PoolFile objects
738 session = DBConn().session()
740 q = session.query(PoolFile).filter_by(filename=filename)
742 if location_id is not None:
743 q = q.join(Location).filter_by(location_id=location_id)
747 __all__.append('get_poolfile_by_name')
749 def get_poolfile_like_name(filename, session=None):
751 Returns an array of PoolFile objects which are like the given name
753 @type filename: string
754 @param filename: the filename of the file to check against the DB
757 @return: array of PoolFile objects
761 session = DBConn().session()
763 # TODO: There must be a way of properly using bind parameters with %FOO%
764 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
768 __all__.append('get_poolfile_like_name')
770 ################################################################################
772 class Fingerprint(object):
773 def __init__(self, *args, **kwargs):
777 return '<Fingerprint %s>' % self.fingerprint
779 __all__.append('Fingerprint')
781 def get_or_set_fingerprint(fpr, session=None):
783 Returns Fingerprint object for given fpr.
785 If no matching fpr is found, a row is inserted.
788 @param fpr: The fpr to find / add
790 @type session: SQLAlchemy
791 @param session: Optional SQL session object (a temporary one will be
792 generated if not supplied). If not passed, a commit will be performed at
793 the end of the function, otherwise the caller is responsible for commiting.
794 A flush will be performed either way.
797 @return: the Fingerprint object for the given fpr
801 session = DBConn().session()
805 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
807 fingerprint = Fingerprint()
808 fingerprint.fingerprint = fpr
809 session.add(fingerprint)
819 traceback.print_exc()
822 __all__.append('get_or_set_fingerprint')
824 ################################################################################
826 class Keyring(object):
827 def __init__(self, *args, **kwargs):
831 return '<Keyring %s>' % self.keyring_name
833 __all__.append('Keyring')
835 ################################################################################
837 class Location(object):
838 def __init__(self, *args, **kwargs):
842 return '<Location %s (%s)>' % (self.path, self.location_id)
844 __all__.append('Location')
846 def get_location(location, component=None, archive=None, session=None):
848 Returns Location object for the given combination of location, component
851 @type location: string
852 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
854 @type component: string
855 @param component: the component name (if None, no restriction applied)
857 @type archive: string
858 @param archive_id: the archive name (if None, no restriction applied)
860 @rtype: Location / None
861 @return: Either a Location object or None if one can't be found
865 session = DBConn().session()
867 q = session.query(Location).filter_by(path=location)
869 if archive is not None:
870 q = q.join(Archive).filter_by(archive_name=archive)
872 if component is not None:
873 q = q.join(Component).filter_by(component_name=component)
880 __all__.append('get_location')
882 ################################################################################
884 class Maintainer(object):
885 def __init__(self, *args, **kwargs):
889 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
891 def get_split_maintainer(self):
892 if not hasattr(self, 'name') or self.name is None:
893 return ('', '', '', '')
895 return fix_maintainer(self.name.strip())
897 __all__.append('Maintainer')
899 def get_or_set_maintainer(name, session=None):
901 Returns Maintainer object for given maintainer name.
903 If no matching maintainer name is found, a row is inserted.
906 @param name: The maintainer name to add
908 @type session: SQLAlchemy
909 @param session: Optional SQL session object (a temporary one will be
910 generated if not supplied). If not passed, a commit will be performed at
911 the end of the function, otherwise the caller is responsible for commiting.
912 A flush will be performed either way.
915 @return: the Maintainer object for the given maintainer
919 session = DBConn().session()
923 q = session.query(Maintainer).filter_by(name=name)
925 maintainer = Maintainer()
926 maintainer.name = name
927 session.add(maintainer)
937 traceback.print_exc()
940 __all__.append('get_or_set_maintainer')
942 ################################################################################
944 class NewComment(object):
945 def __init__(self, *args, **kwargs):
949 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
951 __all__.append('NewComment')
953 def has_new_comment(package, version, session=None):
955 Returns true if the given combination of C{package}, C{version} has a comment.
957 @type package: string
958 @param package: name of the package
960 @type version: string
961 @param version: package version
963 @type session: Session
964 @param session: Optional SQLA session object (a temporary one will be
965 generated if not supplied)
972 session = DBConn().session()
974 q = session.query(NewComment)
975 q = q.filter_by(package=package)
976 q = q.filter_by(version=version)
979 __all__.append('has_new_comment')
981 def get_new_comments(package=None, version=None, comment_id=None, session=None):
983 Returns (possibly empty) list of NewComment objects for the given
986 @type package: string (optional)
987 @param package: name of the package
989 @type version: string (optional)
990 @param version: package version
992 @type comment_id: int (optional)
993 @param comment_id: An id of a comment
995 @type session: Session
996 @param session: Optional SQLA session object (a temporary one will be
997 generated if not supplied)
1000 @return: A (possibly empty) list of NewComment objects will be returned
1005 session = DBConn().session()
1007 q = session.query(NewComment)
1008 if package is not None: q = q.filter_by(package=package)
1009 if version is not None: q = q.filter_by(version=version)
1010 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1014 __all__.append('get_new_comments')
1016 ################################################################################
1018 class Override(object):
1019 def __init__(self, *args, **kwargs):
1023 return '<Override %s (%s)>' % (self.package, self.suite_id)
1025 __all__.append('Override')
1027 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1029 Returns Override object for the given parameters
1031 @type package: string
1032 @param package: The name of the package
1034 @type suite: string, list or None
1035 @param suite: The name of the suite (or suites if a list) to limit to. If
1036 None, don't limit. Defaults to None.
1038 @type component: string, list or None
1039 @param component: The name of the component (or components if a list) to
1040 limit to. If None, don't limit. Defaults to None.
1042 @type overridetype: string, list or None
1043 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1044 limit to. If None, don't limit. Defaults to None.
1046 @type session: Session
1047 @param session: Optional SQLA session object (a temporary one will be
1048 generated if not supplied)
1051 @return: A (possibly empty) list of Override objects will be returned
1055 session = DBConn().session()
1057 q = session.query(Override)
1058 q = q.filter_by(package=package)
1060 if suite is not None:
1061 if not isinstance(suite, list): suite = [suite]
1062 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1064 if component is not None:
1065 if not isinstance(component, list): component = [component]
1066 q = q.join(Component).filter(Component.component_name.in_(component))
1068 if overridetype is not None:
1069 if not isinstance(overridetype, list): overridetype = [overridetype]
1070 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1074 __all__.append('get_override')
1077 ################################################################################
1079 class OverrideType(object):
1080 def __init__(self, *args, **kwargs):
1084 return '<OverrideType %s>' % self.overridetype
1086 __all__.append('OverrideType')
1088 def get_override_type(override_type, session=None):
1090 Returns OverrideType object for given C{override type}.
1092 @type override_type: string
1093 @param override_type: The name of the override type
1095 @type session: Session
1096 @param session: Optional SQLA session object (a temporary one will be
1097 generated if not supplied)
1100 @return: the database id for the given override type
1104 session = DBConn().session()
1105 q = session.query(OverrideType).filter_by(overridetype=override_type)
1110 __all__.append('get_override_type')
1112 ################################################################################
1114 class PendingContentAssociation(object):
1115 def __init__(self, *args, **kwargs):
1119 return '<PendingContentAssociation %s>' % self.pca_id
1121 __all__.append('PendingContentAssociation')
1123 def insert_pending_content_paths(package, fullpaths, session=None):
1125 Make sure given paths are temporarily associated with given
1129 @param package: the package to associate with should have been read in from the binary control file
1130 @type fullpaths: list
1131 @param fullpaths: the list of paths of the file being associated with the binary
1132 @type session: SQLAlchemy session
1133 @param session: Optional SQLAlchemy session. If this is passed, the caller
1134 is responsible for ensuring a transaction has begun and committing the
1135 results or rolling back based on the result code. If not passed, a commit
1136 will be performed at the end of the function
1138 @return: True upon success, False if there is a problem
1141 privatetrans = False
1144 session = DBConn().session()
1148 arch = get_architecture(package['Architecture'], session)
1149 arch_id = arch.arch_id
1151 # Remove any already existing recorded files for this package
1152 q = session.query(PendingContentAssociation)
1153 q = q.filter_by(package=package['Package'])
1154 q = q.filter_by(version=package['Version'])
1155 q = q.filter_by(architecture=arch_id)
1159 for fullpath in fullpaths:
1160 (path, file) = os.path.split(fullpath)
1162 if path.startswith( "./" ):
1165 pca = PendingContentAssociation()
1166 pca.package = package['Package']
1167 pca.version = package['Version']
1168 pca.filename_id = get_or_set_contents_file_id(file, session)
1169 pca.filepath_id = get_or_set_contents_path_id(path, session)
1170 pca.architecture = arch_id
1173 # Only commit if we set up the session ourself
1179 traceback.print_exc()
1181 # Only rollback if we set up the session ourself
1187 __all__.append('insert_pending_content_paths')
1189 ################################################################################
1191 class Priority(object):
1192 def __init__(self, *args, **kwargs):
1195 def __eq__(self, val):
1196 if isinstance(val, str):
1197 return (self.priority == val)
1198 # This signals to use the normal comparison operator
1199 return NotImplemented
1201 def __ne__(self, val):
1202 if isinstance(val, str):
1203 return (self.priority != val)
1204 # This signals to use the normal comparison operator
1205 return NotImplemented
1208 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1210 __all__.append('Priority')
1212 def get_priority(priority, session=None):
1214 Returns Priority object for given C{priority name}.
1216 @type priority: string
1217 @param priority: The name of the priority
1219 @type session: Session
1220 @param session: Optional SQLA session object (a temporary one will be
1221 generated if not supplied)
1224 @return: Priority object for the given priority
1228 session = DBConn().session()
1229 q = session.query(Priority).filter_by(priority=priority)
1234 __all__.append('get_priority')
1236 ################################################################################
1238 class Queue(object):
1239 def __init__(self, *args, **kwargs):
1243 return '<Queue %s>' % self.queue_name
1245 def autobuild_upload(self, changes, srcpath, session=None):
1247 Update queue_build database table used for incoming autobuild support.
1249 @type changes: Changes
1250 @param changes: changes object for the upload to process
1252 @type srcpath: string
1253 @param srcpath: path for the queue file entries/link destinations
1255 @type session: SQLAlchemy session
1256 @param session: Optional SQLAlchemy session. If this is passed, the
1257 caller is responsible for ensuring a transaction has begun and
1258 committing the results or rolling back based on the result code. If
1259 not passed, a commit will be performed at the end of the function,
1260 otherwise the caller is responsible for commiting.
1262 @rtype: NoneType or string
1263 @return: None if the operation failed, a string describing the error if not
1268 session = DBConn().session()
1271 # TODO: Remove by moving queue config into the database
1274 for suitename in changes.changes["distribution"].keys():
1275 # TODO: Move into database as:
1276 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1277 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1278 # This also gets rid of the SecurityQueueBuild hack below
1279 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1283 s = get_suite(suitename, session)
1285 return "INTERNAL ERROR: Could not find suite %s" % suitename
1287 # TODO: Get from database as above
1288 dest_dir = conf["Dir::QueueBuild"]
1290 # TODO: Move into database as above
1291 if conf.FindB("Dinstall::SecurityQueueBuild"):
1292 dest_dir = os.path.join(dest_dir, suitename)
1294 for file_entry in changes.files.keys():
1295 src = os.path.join(srcpath, file_entry)
1296 dest = os.path.join(dest_dir, file_entry)
1298 # TODO: Move into database as above
1299 if Cnf.FindB("Dinstall::SecurityQueueBuild"):
1300 # Copy it since the original won't be readable by www-data
1301 utils.copy(src, dest)
1303 # Create a symlink to it
1304 os.symlink(src, dest)
1307 qb.suite_id = s.suite_id
1308 qb.queue_id = self.queue_id
1314 # If the .orig.tar.gz is in the pool, create a symlink to
1315 # it (if one doesn't already exist)
1316 if changes.orig_tar_id:
1317 # Determine the .orig.tar.gz file name
1318 for dsc_file in changes.dsc_files.keys():
1319 if dsc_file.endswith(".orig.tar.gz"):
1322 dest = os.path.join(dest_dir, filename)
1324 # If it doesn't exist, create a symlink
1325 if not os.path.exists(dest):
1326 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1327 {'id': changes.orig_tar_id})
1330 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1332 src = os.path.join(res[0], res[1])
1333 os.symlink(src, dest)
1335 # Add it to the list of packages for later processing by apt-ftparchive
1337 qb.suite_id = s.suite_id
1338 qb.queue_id = self.queue_id
1343 # If it does, update things to ensure it's not removed prematurely
1345 qb = get_queue_build(dest, suite_id, session)
1356 __all__.append('Queue')
1358 def get_queue(queuename, session=None):
1360 Returns Queue object for given C{queue name}.
1362 @type queuename: string
1363 @param queuename: The name of the queue
1365 @type session: Session
1366 @param session: Optional SQLA session object (a temporary one will be
1367 generated if not supplied)
1370 @return: Queue object for the given queue
1374 session = DBConn().session()
1375 q = session.query(Queue).filter_by(queue_name=queuename)
1380 __all__.append('get_queue')
1382 ################################################################################
1384 class QueueBuild(object):
1385 def __init__(self, *args, **kwargs):
1389 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1391 __all__.append('QueueBuild')
1393 def get_queue_build(filename, suite_id, session=None):
1395 Returns QueueBuild object for given C{filename} and C{suite id}.
1397 @type filename: string
1398 @param filename: The name of the file
1401 @param suiteid: Suite ID
1403 @type session: Session
1404 @param session: Optional SQLA session object (a temporary one will be
1405 generated if not supplied)
1408 @return: Queue object for the given queue
1412 session = DBConn().session()
1413 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite_id)
1418 __all__.append('get_queue_build')
1420 ################################################################################
1422 class Section(object):
1423 def __init__(self, *args, **kwargs):
1426 def __eq__(self, val):
1427 if isinstance(val, str):
1428 return (self.section == val)
1429 # This signals to use the normal comparison operator
1430 return NotImplemented
1432 def __ne__(self, val):
1433 if isinstance(val, str):
1434 return (self.section != val)
1435 # This signals to use the normal comparison operator
1436 return NotImplemented
1439 return '<Section %s>' % self.section
1441 __all__.append('Section')
1443 def get_section(section, session=None):
1445 Returns Section object for given C{section name}.
1447 @type section: string
1448 @param section: The name of the section
1450 @type session: Session
1451 @param session: Optional SQLA session object (a temporary one will be
1452 generated if not supplied)
1455 @return: Section object for the given section name
1459 session = DBConn().session()
1460 q = session.query(Section).filter_by(section=section)
1465 __all__.append('get_section')
1467 ################################################################################
1469 class DBSource(object):
1470 def __init__(self, *args, **kwargs):
1474 return '<DBSource %s (%s)>' % (self.source, self.version)
1476 __all__.append('DBSource')
1478 def source_exists(source, source_version, suites = ["any"], session=None):
1480 Ensure that source exists somewhere in the archive for the binary
1481 upload being processed.
1482 1. exact match => 1.0-3
1483 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1485 @type package: string
1486 @param package: package source name
1488 @type source_version: string
1489 @param source_version: expected source version
1492 @param suites: list of suites to check in, default I{any}
1494 @type session: Session
1495 @param session: Optional SQLA session object (a temporary one will be
1496 generated if not supplied)
1499 @return: returns 1 if a source with expected version is found, otherwise 0
1504 session = DBConn().session()
1508 for suite in suites:
1509 q = session.query(DBSource).filter_by(source=source)
1511 # source must exist in suite X, or in some other suite that's
1512 # mapped to X, recursively... silent-maps are counted too,
1513 # unreleased-maps aren't.
1514 maps = cnf.ValueList("SuiteMappings")[:]
1516 maps = [ m.split() for m in maps ]
1517 maps = [ (x[1], x[2]) for x in maps
1518 if x[0] == "map" or x[0] == "silent-map" ]
1521 if x[1] in s and x[0] not in s:
1524 q = q.join(SrcAssociation).join(Suite)
1525 q = q.filter(Suite.suite_name.in_(s))
1527 # Reduce the query results to a list of version numbers
1528 ql = [ j.version for j in q.all() ]
1531 if source_version in ql:
1535 from daklib.regexes import re_bin_only_nmu
1536 orig_source_version = re_bin_only_nmu.sub('', source_version)
1537 if orig_source_version in ql:
1540 # No source found so return not ok
1546 __all__.append('source_exists')
1548 def get_suites_source_in(source, session=None):
1550 Returns list of Suite objects which given C{source} name is in
1553 @param source: DBSource package name to search for
1556 @return: list of Suite objects for the given source
1560 session = DBConn().session()
1562 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1564 __all__.append('get_suites_source_in')
1566 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1568 Returns list of DBSource objects for given C{source} name and other parameters
1571 @param source: DBSource package name to search for
1573 @type source: str or None
1574 @param source: DBSource version name to search for or None if not applicable
1576 @type dm_upload_allowed: bool
1577 @param dm_upload_allowed: If None, no effect. If True or False, only
1578 return packages with that dm_upload_allowed setting
1580 @type session: Session
1581 @param session: Optional SQL session object (a temporary one will be
1582 generated if not supplied)
1585 @return: list of DBSource objects for the given name (may be empty)
1588 session = DBConn().session()
1590 q = session.query(DBSource).filter_by(source=source)
1592 if version is not None:
1593 q = q.filter_by(version=version)
1595 if dm_upload_allowed is not None:
1596 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1600 __all__.append('get_sources_from_name')
1602 def get_source_in_suite(source, suite, session=None):
1604 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1606 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1607 - B{suite} - a suite name, eg. I{unstable}
1609 @type source: string
1610 @param source: source package name
1613 @param suite: the suite name
1616 @return: the version for I{source} in I{suite}
1620 session = DBConn().session()
1621 q = session.query(SrcAssociation)
1622 q = q.join('source').filter_by(source=source)
1623 q = q.join('suite').filter_by(suite_name=suite)
1626 # ???: Maybe we should just return the SrcAssociation object instead
1627 return q.one().source
1629 __all__.append('get_source_in_suite')
1631 ################################################################################
1633 class SrcAssociation(object):
1634 def __init__(self, *args, **kwargs):
1638 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1640 __all__.append('SrcAssociation')
1642 ################################################################################
1644 class SrcUploader(object):
1645 def __init__(self, *args, **kwargs):
1649 return '<SrcUploader %s>' % self.uploader_id
1651 __all__.append('SrcUploader')
1653 ################################################################################
1655 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1656 ('SuiteID', 'suite_id'),
1657 ('Version', 'version'),
1658 ('Origin', 'origin'),
1660 ('Description', 'description'),
1661 ('Untouchable', 'untouchable'),
1662 ('Announce', 'announce'),
1663 ('Codename', 'codename'),
1664 ('OverrideCodename', 'overridecodename'),
1665 ('ValidTime', 'validtime'),
1666 ('Priority', 'priority'),
1667 ('NotAutomatic', 'notautomatic'),
1668 ('CopyChanges', 'copychanges'),
1669 ('CopyDotDak', 'copydotdak'),
1670 ('CommentsDir', 'commentsdir'),
1671 ('OverrideSuite', 'overridesuite'),
1672 ('ChangelogBase', 'changelogbase')]
1675 class Suite(object):
1676 def __init__(self, *args, **kwargs):
1680 return '<Suite %s>' % self.suite_name
1682 def __eq__(self, val):
1683 if isinstance(val, str):
1684 return (self.suite_name == val)
1685 # This signals to use the normal comparison operator
1686 return NotImplemented
1688 def __ne__(self, val):
1689 if isinstance(val, str):
1690 return (self.suite_name != val)
1691 # This signals to use the normal comparison operator
1692 return NotImplemented
1696 for disp, field in SUITE_FIELDS:
1697 val = getattr(self, field, None)
1699 ret.append("%s: %s" % (disp, val))
1701 return "\n".join(ret)
1703 __all__.append('Suite')
1705 def get_suite_architecture(suite, architecture, session=None):
1707 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1711 @param suite: Suite name to search for
1713 @type architecture: str
1714 @param architecture: Architecture name to search for
1716 @type session: Session
1717 @param session: Optional SQL session object (a temporary one will be
1718 generated if not supplied)
1720 @rtype: SuiteArchitecture
1721 @return: the SuiteArchitecture object or None
1725 session = DBConn().session()
1727 q = session.query(SuiteArchitecture)
1728 q = q.join(Architecture).filter_by(arch_string=architecture)
1729 q = q.join(Suite).filter_by(suite_name=suite)
1734 __all__.append('get_suite_architecture')
1736 def get_suite(suite, session=None):
1738 Returns Suite object for given C{suite name}.
1741 @param suite: The name of the suite
1743 @type session: Session
1744 @param session: Optional SQLA session object (a temporary one will be
1745 generated if not supplied)
1748 @return: Suite object for the requested suite name (None if not presenT)
1752 session = DBConn().session()
1753 q = session.query(Suite).filter_by(suite_name=suite)
1758 __all__.append('get_suite')
1760 ################################################################################
1762 class SuiteArchitecture(object):
1763 def __init__(self, *args, **kwargs):
1767 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1769 __all__.append('SuiteArchitecture')
1771 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1773 Returns list of Architecture objects for given C{suite} name
1776 @param source: Suite name to search for
1778 @type skipsrc: boolean
1779 @param skipsrc: Whether to skip returning the 'source' architecture entry
1782 @type skipall: boolean
1783 @param skipall: Whether to skip returning the 'all' architecture entry
1786 @type session: Session
1787 @param session: Optional SQL session object (a temporary one will be
1788 generated if not supplied)
1791 @return: list of Architecture objects for the given name (may be empty)
1795 session = DBConn().session()
1797 q = session.query(Architecture)
1798 q = q.join(SuiteArchitecture)
1799 q = q.join(Suite).filter_by(suite_name=suite)
1801 q = q.filter(Architecture.arch_string != 'source')
1803 q = q.filter(Architecture.arch_string != 'all')
1804 q = q.order_by('arch_string')
1807 __all__.append('get_suite_architectures')
1809 ################################################################################
1812 def __init__(self, *args, **kwargs):
1815 def __eq__(self, val):
1816 if isinstance(val, str):
1817 return (self.uid == val)
1818 # This signals to use the normal comparison operator
1819 return NotImplemented
1821 def __ne__(self, val):
1822 if isinstance(val, str):
1823 return (self.uid != val)
1824 # This signals to use the normal comparison operator
1825 return NotImplemented
1828 return '<Uid %s (%s)>' % (self.uid, self.name)
1830 __all__.append('Uid')
1832 def add_database_user(uidname, session=None):
1834 Adds a database user
1836 @type uidname: string
1837 @param uidname: The uid of the user to add
1839 @type session: SQLAlchemy
1840 @param session: Optional SQL session object (a temporary one will be
1841 generated if not supplied). If not passed, a commit will be performed at
1842 the end of the function, otherwise the caller is responsible for commiting.
1845 @return: the uid object for the given uidname
1847 privatetrans = False
1849 session = DBConn().session()
1853 session.execute("CREATE USER :uid", {'uid': uidname})
1857 traceback.print_exc()
1860 __all__.append('add_database_user')
1862 def get_or_set_uid(uidname, session=None):
1864 Returns uid object for given uidname.
1866 If no matching uidname is found, a row is inserted.
1868 @type uidname: string
1869 @param uidname: The uid to add
1871 @type session: SQLAlchemy
1872 @param session: Optional SQL session object (a temporary one will be
1873 generated if not supplied). If not passed, a commit will be performed at
1874 the end of the function, otherwise the caller is responsible for commiting.
1877 @return: the uid object for the given uidname
1879 privatetrans = False
1881 session = DBConn().session()
1885 q = session.query(Uid).filter_by(uid=uidname)
1899 traceback.print_exc()
1902 __all__.append('get_or_set_uid')
1905 def get_uid_from_fingerprint(fpr, session=None):
1907 session = DBConn().session()
1909 q = session.query(Uid)
1910 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1917 __all__.append('get_uid_from_fingerprint')
1919 ################################################################################
1921 class DBConn(Singleton):
1923 database module init.
1925 def __init__(self, *args, **kwargs):
1926 super(DBConn, self).__init__(*args, **kwargs)
1928 def _startup(self, *args, **kwargs):
1930 if kwargs.has_key('debug'):
1934 def __setuptables(self):
1935 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1936 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1937 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1938 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1939 self.tbl_component = Table('component', self.db_meta, autoload=True)
1940 self.tbl_config = Table('config', self.db_meta, autoload=True)
1941 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1942 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1943 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1944 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1945 self.tbl_files = Table('files', self.db_meta, autoload=True)
1946 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1947 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1948 self.tbl_location = Table('location', self.db_meta, autoload=True)
1949 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
1950 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
1951 self.tbl_override = Table('override', self.db_meta, autoload=True)
1952 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
1953 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
1954 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
1955 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
1956 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
1957 self.tbl_section = Table('section', self.db_meta, autoload=True)
1958 self.tbl_source = Table('source', self.db_meta, autoload=True)
1959 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
1960 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
1961 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
1962 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
1963 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
1965 def __setupmappers(self):
1966 mapper(Architecture, self.tbl_architecture,
1967 properties = dict(arch_id = self.tbl_architecture.c.id))
1969 mapper(Archive, self.tbl_archive,
1970 properties = dict(archive_id = self.tbl_archive.c.id,
1971 archive_name = self.tbl_archive.c.name))
1973 mapper(BinAssociation, self.tbl_bin_associations,
1974 properties = dict(ba_id = self.tbl_bin_associations.c.id,
1975 suite_id = self.tbl_bin_associations.c.suite,
1976 suite = relation(Suite),
1977 binary_id = self.tbl_bin_associations.c.bin,
1978 binary = relation(DBBinary)))
1980 mapper(DBBinary, self.tbl_binaries,
1981 properties = dict(binary_id = self.tbl_binaries.c.id,
1982 package = self.tbl_binaries.c.package,
1983 version = self.tbl_binaries.c.version,
1984 maintainer_id = self.tbl_binaries.c.maintainer,
1985 maintainer = relation(Maintainer),
1986 source_id = self.tbl_binaries.c.source,
1987 source = relation(DBSource),
1988 arch_id = self.tbl_binaries.c.architecture,
1989 architecture = relation(Architecture),
1990 poolfile_id = self.tbl_binaries.c.file,
1991 poolfile = relation(PoolFile),
1992 binarytype = self.tbl_binaries.c.type,
1993 fingerprint_id = self.tbl_binaries.c.sig_fpr,
1994 fingerprint = relation(Fingerprint),
1995 install_date = self.tbl_binaries.c.install_date,
1996 binassociations = relation(BinAssociation,
1997 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
1999 mapper(Component, self.tbl_component,
2000 properties = dict(component_id = self.tbl_component.c.id,
2001 component_name = self.tbl_component.c.name))
2003 mapper(DBConfig, self.tbl_config,
2004 properties = dict(config_id = self.tbl_config.c.id))
2006 mapper(ContentAssociation, self.tbl_content_associations,
2007 properties = dict(ca_id = self.tbl_content_associations.c.id,
2008 filename_id = self.tbl_content_associations.c.filename,
2009 filename = relation(ContentFilename),
2010 filepath_id = self.tbl_content_associations.c.filepath,
2011 filepath = relation(ContentFilepath),
2012 binary_id = self.tbl_content_associations.c.binary_pkg,
2013 binary = relation(DBBinary)))
2016 mapper(ContentFilename, self.tbl_content_file_names,
2017 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2018 filename = self.tbl_content_file_names.c.file))
2020 mapper(ContentFilepath, self.tbl_content_file_paths,
2021 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2022 filepath = self.tbl_content_file_paths.c.path))
2024 mapper(DSCFile, self.tbl_dsc_files,
2025 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2026 source_id = self.tbl_dsc_files.c.source,
2027 source = relation(DBSource),
2028 poolfile_id = self.tbl_dsc_files.c.file,
2029 poolfile = relation(PoolFile)))
2031 mapper(PoolFile, self.tbl_files,
2032 properties = dict(file_id = self.tbl_files.c.id,
2033 filesize = self.tbl_files.c.size,
2034 location_id = self.tbl_files.c.location,
2035 location = relation(Location)))
2037 mapper(Fingerprint, self.tbl_fingerprint,
2038 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2039 uid_id = self.tbl_fingerprint.c.uid,
2040 uid = relation(Uid),
2041 keyring_id = self.tbl_fingerprint.c.keyring,
2042 keyring = relation(Keyring)))
2044 mapper(Keyring, self.tbl_keyrings,
2045 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2046 keyring_id = self.tbl_keyrings.c.id))
2048 mapper(Location, self.tbl_location,
2049 properties = dict(location_id = self.tbl_location.c.id,
2050 component_id = self.tbl_location.c.component,
2051 component = relation(Component),
2052 archive_id = self.tbl_location.c.archive,
2053 archive = relation(Archive),
2054 archive_type = self.tbl_location.c.type))
2056 mapper(Maintainer, self.tbl_maintainer,
2057 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2059 mapper(NewComment, self.tbl_new_comments,
2060 properties = dict(comment_id = self.tbl_new_comments.c.id))
2062 mapper(Override, self.tbl_override,
2063 properties = dict(suite_id = self.tbl_override.c.suite,
2064 suite = relation(Suite),
2065 component_id = self.tbl_override.c.component,
2066 component = relation(Component),
2067 priority_id = self.tbl_override.c.priority,
2068 priority = relation(Priority),
2069 section_id = self.tbl_override.c.section,
2070 section = relation(Section),
2071 overridetype_id = self.tbl_override.c.type,
2072 overridetype = relation(OverrideType)))
2074 mapper(OverrideType, self.tbl_override_type,
2075 properties = dict(overridetype = self.tbl_override_type.c.type,
2076 overridetype_id = self.tbl_override_type.c.id))
2078 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2079 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2080 filepath_id = self.tbl_pending_content_associations.c.filepath,
2081 filepath = relation(ContentFilepath),
2082 filename_id = self.tbl_pending_content_associations.c.filename,
2083 filename = relation(ContentFilename)))
2085 mapper(Priority, self.tbl_priority,
2086 properties = dict(priority_id = self.tbl_priority.c.id))
2088 mapper(Queue, self.tbl_queue,
2089 properties = dict(queue_id = self.tbl_queue.c.id))
2091 mapper(QueueBuild, self.tbl_queue_build,
2092 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2093 queue_id = self.tbl_queue_build.c.queue,
2094 queue = relation(Queue, backref='queuebuild')))
2096 mapper(Section, self.tbl_section,
2097 properties = dict(section_id = self.tbl_section.c.id))
2099 mapper(DBSource, self.tbl_source,
2100 properties = dict(source_id = self.tbl_source.c.id,
2101 version = self.tbl_source.c.version,
2102 maintainer_id = self.tbl_source.c.maintainer,
2103 maintainer = relation(Maintainer,
2104 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2105 poolfile_id = self.tbl_source.c.file,
2106 poolfile = relation(PoolFile),
2107 fingerprint_id = self.tbl_source.c.sig_fpr,
2108 fingerprint = relation(Fingerprint),
2109 changedby_id = self.tbl_source.c.changedby,
2110 changedby = relation(Maintainer,
2111 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2112 srcfiles = relation(DSCFile,
2113 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2114 srcassociations = relation(SrcAssociation,
2115 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2117 mapper(SrcAssociation, self.tbl_src_associations,
2118 properties = dict(sa_id = self.tbl_src_associations.c.id,
2119 suite_id = self.tbl_src_associations.c.suite,
2120 suite = relation(Suite),
2121 source_id = self.tbl_src_associations.c.source,
2122 source = relation(DBSource)))
2124 mapper(SrcUploader, self.tbl_src_uploaders,
2125 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2126 source_id = self.tbl_src_uploaders.c.source,
2127 source = relation(DBSource,
2128 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2129 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2130 maintainer = relation(Maintainer,
2131 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2133 mapper(Suite, self.tbl_suite,
2134 properties = dict(suite_id = self.tbl_suite.c.id))
2136 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2137 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2138 suite = relation(Suite, backref='suitearchitectures'),
2139 arch_id = self.tbl_suite_architectures.c.architecture,
2140 architecture = relation(Architecture)))
2142 mapper(Uid, self.tbl_uid,
2143 properties = dict(uid_id = self.tbl_uid.c.id,
2144 fingerprint = relation(Fingerprint)))
2146 ## Connection functions
2147 def __createconn(self):
2148 from config import Config
2152 connstr = "postgres://%s" % cnf["DB::Host"]
2153 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2154 connstr += ":%s" % cnf["DB::Port"]
2155 connstr += "/%s" % cnf["DB::Name"]
2158 connstr = "postgres:///%s" % cnf["DB::Name"]
2159 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2160 connstr += "?port=%s" % cnf["DB::Port"]
2162 self.db_pg = create_engine(connstr, echo=self.debug)
2163 self.db_meta = MetaData()
2164 self.db_meta.bind = self.db_pg
2165 self.db_smaker = sessionmaker(bind=self.db_pg,
2169 self.__setuptables()
2170 self.__setupmappers()
2173 return self.db_smaker()
2175 __all__.append('DBConn')