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, session=None):
1395 Returns QueueBuild object for given C{filename} and C{suite}.
1397 @type filename: string
1398 @param filename: The name of the file
1400 @type suiteid: int or str
1401 @param suiteid: Suite name or 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 if isinstance(suite, int):
1414 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1416 q = session.query(QueueBuild).filter_by(filename=filename)
1417 q = q.join(Suite).filter_by(suite_name=suite)
1423 __all__.append('get_queue_build')
1425 ################################################################################
1427 class Section(object):
1428 def __init__(self, *args, **kwargs):
1431 def __eq__(self, val):
1432 if isinstance(val, str):
1433 return (self.section == val)
1434 # This signals to use the normal comparison operator
1435 return NotImplemented
1437 def __ne__(self, val):
1438 if isinstance(val, str):
1439 return (self.section != val)
1440 # This signals to use the normal comparison operator
1441 return NotImplemented
1444 return '<Section %s>' % self.section
1446 __all__.append('Section')
1448 def get_section(section, session=None):
1450 Returns Section object for given C{section name}.
1452 @type section: string
1453 @param section: The name of the section
1455 @type session: Session
1456 @param session: Optional SQLA session object (a temporary one will be
1457 generated if not supplied)
1460 @return: Section object for the given section name
1464 session = DBConn().session()
1465 q = session.query(Section).filter_by(section=section)
1470 __all__.append('get_section')
1472 ################################################################################
1474 class DBSource(object):
1475 def __init__(self, *args, **kwargs):
1479 return '<DBSource %s (%s)>' % (self.source, self.version)
1481 __all__.append('DBSource')
1483 def source_exists(source, source_version, suites = ["any"], session=None):
1485 Ensure that source exists somewhere in the archive for the binary
1486 upload being processed.
1487 1. exact match => 1.0-3
1488 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1490 @type package: string
1491 @param package: package source name
1493 @type source_version: string
1494 @param source_version: expected source version
1497 @param suites: list of suites to check in, default I{any}
1499 @type session: Session
1500 @param session: Optional SQLA session object (a temporary one will be
1501 generated if not supplied)
1504 @return: returns 1 if a source with expected version is found, otherwise 0
1509 session = DBConn().session()
1513 for suite in suites:
1514 q = session.query(DBSource).filter_by(source=source)
1516 # source must exist in suite X, or in some other suite that's
1517 # mapped to X, recursively... silent-maps are counted too,
1518 # unreleased-maps aren't.
1519 maps = cnf.ValueList("SuiteMappings")[:]
1521 maps = [ m.split() for m in maps ]
1522 maps = [ (x[1], x[2]) for x in maps
1523 if x[0] == "map" or x[0] == "silent-map" ]
1526 if x[1] in s and x[0] not in s:
1529 q = q.join(SrcAssociation).join(Suite)
1530 q = q.filter(Suite.suite_name.in_(s))
1532 # Reduce the query results to a list of version numbers
1533 ql = [ j.version for j in q.all() ]
1536 if source_version in ql:
1540 from daklib.regexes import re_bin_only_nmu
1541 orig_source_version = re_bin_only_nmu.sub('', source_version)
1542 if orig_source_version in ql:
1545 # No source found so return not ok
1551 __all__.append('source_exists')
1553 def get_suites_source_in(source, session=None):
1555 Returns list of Suite objects which given C{source} name is in
1558 @param source: DBSource package name to search for
1561 @return: list of Suite objects for the given source
1565 session = DBConn().session()
1567 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1569 __all__.append('get_suites_source_in')
1571 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1573 Returns list of DBSource objects for given C{source} name and other parameters
1576 @param source: DBSource package name to search for
1578 @type source: str or None
1579 @param source: DBSource version name to search for or None if not applicable
1581 @type dm_upload_allowed: bool
1582 @param dm_upload_allowed: If None, no effect. If True or False, only
1583 return packages with that dm_upload_allowed setting
1585 @type session: Session
1586 @param session: Optional SQL session object (a temporary one will be
1587 generated if not supplied)
1590 @return: list of DBSource objects for the given name (may be empty)
1593 session = DBConn().session()
1595 q = session.query(DBSource).filter_by(source=source)
1597 if version is not None:
1598 q = q.filter_by(version=version)
1600 if dm_upload_allowed is not None:
1601 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1605 __all__.append('get_sources_from_name')
1607 def get_source_in_suite(source, suite, session=None):
1609 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1611 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1612 - B{suite} - a suite name, eg. I{unstable}
1614 @type source: string
1615 @param source: source package name
1618 @param suite: the suite name
1621 @return: the version for I{source} in I{suite}
1625 session = DBConn().session()
1626 q = session.query(SrcAssociation)
1627 q = q.join('source').filter_by(source=source)
1628 q = q.join('suite').filter_by(suite_name=suite)
1631 # ???: Maybe we should just return the SrcAssociation object instead
1632 return q.one().source
1634 __all__.append('get_source_in_suite')
1636 ################################################################################
1638 class SrcAssociation(object):
1639 def __init__(self, *args, **kwargs):
1643 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1645 __all__.append('SrcAssociation')
1647 ################################################################################
1649 class SrcUploader(object):
1650 def __init__(self, *args, **kwargs):
1654 return '<SrcUploader %s>' % self.uploader_id
1656 __all__.append('SrcUploader')
1658 ################################################################################
1660 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1661 ('SuiteID', 'suite_id'),
1662 ('Version', 'version'),
1663 ('Origin', 'origin'),
1665 ('Description', 'description'),
1666 ('Untouchable', 'untouchable'),
1667 ('Announce', 'announce'),
1668 ('Codename', 'codename'),
1669 ('OverrideCodename', 'overridecodename'),
1670 ('ValidTime', 'validtime'),
1671 ('Priority', 'priority'),
1672 ('NotAutomatic', 'notautomatic'),
1673 ('CopyChanges', 'copychanges'),
1674 ('CopyDotDak', 'copydotdak'),
1675 ('CommentsDir', 'commentsdir'),
1676 ('OverrideSuite', 'overridesuite'),
1677 ('ChangelogBase', 'changelogbase')]
1680 class Suite(object):
1681 def __init__(self, *args, **kwargs):
1685 return '<Suite %s>' % self.suite_name
1687 def __eq__(self, val):
1688 if isinstance(val, str):
1689 return (self.suite_name == val)
1690 # This signals to use the normal comparison operator
1691 return NotImplemented
1693 def __ne__(self, val):
1694 if isinstance(val, str):
1695 return (self.suite_name != val)
1696 # This signals to use the normal comparison operator
1697 return NotImplemented
1701 for disp, field in SUITE_FIELDS:
1702 val = getattr(self, field, None)
1704 ret.append("%s: %s" % (disp, val))
1706 return "\n".join(ret)
1708 __all__.append('Suite')
1710 def get_suite_architecture(suite, architecture, session=None):
1712 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1716 @param suite: Suite name to search for
1718 @type architecture: str
1719 @param architecture: Architecture name to search for
1721 @type session: Session
1722 @param session: Optional SQL session object (a temporary one will be
1723 generated if not supplied)
1725 @rtype: SuiteArchitecture
1726 @return: the SuiteArchitecture object or None
1730 session = DBConn().session()
1732 q = session.query(SuiteArchitecture)
1733 q = q.join(Architecture).filter_by(arch_string=architecture)
1734 q = q.join(Suite).filter_by(suite_name=suite)
1739 __all__.append('get_suite_architecture')
1741 def get_suite(suite, session=None):
1743 Returns Suite object for given C{suite name}.
1746 @param suite: The name of the suite
1748 @type session: Session
1749 @param session: Optional SQLA session object (a temporary one will be
1750 generated if not supplied)
1753 @return: Suite object for the requested suite name (None if not presenT)
1757 session = DBConn().session()
1758 q = session.query(Suite).filter_by(suite_name=suite)
1763 __all__.append('get_suite')
1765 ################################################################################
1767 class SuiteArchitecture(object):
1768 def __init__(self, *args, **kwargs):
1772 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1774 __all__.append('SuiteArchitecture')
1776 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1778 Returns list of Architecture objects for given C{suite} name
1781 @param source: Suite name to search for
1783 @type skipsrc: boolean
1784 @param skipsrc: Whether to skip returning the 'source' architecture entry
1787 @type skipall: boolean
1788 @param skipall: Whether to skip returning the 'all' architecture entry
1791 @type session: Session
1792 @param session: Optional SQL session object (a temporary one will be
1793 generated if not supplied)
1796 @return: list of Architecture objects for the given name (may be empty)
1800 session = DBConn().session()
1802 q = session.query(Architecture)
1803 q = q.join(SuiteArchitecture)
1804 q = q.join(Suite).filter_by(suite_name=suite)
1806 q = q.filter(Architecture.arch_string != 'source')
1808 q = q.filter(Architecture.arch_string != 'all')
1809 q = q.order_by('arch_string')
1812 __all__.append('get_suite_architectures')
1814 ################################################################################
1817 def __init__(self, *args, **kwargs):
1820 def __eq__(self, val):
1821 if isinstance(val, str):
1822 return (self.uid == val)
1823 # This signals to use the normal comparison operator
1824 return NotImplemented
1826 def __ne__(self, val):
1827 if isinstance(val, str):
1828 return (self.uid != val)
1829 # This signals to use the normal comparison operator
1830 return NotImplemented
1833 return '<Uid %s (%s)>' % (self.uid, self.name)
1835 __all__.append('Uid')
1837 def add_database_user(uidname, session=None):
1839 Adds a database user
1841 @type uidname: string
1842 @param uidname: The uid of the user to add
1844 @type session: SQLAlchemy
1845 @param session: Optional SQL session object (a temporary one will be
1846 generated if not supplied). If not passed, a commit will be performed at
1847 the end of the function, otherwise the caller is responsible for commiting.
1850 @return: the uid object for the given uidname
1852 privatetrans = False
1854 session = DBConn().session()
1858 session.execute("CREATE USER :uid", {'uid': uidname})
1862 traceback.print_exc()
1865 __all__.append('add_database_user')
1867 def get_or_set_uid(uidname, session=None):
1869 Returns uid object for given uidname.
1871 If no matching uidname is found, a row is inserted.
1873 @type uidname: string
1874 @param uidname: The uid to add
1876 @type session: SQLAlchemy
1877 @param session: Optional SQL session object (a temporary one will be
1878 generated if not supplied). If not passed, a commit will be performed at
1879 the end of the function, otherwise the caller is responsible for commiting.
1882 @return: the uid object for the given uidname
1884 privatetrans = False
1886 session = DBConn().session()
1890 q = session.query(Uid).filter_by(uid=uidname)
1904 traceback.print_exc()
1907 __all__.append('get_or_set_uid')
1910 def get_uid_from_fingerprint(fpr, session=None):
1912 session = DBConn().session()
1914 q = session.query(Uid)
1915 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1922 __all__.append('get_uid_from_fingerprint')
1924 ################################################################################
1926 class DBConn(Singleton):
1928 database module init.
1930 def __init__(self, *args, **kwargs):
1931 super(DBConn, self).__init__(*args, **kwargs)
1933 def _startup(self, *args, **kwargs):
1935 if kwargs.has_key('debug'):
1939 def __setuptables(self):
1940 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1941 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1942 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1943 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1944 self.tbl_component = Table('component', self.db_meta, autoload=True)
1945 self.tbl_config = Table('config', self.db_meta, autoload=True)
1946 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1947 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1948 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1949 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1950 self.tbl_files = Table('files', self.db_meta, autoload=True)
1951 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1952 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1953 self.tbl_location = Table('location', self.db_meta, autoload=True)
1954 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
1955 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
1956 self.tbl_override = Table('override', self.db_meta, autoload=True)
1957 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
1958 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
1959 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
1960 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
1961 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
1962 self.tbl_section = Table('section', self.db_meta, autoload=True)
1963 self.tbl_source = Table('source', self.db_meta, autoload=True)
1964 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
1965 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
1966 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
1967 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
1968 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
1970 def __setupmappers(self):
1971 mapper(Architecture, self.tbl_architecture,
1972 properties = dict(arch_id = self.tbl_architecture.c.id))
1974 mapper(Archive, self.tbl_archive,
1975 properties = dict(archive_id = self.tbl_archive.c.id,
1976 archive_name = self.tbl_archive.c.name))
1978 mapper(BinAssociation, self.tbl_bin_associations,
1979 properties = dict(ba_id = self.tbl_bin_associations.c.id,
1980 suite_id = self.tbl_bin_associations.c.suite,
1981 suite = relation(Suite),
1982 binary_id = self.tbl_bin_associations.c.bin,
1983 binary = relation(DBBinary)))
1985 mapper(DBBinary, self.tbl_binaries,
1986 properties = dict(binary_id = self.tbl_binaries.c.id,
1987 package = self.tbl_binaries.c.package,
1988 version = self.tbl_binaries.c.version,
1989 maintainer_id = self.tbl_binaries.c.maintainer,
1990 maintainer = relation(Maintainer),
1991 source_id = self.tbl_binaries.c.source,
1992 source = relation(DBSource),
1993 arch_id = self.tbl_binaries.c.architecture,
1994 architecture = relation(Architecture),
1995 poolfile_id = self.tbl_binaries.c.file,
1996 poolfile = relation(PoolFile),
1997 binarytype = self.tbl_binaries.c.type,
1998 fingerprint_id = self.tbl_binaries.c.sig_fpr,
1999 fingerprint = relation(Fingerprint),
2000 install_date = self.tbl_binaries.c.install_date,
2001 binassociations = relation(BinAssociation,
2002 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2004 mapper(Component, self.tbl_component,
2005 properties = dict(component_id = self.tbl_component.c.id,
2006 component_name = self.tbl_component.c.name))
2008 mapper(DBConfig, self.tbl_config,
2009 properties = dict(config_id = self.tbl_config.c.id))
2011 mapper(ContentAssociation, self.tbl_content_associations,
2012 properties = dict(ca_id = self.tbl_content_associations.c.id,
2013 filename_id = self.tbl_content_associations.c.filename,
2014 filename = relation(ContentFilename),
2015 filepath_id = self.tbl_content_associations.c.filepath,
2016 filepath = relation(ContentFilepath),
2017 binary_id = self.tbl_content_associations.c.binary_pkg,
2018 binary = relation(DBBinary)))
2021 mapper(ContentFilename, self.tbl_content_file_names,
2022 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2023 filename = self.tbl_content_file_names.c.file))
2025 mapper(ContentFilepath, self.tbl_content_file_paths,
2026 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2027 filepath = self.tbl_content_file_paths.c.path))
2029 mapper(DSCFile, self.tbl_dsc_files,
2030 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2031 source_id = self.tbl_dsc_files.c.source,
2032 source = relation(DBSource),
2033 poolfile_id = self.tbl_dsc_files.c.file,
2034 poolfile = relation(PoolFile)))
2036 mapper(PoolFile, self.tbl_files,
2037 properties = dict(file_id = self.tbl_files.c.id,
2038 filesize = self.tbl_files.c.size,
2039 location_id = self.tbl_files.c.location,
2040 location = relation(Location)))
2042 mapper(Fingerprint, self.tbl_fingerprint,
2043 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2044 uid_id = self.tbl_fingerprint.c.uid,
2045 uid = relation(Uid),
2046 keyring_id = self.tbl_fingerprint.c.keyring,
2047 keyring = relation(Keyring)))
2049 mapper(Keyring, self.tbl_keyrings,
2050 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2051 keyring_id = self.tbl_keyrings.c.id))
2053 mapper(Location, self.tbl_location,
2054 properties = dict(location_id = self.tbl_location.c.id,
2055 component_id = self.tbl_location.c.component,
2056 component = relation(Component),
2057 archive_id = self.tbl_location.c.archive,
2058 archive = relation(Archive),
2059 archive_type = self.tbl_location.c.type))
2061 mapper(Maintainer, self.tbl_maintainer,
2062 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2064 mapper(NewComment, self.tbl_new_comments,
2065 properties = dict(comment_id = self.tbl_new_comments.c.id))
2067 mapper(Override, self.tbl_override,
2068 properties = dict(suite_id = self.tbl_override.c.suite,
2069 suite = relation(Suite),
2070 component_id = self.tbl_override.c.component,
2071 component = relation(Component),
2072 priority_id = self.tbl_override.c.priority,
2073 priority = relation(Priority),
2074 section_id = self.tbl_override.c.section,
2075 section = relation(Section),
2076 overridetype_id = self.tbl_override.c.type,
2077 overridetype = relation(OverrideType)))
2079 mapper(OverrideType, self.tbl_override_type,
2080 properties = dict(overridetype = self.tbl_override_type.c.type,
2081 overridetype_id = self.tbl_override_type.c.id))
2083 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2084 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2085 filepath_id = self.tbl_pending_content_associations.c.filepath,
2086 filepath = relation(ContentFilepath),
2087 filename_id = self.tbl_pending_content_associations.c.filename,
2088 filename = relation(ContentFilename)))
2090 mapper(Priority, self.tbl_priority,
2091 properties = dict(priority_id = self.tbl_priority.c.id))
2093 mapper(Queue, self.tbl_queue,
2094 properties = dict(queue_id = self.tbl_queue.c.id))
2096 mapper(QueueBuild, self.tbl_queue_build,
2097 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2098 queue_id = self.tbl_queue_build.c.queue,
2099 queue = relation(Queue, backref='queuebuild')))
2101 mapper(Section, self.tbl_section,
2102 properties = dict(section_id = self.tbl_section.c.id))
2104 mapper(DBSource, self.tbl_source,
2105 properties = dict(source_id = self.tbl_source.c.id,
2106 version = self.tbl_source.c.version,
2107 maintainer_id = self.tbl_source.c.maintainer,
2108 maintainer = relation(Maintainer,
2109 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2110 poolfile_id = self.tbl_source.c.file,
2111 poolfile = relation(PoolFile),
2112 fingerprint_id = self.tbl_source.c.sig_fpr,
2113 fingerprint = relation(Fingerprint),
2114 changedby_id = self.tbl_source.c.changedby,
2115 changedby = relation(Maintainer,
2116 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2117 srcfiles = relation(DSCFile,
2118 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2119 srcassociations = relation(SrcAssociation,
2120 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2122 mapper(SrcAssociation, self.tbl_src_associations,
2123 properties = dict(sa_id = self.tbl_src_associations.c.id,
2124 suite_id = self.tbl_src_associations.c.suite,
2125 suite = relation(Suite),
2126 source_id = self.tbl_src_associations.c.source,
2127 source = relation(DBSource)))
2129 mapper(SrcUploader, self.tbl_src_uploaders,
2130 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2131 source_id = self.tbl_src_uploaders.c.source,
2132 source = relation(DBSource,
2133 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2134 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2135 maintainer = relation(Maintainer,
2136 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2138 mapper(Suite, self.tbl_suite,
2139 properties = dict(suite_id = self.tbl_suite.c.id))
2141 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2142 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2143 suite = relation(Suite, backref='suitearchitectures'),
2144 arch_id = self.tbl_suite_architectures.c.architecture,
2145 architecture = relation(Architecture)))
2147 mapper(Uid, self.tbl_uid,
2148 properties = dict(uid_id = self.tbl_uid.c.id,
2149 fingerprint = relation(Fingerprint)))
2151 ## Connection functions
2152 def __createconn(self):
2153 from config import Config
2157 connstr = "postgres://%s" % cnf["DB::Host"]
2158 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2159 connstr += ":%s" % cnf["DB::Port"]
2160 connstr += "/%s" % cnf["DB::Name"]
2163 connstr = "postgres:///%s" % cnf["DB::Name"]
2164 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2165 connstr += "?port=%s" % cnf["DB::Port"]
2167 self.db_pg = create_engine(connstr, echo=self.debug)
2168 self.db_meta = MetaData()
2169 self.db_meta.bind = self.db_pg
2170 self.db_smaker = sessionmaker(bind=self.db_pg,
2174 self.__setuptables()
2175 self.__setupmappers()
2178 return self.db_smaker()
2180 __all__.append('DBConn')