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 def get_priorities(session=None):
1238 Returns dictionary of priority names -> id mappings
1240 @type session: Session
1241 @param session: Optional SQL session object (a temporary one will be
1242 generated if not supplied)
1245 @return: dictionary of priority names -> id mappings
1248 session = DBConn().session()
1251 q = session.query(Priority)
1253 ret[x.priority] = x.priority_id
1257 __all__.append('get_priorities')
1259 ################################################################################
1261 class Queue(object):
1262 def __init__(self, *args, **kwargs):
1266 return '<Queue %s>' % self.queue_name
1268 def autobuild_upload(self, changes, srcpath, session=None):
1270 Update queue_build database table used for incoming autobuild support.
1272 @type changes: Changes
1273 @param changes: changes object for the upload to process
1275 @type srcpath: string
1276 @param srcpath: path for the queue file entries/link destinations
1278 @type session: SQLAlchemy session
1279 @param session: Optional SQLAlchemy session. If this is passed, the
1280 caller is responsible for ensuring a transaction has begun and
1281 committing the results or rolling back based on the result code. If
1282 not passed, a commit will be performed at the end of the function,
1283 otherwise the caller is responsible for commiting.
1285 @rtype: NoneType or string
1286 @return: None if the operation failed, a string describing the error if not
1291 session = DBConn().session()
1294 # TODO: Remove by moving queue config into the database
1297 for suitename in changes.changes["distribution"].keys():
1298 # TODO: Move into database as:
1299 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1300 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1301 # This also gets rid of the SecurityQueueBuild hack below
1302 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1306 s = get_suite(suitename, session)
1308 return "INTERNAL ERROR: Could not find suite %s" % suitename
1310 # TODO: Get from database as above
1311 dest_dir = conf["Dir::QueueBuild"]
1313 # TODO: Move into database as above
1314 if conf.FindB("Dinstall::SecurityQueueBuild"):
1315 dest_dir = os.path.join(dest_dir, suitename)
1317 for file_entry in changes.files.keys():
1318 src = os.path.join(srcpath, file_entry)
1319 dest = os.path.join(dest_dir, file_entry)
1321 # TODO: Move into database as above
1322 if Cnf.FindB("Dinstall::SecurityQueueBuild"):
1323 # Copy it since the original won't be readable by www-data
1324 utils.copy(src, dest)
1326 # Create a symlink to it
1327 os.symlink(src, dest)
1330 qb.suite_id = s.suite_id
1331 qb.queue_id = self.queue_id
1337 # If the .orig.tar.gz is in the pool, create a symlink to
1338 # it (if one doesn't already exist)
1339 if changes.orig_tar_id:
1340 # Determine the .orig.tar.gz file name
1341 for dsc_file in changes.dsc_files.keys():
1342 if dsc_file.endswith(".orig.tar.gz"):
1345 dest = os.path.join(dest_dir, filename)
1347 # If it doesn't exist, create a symlink
1348 if not os.path.exists(dest):
1349 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1350 {'id': changes.orig_tar_id})
1353 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1355 src = os.path.join(res[0], res[1])
1356 os.symlink(src, dest)
1358 # Add it to the list of packages for later processing by apt-ftparchive
1360 qb.suite_id = s.suite_id
1361 qb.queue_id = self.queue_id
1366 # If it does, update things to ensure it's not removed prematurely
1368 qb = get_queue_build(dest, suite_id, session)
1379 __all__.append('Queue')
1381 def get_queue(queuename, session=None):
1383 Returns Queue object for given C{queue name}.
1385 @type queuename: string
1386 @param queuename: The name of the queue
1388 @type session: Session
1389 @param session: Optional SQLA session object (a temporary one will be
1390 generated if not supplied)
1393 @return: Queue object for the given queue
1397 session = DBConn().session()
1398 q = session.query(Queue).filter_by(queue_name=queuename)
1403 __all__.append('get_queue')
1405 ################################################################################
1407 class QueueBuild(object):
1408 def __init__(self, *args, **kwargs):
1412 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1414 __all__.append('QueueBuild')
1416 def get_queue_build(filename, suite, session=None):
1418 Returns QueueBuild object for given C{filename} and C{suite}.
1420 @type filename: string
1421 @param filename: The name of the file
1423 @type suiteid: int or str
1424 @param suiteid: Suite name or ID
1426 @type session: Session
1427 @param session: Optional SQLA session object (a temporary one will be
1428 generated if not supplied)
1431 @return: Queue object for the given queue
1435 session = DBConn().session()
1436 if isinstance(suite, int):
1437 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1439 q = session.query(QueueBuild).filter_by(filename=filename)
1440 q = q.join(Suite).filter_by(suite_name=suite)
1446 __all__.append('get_queue_build')
1448 ################################################################################
1450 class Section(object):
1451 def __init__(self, *args, **kwargs):
1454 def __eq__(self, val):
1455 if isinstance(val, str):
1456 return (self.section == val)
1457 # This signals to use the normal comparison operator
1458 return NotImplemented
1460 def __ne__(self, val):
1461 if isinstance(val, str):
1462 return (self.section != val)
1463 # This signals to use the normal comparison operator
1464 return NotImplemented
1467 return '<Section %s>' % self.section
1469 __all__.append('Section')
1471 def get_section(section, session=None):
1473 Returns Section object for given C{section name}.
1475 @type section: string
1476 @param section: The name of the section
1478 @type session: Session
1479 @param session: Optional SQLA session object (a temporary one will be
1480 generated if not supplied)
1483 @return: Section object for the given section name
1487 session = DBConn().session()
1488 q = session.query(Section).filter_by(section=section)
1493 __all__.append('get_section')
1495 def get_sections(session=None):
1497 Returns dictionary of section names -> id mappings
1499 @type session: Session
1500 @param session: Optional SQL session object (a temporary one will be
1501 generated if not supplied)
1504 @return: dictionary of section names -> id mappings
1507 session = DBConn().session()
1510 q = session.query(Section)
1512 ret[x.section] = x.section_id
1516 __all__.append('get_sections')
1518 ################################################################################
1520 class DBSource(object):
1521 def __init__(self, *args, **kwargs):
1525 return '<DBSource %s (%s)>' % (self.source, self.version)
1527 __all__.append('DBSource')
1529 def source_exists(source, source_version, suites = ["any"], session=None):
1531 Ensure that source exists somewhere in the archive for the binary
1532 upload being processed.
1533 1. exact match => 1.0-3
1534 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1536 @type package: string
1537 @param package: package source name
1539 @type source_version: string
1540 @param source_version: expected source version
1543 @param suites: list of suites to check in, default I{any}
1545 @type session: Session
1546 @param session: Optional SQLA session object (a temporary one will be
1547 generated if not supplied)
1550 @return: returns 1 if a source with expected version is found, otherwise 0
1555 session = DBConn().session()
1559 for suite in suites:
1560 q = session.query(DBSource).filter_by(source=source)
1562 # source must exist in suite X, or in some other suite that's
1563 # mapped to X, recursively... silent-maps are counted too,
1564 # unreleased-maps aren't.
1565 maps = cnf.ValueList("SuiteMappings")[:]
1567 maps = [ m.split() for m in maps ]
1568 maps = [ (x[1], x[2]) for x in maps
1569 if x[0] == "map" or x[0] == "silent-map" ]
1572 if x[1] in s and x[0] not in s:
1575 q = q.join(SrcAssociation).join(Suite)
1576 q = q.filter(Suite.suite_name.in_(s))
1578 # Reduce the query results to a list of version numbers
1579 ql = [ j.version for j in q.all() ]
1582 if source_version in ql:
1586 from daklib.regexes import re_bin_only_nmu
1587 orig_source_version = re_bin_only_nmu.sub('', source_version)
1588 if orig_source_version in ql:
1591 # No source found so return not ok
1597 __all__.append('source_exists')
1599 def get_suites_source_in(source, session=None):
1601 Returns list of Suite objects which given C{source} name is in
1604 @param source: DBSource package name to search for
1607 @return: list of Suite objects for the given source
1611 session = DBConn().session()
1613 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1615 __all__.append('get_suites_source_in')
1617 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1619 Returns list of DBSource objects for given C{source} name and other parameters
1622 @param source: DBSource package name to search for
1624 @type source: str or None
1625 @param source: DBSource version name to search for or None if not applicable
1627 @type dm_upload_allowed: bool
1628 @param dm_upload_allowed: If None, no effect. If True or False, only
1629 return packages with that dm_upload_allowed setting
1631 @type session: Session
1632 @param session: Optional SQL session object (a temporary one will be
1633 generated if not supplied)
1636 @return: list of DBSource objects for the given name (may be empty)
1639 session = DBConn().session()
1641 q = session.query(DBSource).filter_by(source=source)
1643 if version is not None:
1644 q = q.filter_by(version=version)
1646 if dm_upload_allowed is not None:
1647 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1651 __all__.append('get_sources_from_name')
1653 def get_source_in_suite(source, suite, session=None):
1655 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1657 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1658 - B{suite} - a suite name, eg. I{unstable}
1660 @type source: string
1661 @param source: source package name
1664 @param suite: the suite name
1667 @return: the version for I{source} in I{suite}
1671 session = DBConn().session()
1672 q = session.query(SrcAssociation)
1673 q = q.join('source').filter_by(source=source)
1674 q = q.join('suite').filter_by(suite_name=suite)
1677 # ???: Maybe we should just return the SrcAssociation object instead
1678 return q.one().source
1680 __all__.append('get_source_in_suite')
1682 ################################################################################
1684 class SrcAssociation(object):
1685 def __init__(self, *args, **kwargs):
1689 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1691 __all__.append('SrcAssociation')
1693 ################################################################################
1695 class SrcUploader(object):
1696 def __init__(self, *args, **kwargs):
1700 return '<SrcUploader %s>' % self.uploader_id
1702 __all__.append('SrcUploader')
1704 ################################################################################
1706 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1707 ('SuiteID', 'suite_id'),
1708 ('Version', 'version'),
1709 ('Origin', 'origin'),
1711 ('Description', 'description'),
1712 ('Untouchable', 'untouchable'),
1713 ('Announce', 'announce'),
1714 ('Codename', 'codename'),
1715 ('OverrideCodename', 'overridecodename'),
1716 ('ValidTime', 'validtime'),
1717 ('Priority', 'priority'),
1718 ('NotAutomatic', 'notautomatic'),
1719 ('CopyChanges', 'copychanges'),
1720 ('CopyDotDak', 'copydotdak'),
1721 ('CommentsDir', 'commentsdir'),
1722 ('OverrideSuite', 'overridesuite'),
1723 ('ChangelogBase', 'changelogbase')]
1726 class Suite(object):
1727 def __init__(self, *args, **kwargs):
1731 return '<Suite %s>' % self.suite_name
1733 def __eq__(self, val):
1734 if isinstance(val, str):
1735 return (self.suite_name == val)
1736 # This signals to use the normal comparison operator
1737 return NotImplemented
1739 def __ne__(self, val):
1740 if isinstance(val, str):
1741 return (self.suite_name != val)
1742 # This signals to use the normal comparison operator
1743 return NotImplemented
1747 for disp, field in SUITE_FIELDS:
1748 val = getattr(self, field, None)
1750 ret.append("%s: %s" % (disp, val))
1752 return "\n".join(ret)
1754 __all__.append('Suite')
1756 def get_suite_architecture(suite, architecture, session=None):
1758 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1762 @param suite: Suite name to search for
1764 @type architecture: str
1765 @param architecture: Architecture name to search for
1767 @type session: Session
1768 @param session: Optional SQL session object (a temporary one will be
1769 generated if not supplied)
1771 @rtype: SuiteArchitecture
1772 @return: the SuiteArchitecture object or None
1776 session = DBConn().session()
1778 q = session.query(SuiteArchitecture)
1779 q = q.join(Architecture).filter_by(arch_string=architecture)
1780 q = q.join(Suite).filter_by(suite_name=suite)
1785 __all__.append('get_suite_architecture')
1787 def get_suite(suite, session=None):
1789 Returns Suite object for given C{suite name}.
1792 @param suite: The name of the suite
1794 @type session: Session
1795 @param session: Optional SQLA session object (a temporary one will be
1796 generated if not supplied)
1799 @return: Suite object for the requested suite name (None if not presenT)
1803 session = DBConn().session()
1804 q = session.query(Suite).filter_by(suite_name=suite)
1809 __all__.append('get_suite')
1811 ################################################################################
1813 class SuiteArchitecture(object):
1814 def __init__(self, *args, **kwargs):
1818 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1820 __all__.append('SuiteArchitecture')
1822 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1824 Returns list of Architecture objects for given C{suite} name
1827 @param source: Suite name to search for
1829 @type skipsrc: boolean
1830 @param skipsrc: Whether to skip returning the 'source' architecture entry
1833 @type skipall: boolean
1834 @param skipall: Whether to skip returning the 'all' architecture entry
1837 @type session: Session
1838 @param session: Optional SQL session object (a temporary one will be
1839 generated if not supplied)
1842 @return: list of Architecture objects for the given name (may be empty)
1846 session = DBConn().session()
1848 q = session.query(Architecture)
1849 q = q.join(SuiteArchitecture)
1850 q = q.join(Suite).filter_by(suite_name=suite)
1852 q = q.filter(Architecture.arch_string != 'source')
1854 q = q.filter(Architecture.arch_string != 'all')
1855 q = q.order_by('arch_string')
1858 __all__.append('get_suite_architectures')
1860 ################################################################################
1863 def __init__(self, *args, **kwargs):
1866 def __eq__(self, val):
1867 if isinstance(val, str):
1868 return (self.uid == val)
1869 # This signals to use the normal comparison operator
1870 return NotImplemented
1872 def __ne__(self, val):
1873 if isinstance(val, str):
1874 return (self.uid != val)
1875 # This signals to use the normal comparison operator
1876 return NotImplemented
1879 return '<Uid %s (%s)>' % (self.uid, self.name)
1881 __all__.append('Uid')
1883 def add_database_user(uidname, session=None):
1885 Adds a database user
1887 @type uidname: string
1888 @param uidname: The uid of the user to add
1890 @type session: SQLAlchemy
1891 @param session: Optional SQL session object (a temporary one will be
1892 generated if not supplied). If not passed, a commit will be performed at
1893 the end of the function, otherwise the caller is responsible for commiting.
1896 @return: the uid object for the given uidname
1898 privatetrans = False
1900 session = DBConn().session()
1904 session.execute("CREATE USER :uid", {'uid': uidname})
1908 traceback.print_exc()
1911 __all__.append('add_database_user')
1913 def get_or_set_uid(uidname, session=None):
1915 Returns uid object for given uidname.
1917 If no matching uidname is found, a row is inserted.
1919 @type uidname: string
1920 @param uidname: The uid to add
1922 @type session: SQLAlchemy
1923 @param session: Optional SQL session object (a temporary one will be
1924 generated if not supplied). If not passed, a commit will be performed at
1925 the end of the function, otherwise the caller is responsible for commiting.
1928 @return: the uid object for the given uidname
1930 privatetrans = False
1932 session = DBConn().session()
1936 q = session.query(Uid).filter_by(uid=uidname)
1950 traceback.print_exc()
1953 __all__.append('get_or_set_uid')
1956 def get_uid_from_fingerprint(fpr, session=None):
1958 session = DBConn().session()
1960 q = session.query(Uid)
1961 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1968 __all__.append('get_uid_from_fingerprint')
1970 ################################################################################
1972 class DBConn(Singleton):
1974 database module init.
1976 def __init__(self, *args, **kwargs):
1977 super(DBConn, self).__init__(*args, **kwargs)
1979 def _startup(self, *args, **kwargs):
1981 if kwargs.has_key('debug'):
1985 def __setuptables(self):
1986 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1987 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1988 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1989 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1990 self.tbl_component = Table('component', self.db_meta, autoload=True)
1991 self.tbl_config = Table('config', self.db_meta, autoload=True)
1992 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1993 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1994 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1995 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1996 self.tbl_files = Table('files', self.db_meta, autoload=True)
1997 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1998 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1999 self.tbl_location = Table('location', self.db_meta, autoload=True)
2000 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2001 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2002 self.tbl_override = Table('override', self.db_meta, autoload=True)
2003 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2004 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2005 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2006 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2007 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2008 self.tbl_section = Table('section', self.db_meta, autoload=True)
2009 self.tbl_source = Table('source', self.db_meta, autoload=True)
2010 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2011 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2012 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2013 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2014 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2016 def __setupmappers(self):
2017 mapper(Architecture, self.tbl_architecture,
2018 properties = dict(arch_id = self.tbl_architecture.c.id))
2020 mapper(Archive, self.tbl_archive,
2021 properties = dict(archive_id = self.tbl_archive.c.id,
2022 archive_name = self.tbl_archive.c.name))
2024 mapper(BinAssociation, self.tbl_bin_associations,
2025 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2026 suite_id = self.tbl_bin_associations.c.suite,
2027 suite = relation(Suite),
2028 binary_id = self.tbl_bin_associations.c.bin,
2029 binary = relation(DBBinary)))
2031 mapper(DBBinary, self.tbl_binaries,
2032 properties = dict(binary_id = self.tbl_binaries.c.id,
2033 package = self.tbl_binaries.c.package,
2034 version = self.tbl_binaries.c.version,
2035 maintainer_id = self.tbl_binaries.c.maintainer,
2036 maintainer = relation(Maintainer),
2037 source_id = self.tbl_binaries.c.source,
2038 source = relation(DBSource),
2039 arch_id = self.tbl_binaries.c.architecture,
2040 architecture = relation(Architecture),
2041 poolfile_id = self.tbl_binaries.c.file,
2042 poolfile = relation(PoolFile),
2043 binarytype = self.tbl_binaries.c.type,
2044 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2045 fingerprint = relation(Fingerprint),
2046 install_date = self.tbl_binaries.c.install_date,
2047 binassociations = relation(BinAssociation,
2048 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2050 mapper(Component, self.tbl_component,
2051 properties = dict(component_id = self.tbl_component.c.id,
2052 component_name = self.tbl_component.c.name))
2054 mapper(DBConfig, self.tbl_config,
2055 properties = dict(config_id = self.tbl_config.c.id))
2057 mapper(ContentAssociation, self.tbl_content_associations,
2058 properties = dict(ca_id = self.tbl_content_associations.c.id,
2059 filename_id = self.tbl_content_associations.c.filename,
2060 filename = relation(ContentFilename),
2061 filepath_id = self.tbl_content_associations.c.filepath,
2062 filepath = relation(ContentFilepath),
2063 binary_id = self.tbl_content_associations.c.binary_pkg,
2064 binary = relation(DBBinary)))
2067 mapper(ContentFilename, self.tbl_content_file_names,
2068 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2069 filename = self.tbl_content_file_names.c.file))
2071 mapper(ContentFilepath, self.tbl_content_file_paths,
2072 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2073 filepath = self.tbl_content_file_paths.c.path))
2075 mapper(DSCFile, self.tbl_dsc_files,
2076 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2077 source_id = self.tbl_dsc_files.c.source,
2078 source = relation(DBSource),
2079 poolfile_id = self.tbl_dsc_files.c.file,
2080 poolfile = relation(PoolFile)))
2082 mapper(PoolFile, self.tbl_files,
2083 properties = dict(file_id = self.tbl_files.c.id,
2084 filesize = self.tbl_files.c.size,
2085 location_id = self.tbl_files.c.location,
2086 location = relation(Location)))
2088 mapper(Fingerprint, self.tbl_fingerprint,
2089 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2090 uid_id = self.tbl_fingerprint.c.uid,
2091 uid = relation(Uid),
2092 keyring_id = self.tbl_fingerprint.c.keyring,
2093 keyring = relation(Keyring)))
2095 mapper(Keyring, self.tbl_keyrings,
2096 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2097 keyring_id = self.tbl_keyrings.c.id))
2099 mapper(Location, self.tbl_location,
2100 properties = dict(location_id = self.tbl_location.c.id,
2101 component_id = self.tbl_location.c.component,
2102 component = relation(Component),
2103 archive_id = self.tbl_location.c.archive,
2104 archive = relation(Archive),
2105 archive_type = self.tbl_location.c.type))
2107 mapper(Maintainer, self.tbl_maintainer,
2108 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2110 mapper(NewComment, self.tbl_new_comments,
2111 properties = dict(comment_id = self.tbl_new_comments.c.id))
2113 mapper(Override, self.tbl_override,
2114 properties = dict(suite_id = self.tbl_override.c.suite,
2115 suite = relation(Suite),
2116 component_id = self.tbl_override.c.component,
2117 component = relation(Component),
2118 priority_id = self.tbl_override.c.priority,
2119 priority = relation(Priority),
2120 section_id = self.tbl_override.c.section,
2121 section = relation(Section),
2122 overridetype_id = self.tbl_override.c.type,
2123 overridetype = relation(OverrideType)))
2125 mapper(OverrideType, self.tbl_override_type,
2126 properties = dict(overridetype = self.tbl_override_type.c.type,
2127 overridetype_id = self.tbl_override_type.c.id))
2129 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2130 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2131 filepath_id = self.tbl_pending_content_associations.c.filepath,
2132 filepath = relation(ContentFilepath),
2133 filename_id = self.tbl_pending_content_associations.c.filename,
2134 filename = relation(ContentFilename)))
2136 mapper(Priority, self.tbl_priority,
2137 properties = dict(priority_id = self.tbl_priority.c.id))
2139 mapper(Queue, self.tbl_queue,
2140 properties = dict(queue_id = self.tbl_queue.c.id))
2142 mapper(QueueBuild, self.tbl_queue_build,
2143 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2144 queue_id = self.tbl_queue_build.c.queue,
2145 queue = relation(Queue, backref='queuebuild')))
2147 mapper(Section, self.tbl_section,
2148 properties = dict(section_id = self.tbl_section.c.id))
2150 mapper(DBSource, self.tbl_source,
2151 properties = dict(source_id = self.tbl_source.c.id,
2152 version = self.tbl_source.c.version,
2153 maintainer_id = self.tbl_source.c.maintainer,
2154 maintainer = relation(Maintainer,
2155 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2156 poolfile_id = self.tbl_source.c.file,
2157 poolfile = relation(PoolFile),
2158 fingerprint_id = self.tbl_source.c.sig_fpr,
2159 fingerprint = relation(Fingerprint),
2160 changedby_id = self.tbl_source.c.changedby,
2161 changedby = relation(Maintainer,
2162 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2163 srcfiles = relation(DSCFile,
2164 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2165 srcassociations = relation(SrcAssociation,
2166 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2168 mapper(SrcAssociation, self.tbl_src_associations,
2169 properties = dict(sa_id = self.tbl_src_associations.c.id,
2170 suite_id = self.tbl_src_associations.c.suite,
2171 suite = relation(Suite),
2172 source_id = self.tbl_src_associations.c.source,
2173 source = relation(DBSource)))
2175 mapper(SrcUploader, self.tbl_src_uploaders,
2176 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2177 source_id = self.tbl_src_uploaders.c.source,
2178 source = relation(DBSource,
2179 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2180 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2181 maintainer = relation(Maintainer,
2182 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2184 mapper(Suite, self.tbl_suite,
2185 properties = dict(suite_id = self.tbl_suite.c.id))
2187 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2188 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2189 suite = relation(Suite, backref='suitearchitectures'),
2190 arch_id = self.tbl_suite_architectures.c.architecture,
2191 architecture = relation(Architecture)))
2193 mapper(Uid, self.tbl_uid,
2194 properties = dict(uid_id = self.tbl_uid.c.id,
2195 fingerprint = relation(Fingerprint)))
2197 ## Connection functions
2198 def __createconn(self):
2199 from config import Config
2203 connstr = "postgres://%s" % cnf["DB::Host"]
2204 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2205 connstr += ":%s" % cnf["DB::Port"]
2206 connstr += "/%s" % cnf["DB::Name"]
2209 connstr = "postgres:///%s" % cnf["DB::Name"]
2210 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2211 connstr += "?port=%s" % cnf["DB::Port"]
2213 self.db_pg = create_engine(connstr, echo=self.debug)
2214 self.db_meta = MetaData()
2215 self.db_meta.bind = self.db_pg
2216 self.db_smaker = sessionmaker(bind=self.db_pg,
2220 self.__setuptables()
2221 self.__setupmappers()
2224 return self.db_smaker()
2226 __all__.append('DBConn')