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
423 return cf.cafilename_id
425 return q.one().cafilename_id
428 traceback.print_exc()
431 __all__.append('get_or_set_contents_file_id')
433 def get_contents(suite, overridetype, section=None, session=None):
435 Returns contents for a suite / overridetype combination, limiting
436 to a section if not None.
439 @param suite: Suite object
441 @type overridetype: OverrideType
442 @param overridetype: OverrideType object
444 @type section: Section
445 @param section: Optional section object to limit results to
447 @type session: SQLAlchemy
448 @param session: Optional SQL session object (a temporary one will be
449 generated if not supplied)
452 @return: ResultsProxy object set up to return tuples of (filename, section,
457 session = DBConn().session()
459 # find me all of the contents for a given suite
460 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
464 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
465 JOIN content_file_names n ON (c.filename=n.id)
466 JOIN binaries b ON (b.id=c.binary_pkg)
467 JOIN override o ON (o.package=b.package)
468 JOIN section s ON (s.id=o.section)
469 WHERE o.suite = :suiteid AND o.type = :overridetypeid
470 AND b.type=:overridetypename"""
472 vals = {'suiteid': suite.suite_id,
473 'overridetypeid': overridetype.overridetype_id,
474 'overridetypename': overridetype.overridetype}
476 if section is not None:
477 contents_q += " AND s.id = :sectionid"
478 vals['sectionid'] = section.section_id
480 contents_q += " ORDER BY fn"
482 return session.execute(contents_q, vals)
484 __all__.append('get_contents')
486 ################################################################################
488 class ContentFilepath(object):
489 def __init__(self, *args, **kwargs):
493 return '<ContentFilepath %s>' % self.filepath
495 __all__.append('ContentFilepath')
497 def get_or_set_contents_path_id(filepath, session=None):
499 Returns database id for given path.
501 If no matching file is found, a row is inserted.
503 @type filename: string
504 @param filename: The filepath
505 @type session: SQLAlchemy
506 @param session: Optional SQL session object (a temporary one will be
507 generated if not supplied). If not passed, a commit will be performed at
508 the end of the function, otherwise the caller is responsible for commiting.
511 @return: the database id for the given path
515 session = DBConn().session()
519 q = session.query(ContentFilepath).filter_by(filepath=filepath)
521 cf = ContentFilepath()
522 cf.filepath = filepath
529 return cf.cafilepath_id
531 return q.one().cafilepath_id
534 traceback.print_exc()
537 __all__.append('get_or_set_contents_path_id')
539 ################################################################################
541 class ContentAssociation(object):
542 def __init__(self, *args, **kwargs):
546 return '<ContentAssociation %s>' % self.ca_id
548 __all__.append('ContentAssociation')
550 def insert_content_paths(binary_id, fullpaths, session=None):
552 Make sure given path is associated with given binary id
555 @param binary_id: the id of the binary
556 @type fullpaths: list
557 @param fullpaths: the list of paths of the file being associated with the binary
558 @type session: SQLAlchemy session
559 @param session: Optional SQLAlchemy session. If this is passed, the caller
560 is responsible for ensuring a transaction has begun and committing the
561 results or rolling back based on the result code. If not passed, a commit
562 will be performed at the end of the function, otherwise the caller is
563 responsible for commiting.
565 @return: True upon success
571 session = DBConn().session()
577 for fullpath in fullpaths:
578 # Get the necessary IDs ...
579 (path, file) = os.path.split(fullpath)
581 filepath_id = get_or_set_contents_path_id(path, session)
582 filename_id = get_or_set_contents_file_id(file, session)
584 pathcache[fullpath] = (filepath_id, filename_id)
586 for fullpath, dat in pathcache.items():
587 ca = ContentAssociation()
588 ca.binary_id = binary_id
589 ca.filepath_id = dat[0]
590 ca.filename_id = dat[1]
593 # Only commit if we set up the session ourself
603 traceback.print_exc()
605 # Only rollback if we set up the session ourself
612 __all__.append('insert_content_paths')
614 ################################################################################
616 class DSCFile(object):
617 def __init__(self, *args, **kwargs):
621 return '<DSCFile %s>' % self.dscfile_id
623 __all__.append('DSCFile')
625 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
627 Returns a list of DSCFiles which may be empty
629 @type dscfile_id: int (optional)
630 @param dscfile_id: the dscfile_id of the DSCFiles to find
632 @type source_id: int (optional)
633 @param source_id: the source id related to the DSCFiles to find
635 @type poolfile_id: int (optional)
636 @param poolfile_id: the poolfile id related to the DSCFiles to find
639 @return: Possibly empty list of DSCFiles
643 session = DBConn().session()
645 q = session.query(DSCFile)
647 if dscfile_id is not None:
648 q = q.filter_by(dscfile_id=dscfile_id)
650 if source_id is not None:
651 q = q.filter_by(source_id=source_id)
653 if poolfile_id is not None:
654 q = q.filter_by(poolfile_id=poolfile_id)
658 __all__.append('get_dscfiles')
660 ################################################################################
662 class PoolFile(object):
663 def __init__(self, *args, **kwargs):
667 return '<PoolFile %s>' % self.filename
669 __all__.append('PoolFile')
671 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
674 (ValidFileFound [boolean or None], PoolFile object or None)
676 @type filename: string
677 @param filename: the filename of the file to check against the DB
680 @param filesize: the size of the file to check against the DB
683 @param md5sum: the md5sum of the file to check against the DB
685 @type location_id: int
686 @param location_id: the id of the location to look in
689 @return: Tuple of length 2.
690 If more than one file found with that name:
692 If valid pool file found: (True, PoolFile object)
693 If valid pool file not found:
694 (False, None) if no file found
695 (False, PoolFile object) if file found with size/md5sum mismatch
699 session = DBConn().session()
701 q = session.query(PoolFile).filter_by(filename=filename)
702 q = q.join(Location).filter_by(location_id=location_id)
710 if obj.md5sum != md5sum or obj.filesize != filesize:
715 __all__.append('check_poolfile')
717 def get_poolfile_by_id(file_id, session=None):
719 Returns a PoolFile objects or None for the given id
722 @param file_id: the id of the file to look for
724 @rtype: PoolFile or None
725 @return: either the PoolFile object or None
729 session = DBConn().session()
731 q = session.query(PoolFile).filter_by(file_id=file_id)
738 __all__.append('get_poolfile_by_id')
741 def get_poolfile_by_name(filename, location_id=None, session=None):
743 Returns an array of PoolFile objects for the given filename and
744 (optionally) location_id
746 @type filename: string
747 @param filename: the filename of the file to check against the DB
749 @type location_id: int
750 @param location_id: the id of the location to look in (optional)
753 @return: array of PoolFile objects
757 session = DBConn().session()
759 q = session.query(PoolFile).filter_by(filename=filename)
761 if location_id is not None:
762 q = q.join(Location).filter_by(location_id=location_id)
766 __all__.append('get_poolfile_by_name')
768 def get_poolfile_like_name(filename, session=None):
770 Returns an array of PoolFile objects which are like the given name
772 @type filename: string
773 @param filename: the filename of the file to check against the DB
776 @return: array of PoolFile objects
780 session = DBConn().session()
782 # TODO: There must be a way of properly using bind parameters with %FOO%
783 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
787 __all__.append('get_poolfile_like_name')
789 ################################################################################
791 class Fingerprint(object):
792 def __init__(self, *args, **kwargs):
796 return '<Fingerprint %s>' % self.fingerprint
798 __all__.append('Fingerprint')
800 def get_or_set_fingerprint(fpr, session=None):
802 Returns Fingerprint object for given fpr.
804 If no matching fpr is found, a row is inserted.
807 @param fpr: The fpr to find / add
809 @type session: SQLAlchemy
810 @param session: Optional SQL session object (a temporary one will be
811 generated if not supplied). If not passed, a commit will be performed at
812 the end of the function, otherwise the caller is responsible for commiting.
813 A flush will be performed either way.
816 @return: the Fingerprint object for the given fpr
820 session = DBConn().session()
824 q = session.query(Fingerprint).filter_by(fingerprint=fpr)
826 fingerprint = Fingerprint()
827 fingerprint.fingerprint = fpr
828 session.add(fingerprint)
838 traceback.print_exc()
841 __all__.append('get_or_set_fingerprint')
843 ################################################################################
845 class Keyring(object):
846 def __init__(self, *args, **kwargs):
850 return '<Keyring %s>' % self.keyring_name
852 __all__.append('Keyring')
854 ################################################################################
856 class Location(object):
857 def __init__(self, *args, **kwargs):
861 return '<Location %s (%s)>' % (self.path, self.location_id)
863 __all__.append('Location')
865 def get_location(location, component=None, archive=None, session=None):
867 Returns Location object for the given combination of location, component
870 @type location: string
871 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
873 @type component: string
874 @param component: the component name (if None, no restriction applied)
876 @type archive: string
877 @param archive_id: the archive name (if None, no restriction applied)
879 @rtype: Location / None
880 @return: Either a Location object or None if one can't be found
884 session = DBConn().session()
886 q = session.query(Location).filter_by(path=location)
888 if archive is not None:
889 q = q.join(Archive).filter_by(archive_name=archive)
891 if component is not None:
892 q = q.join(Component).filter_by(component_name=component)
899 __all__.append('get_location')
901 ################################################################################
903 class Maintainer(object):
904 def __init__(self, *args, **kwargs):
908 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
910 def get_split_maintainer(self):
911 if not hasattr(self, 'name') or self.name is None:
912 return ('', '', '', '')
914 return fix_maintainer(self.name.strip())
916 __all__.append('Maintainer')
918 def get_or_set_maintainer(name, session=None):
920 Returns Maintainer object for given maintainer name.
922 If no matching maintainer name is found, a row is inserted.
925 @param name: The maintainer name to add
927 @type session: SQLAlchemy
928 @param session: Optional SQL session object (a temporary one will be
929 generated if not supplied). If not passed, a commit will be performed at
930 the end of the function, otherwise the caller is responsible for commiting.
931 A flush will be performed either way.
934 @return: the Maintainer object for the given maintainer
938 session = DBConn().session()
942 q = session.query(Maintainer).filter_by(name=name)
944 maintainer = Maintainer()
945 maintainer.name = name
946 session.add(maintainer)
956 traceback.print_exc()
959 __all__.append('get_or_set_maintainer')
961 ################################################################################
963 class NewComment(object):
964 def __init__(self, *args, **kwargs):
968 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
970 __all__.append('NewComment')
972 def has_new_comment(package, version, session=None):
974 Returns true if the given combination of C{package}, C{version} has a comment.
976 @type package: string
977 @param package: name of the package
979 @type version: string
980 @param version: package version
982 @type session: Session
983 @param session: Optional SQLA session object (a temporary one will be
984 generated if not supplied)
991 session = DBConn().session()
993 q = session.query(NewComment)
994 q = q.filter_by(package=package)
995 q = q.filter_by(version=version)
998 __all__.append('has_new_comment')
1000 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1002 Returns (possibly empty) list of NewComment objects for the given
1005 @type package: string (optional)
1006 @param package: name of the package
1008 @type version: string (optional)
1009 @param version: package version
1011 @type comment_id: int (optional)
1012 @param comment_id: An id of a comment
1014 @type session: Session
1015 @param session: Optional SQLA session object (a temporary one will be
1016 generated if not supplied)
1019 @return: A (possibly empty) list of NewComment objects will be returned
1024 session = DBConn().session()
1026 q = session.query(NewComment)
1027 if package is not None: q = q.filter_by(package=package)
1028 if version is not None: q = q.filter_by(version=version)
1029 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1033 __all__.append('get_new_comments')
1035 ################################################################################
1037 class Override(object):
1038 def __init__(self, *args, **kwargs):
1042 return '<Override %s (%s)>' % (self.package, self.suite_id)
1044 __all__.append('Override')
1046 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1048 Returns Override object for the given parameters
1050 @type package: string
1051 @param package: The name of the package
1053 @type suite: string, list or None
1054 @param suite: The name of the suite (or suites if a list) to limit to. If
1055 None, don't limit. Defaults to None.
1057 @type component: string, list or None
1058 @param component: The name of the component (or components if a list) to
1059 limit to. If None, don't limit. Defaults to None.
1061 @type overridetype: string, list or None
1062 @param overridetype: The name of the overridetype (or overridetypes if a list) to
1063 limit to. If None, don't limit. Defaults to None.
1065 @type session: Session
1066 @param session: Optional SQLA session object (a temporary one will be
1067 generated if not supplied)
1070 @return: A (possibly empty) list of Override objects will be returned
1074 session = DBConn().session()
1076 q = session.query(Override)
1077 q = q.filter_by(package=package)
1079 if suite is not None:
1080 if not isinstance(suite, list): suite = [suite]
1081 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1083 if component is not None:
1084 if not isinstance(component, list): component = [component]
1085 q = q.join(Component).filter(Component.component_name.in_(component))
1087 if overridetype is not None:
1088 if not isinstance(overridetype, list): overridetype = [overridetype]
1089 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1093 __all__.append('get_override')
1096 ################################################################################
1098 class OverrideType(object):
1099 def __init__(self, *args, **kwargs):
1103 return '<OverrideType %s>' % self.overridetype
1105 __all__.append('OverrideType')
1107 def get_override_type(override_type, session=None):
1109 Returns OverrideType object for given C{override type}.
1111 @type override_type: string
1112 @param override_type: The name of the override type
1114 @type session: Session
1115 @param session: Optional SQLA session object (a temporary one will be
1116 generated if not supplied)
1119 @return: the database id for the given override type
1123 session = DBConn().session()
1124 q = session.query(OverrideType).filter_by(overridetype=override_type)
1129 __all__.append('get_override_type')
1131 ################################################################################
1133 class PendingContentAssociation(object):
1134 def __init__(self, *args, **kwargs):
1138 return '<PendingContentAssociation %s>' % self.pca_id
1140 __all__.append('PendingContentAssociation')
1142 def insert_pending_content_paths(package, fullpaths, session=None):
1144 Make sure given paths are temporarily associated with given
1148 @param package: the package to associate with should have been read in from the binary control file
1149 @type fullpaths: list
1150 @param fullpaths: the list of paths of the file being associated with the binary
1151 @type session: SQLAlchemy session
1152 @param session: Optional SQLAlchemy session. If this is passed, the caller
1153 is responsible for ensuring a transaction has begun and committing the
1154 results or rolling back based on the result code. If not passed, a commit
1155 will be performed at the end of the function
1157 @return: True upon success, False if there is a problem
1160 privatetrans = False
1163 session = DBConn().session()
1167 arch = get_architecture(package['Architecture'], session)
1168 arch_id = arch.arch_id
1170 # Remove any already existing recorded files for this package
1171 q = session.query(PendingContentAssociation)
1172 q = q.filter_by(package=package['Package'])
1173 q = q.filter_by(version=package['Version'])
1174 q = q.filter_by(architecture=arch_id)
1179 for fullpath in fullpaths:
1180 (path, file) = os.path.split(fullpath)
1182 if path.startswith( "./" ):
1185 filepath_id = get_or_set_contents_path_id(path, session)
1186 filename_id = get_or_set_contents_file_id(file, session)
1188 pathcache[fullpath] = (filepath_id, filename_id)
1190 for fullpath, dat in pathcache.items():
1191 pca = PendingContentAssociation()
1192 pca.package = package['Package']
1193 pca.version = package['Version']
1194 pca.filepath_id = dat[0]
1195 pca.filename_id = dat[1]
1196 pca.architecture = arch_id
1199 # Only commit if we set up the session ourself
1207 traceback.print_exc()
1209 # Only rollback if we set up the session ourself
1215 __all__.append('insert_pending_content_paths')
1217 ################################################################################
1219 class Priority(object):
1220 def __init__(self, *args, **kwargs):
1223 def __eq__(self, val):
1224 if isinstance(val, str):
1225 return (self.priority == val)
1226 # This signals to use the normal comparison operator
1227 return NotImplemented
1229 def __ne__(self, val):
1230 if isinstance(val, str):
1231 return (self.priority != val)
1232 # This signals to use the normal comparison operator
1233 return NotImplemented
1236 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1238 __all__.append('Priority')
1240 def get_priority(priority, session=None):
1242 Returns Priority object for given C{priority name}.
1244 @type priority: string
1245 @param priority: The name of the priority
1247 @type session: Session
1248 @param session: Optional SQLA session object (a temporary one will be
1249 generated if not supplied)
1252 @return: Priority object for the given priority
1256 session = DBConn().session()
1257 q = session.query(Priority).filter_by(priority=priority)
1262 __all__.append('get_priority')
1264 def get_priorities(session=None):
1266 Returns dictionary of priority names -> id mappings
1268 @type session: Session
1269 @param session: Optional SQL session object (a temporary one will be
1270 generated if not supplied)
1273 @return: dictionary of priority names -> id mappings
1276 session = DBConn().session()
1279 q = session.query(Priority)
1281 ret[x.priority] = x.priority_id
1285 __all__.append('get_priorities')
1287 ################################################################################
1289 class Queue(object):
1290 def __init__(self, *args, **kwargs):
1294 return '<Queue %s>' % self.queue_name
1296 def autobuild_upload(self, changes, srcpath, session=None):
1298 Update queue_build database table used for incoming autobuild support.
1300 @type changes: Changes
1301 @param changes: changes object for the upload to process
1303 @type srcpath: string
1304 @param srcpath: path for the queue file entries/link destinations
1306 @type session: SQLAlchemy session
1307 @param session: Optional SQLAlchemy session. If this is passed, the
1308 caller is responsible for ensuring a transaction has begun and
1309 committing the results or rolling back based on the result code. If
1310 not passed, a commit will be performed at the end of the function,
1311 otherwise the caller is responsible for commiting.
1313 @rtype: NoneType or string
1314 @return: None if the operation failed, a string describing the error if not
1319 session = DBConn().session()
1322 # TODO: Remove by moving queue config into the database
1325 for suitename in changes.changes["distribution"].keys():
1326 # TODO: Move into database as:
1327 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1328 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1329 # This also gets rid of the SecurityQueueBuild hack below
1330 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1334 s = get_suite(suitename, session)
1336 return "INTERNAL ERROR: Could not find suite %s" % suitename
1338 # TODO: Get from database as above
1339 dest_dir = conf["Dir::QueueBuild"]
1341 # TODO: Move into database as above
1342 if conf.FindB("Dinstall::SecurityQueueBuild"):
1343 dest_dir = os.path.join(dest_dir, suitename)
1345 for file_entry in changes.files.keys():
1346 src = os.path.join(srcpath, file_entry)
1347 dest = os.path.join(dest_dir, file_entry)
1349 # TODO: Move into database as above
1350 if conf.FindB("Dinstall::SecurityQueueBuild"):
1351 # Copy it since the original won't be readable by www-data
1352 utils.copy(src, dest)
1354 # Create a symlink to it
1355 os.symlink(src, dest)
1358 qb.suite_id = s.suite_id
1359 qb.queue_id = self.queue_id
1365 # If the .orig.tar.gz is in the pool, create a symlink to
1366 # it (if one doesn't already exist)
1367 if changes.orig_tar_id:
1368 # Determine the .orig.tar.gz file name
1369 for dsc_file in changes.dsc_files.keys():
1370 if dsc_file.endswith(".orig.tar.gz"):
1373 dest = os.path.join(dest_dir, filename)
1375 # If it doesn't exist, create a symlink
1376 if not os.path.exists(dest):
1377 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1378 {'id': changes.orig_tar_id})
1381 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1383 src = os.path.join(res[0], res[1])
1384 os.symlink(src, dest)
1386 # Add it to the list of packages for later processing by apt-ftparchive
1388 qb.suite_id = s.suite_id
1389 qb.queue_id = self.queue_id
1394 # If it does, update things to ensure it's not removed prematurely
1396 qb = get_queue_build(dest, suite_id, session)
1407 __all__.append('Queue')
1409 def get_queue(queuename, session=None):
1411 Returns Queue object for given C{queue name}.
1413 @type queuename: string
1414 @param queuename: The name of the queue
1416 @type session: Session
1417 @param session: Optional SQLA session object (a temporary one will be
1418 generated if not supplied)
1421 @return: Queue object for the given queue
1425 session = DBConn().session()
1426 q = session.query(Queue).filter_by(queue_name=queuename)
1431 __all__.append('get_queue')
1433 ################################################################################
1435 class QueueBuild(object):
1436 def __init__(self, *args, **kwargs):
1440 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1442 __all__.append('QueueBuild')
1444 def get_queue_build(filename, suite, session=None):
1446 Returns QueueBuild object for given C{filename} and C{suite}.
1448 @type filename: string
1449 @param filename: The name of the file
1451 @type suiteid: int or str
1452 @param suiteid: Suite name or ID
1454 @type session: Session
1455 @param session: Optional SQLA session object (a temporary one will be
1456 generated if not supplied)
1459 @return: Queue object for the given queue
1463 session = DBConn().session()
1464 if isinstance(suite, int):
1465 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1467 q = session.query(QueueBuild).filter_by(filename=filename)
1468 q = q.join(Suite).filter_by(suite_name=suite)
1474 __all__.append('get_queue_build')
1476 ################################################################################
1478 class Section(object):
1479 def __init__(self, *args, **kwargs):
1482 def __eq__(self, val):
1483 if isinstance(val, str):
1484 return (self.section == val)
1485 # This signals to use the normal comparison operator
1486 return NotImplemented
1488 def __ne__(self, val):
1489 if isinstance(val, str):
1490 return (self.section != val)
1491 # This signals to use the normal comparison operator
1492 return NotImplemented
1495 return '<Section %s>' % self.section
1497 __all__.append('Section')
1499 def get_section(section, session=None):
1501 Returns Section object for given C{section name}.
1503 @type section: string
1504 @param section: The name of the section
1506 @type session: Session
1507 @param session: Optional SQLA session object (a temporary one will be
1508 generated if not supplied)
1511 @return: Section object for the given section name
1515 session = DBConn().session()
1516 q = session.query(Section).filter_by(section=section)
1521 __all__.append('get_section')
1523 def get_sections(session=None):
1525 Returns dictionary of section names -> id mappings
1527 @type session: Session
1528 @param session: Optional SQL session object (a temporary one will be
1529 generated if not supplied)
1532 @return: dictionary of section names -> id mappings
1535 session = DBConn().session()
1538 q = session.query(Section)
1540 ret[x.section] = x.section_id
1544 __all__.append('get_sections')
1546 ################################################################################
1548 class DBSource(object):
1549 def __init__(self, *args, **kwargs):
1553 return '<DBSource %s (%s)>' % (self.source, self.version)
1555 __all__.append('DBSource')
1557 def source_exists(source, source_version, suites = ["any"], session=None):
1559 Ensure that source exists somewhere in the archive for the binary
1560 upload being processed.
1561 1. exact match => 1.0-3
1562 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1564 @type package: string
1565 @param package: package source name
1567 @type source_version: string
1568 @param source_version: expected source version
1571 @param suites: list of suites to check in, default I{any}
1573 @type session: Session
1574 @param session: Optional SQLA session object (a temporary one will be
1575 generated if not supplied)
1578 @return: returns 1 if a source with expected version is found, otherwise 0
1583 session = DBConn().session()
1587 for suite in suites:
1588 q = session.query(DBSource).filter_by(source=source)
1590 # source must exist in suite X, or in some other suite that's
1591 # mapped to X, recursively... silent-maps are counted too,
1592 # unreleased-maps aren't.
1593 maps = cnf.ValueList("SuiteMappings")[:]
1595 maps = [ m.split() for m in maps ]
1596 maps = [ (x[1], x[2]) for x in maps
1597 if x[0] == "map" or x[0] == "silent-map" ]
1600 if x[1] in s and x[0] not in s:
1603 q = q.join(SrcAssociation).join(Suite)
1604 q = q.filter(Suite.suite_name.in_(s))
1606 # Reduce the query results to a list of version numbers
1607 ql = [ j.version for j in q.all() ]
1610 if source_version in ql:
1614 from daklib.regexes import re_bin_only_nmu
1615 orig_source_version = re_bin_only_nmu.sub('', source_version)
1616 if orig_source_version in ql:
1619 # No source found so return not ok
1625 __all__.append('source_exists')
1627 def get_suites_source_in(source, session=None):
1629 Returns list of Suite objects which given C{source} name is in
1632 @param source: DBSource package name to search for
1635 @return: list of Suite objects for the given source
1639 session = DBConn().session()
1641 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1643 __all__.append('get_suites_source_in')
1645 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1647 Returns list of DBSource objects for given C{source} name and other parameters
1650 @param source: DBSource package name to search for
1652 @type source: str or None
1653 @param source: DBSource version name to search for or None if not applicable
1655 @type dm_upload_allowed: bool
1656 @param dm_upload_allowed: If None, no effect. If True or False, only
1657 return packages with that dm_upload_allowed setting
1659 @type session: Session
1660 @param session: Optional SQL session object (a temporary one will be
1661 generated if not supplied)
1664 @return: list of DBSource objects for the given name (may be empty)
1667 session = DBConn().session()
1669 q = session.query(DBSource).filter_by(source=source)
1671 if version is not None:
1672 q = q.filter_by(version=version)
1674 if dm_upload_allowed is not None:
1675 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1679 __all__.append('get_sources_from_name')
1681 def get_source_in_suite(source, suite, session=None):
1683 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1685 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1686 - B{suite} - a suite name, eg. I{unstable}
1688 @type source: string
1689 @param source: source package name
1692 @param suite: the suite name
1695 @return: the version for I{source} in I{suite}
1699 session = DBConn().session()
1700 q = session.query(SrcAssociation)
1701 q = q.join('source').filter_by(source=source)
1702 q = q.join('suite').filter_by(suite_name=suite)
1705 # ???: Maybe we should just return the SrcAssociation object instead
1706 return q.one().source
1708 __all__.append('get_source_in_suite')
1710 ################################################################################
1712 class SrcAssociation(object):
1713 def __init__(self, *args, **kwargs):
1717 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1719 __all__.append('SrcAssociation')
1721 ################################################################################
1723 class SrcUploader(object):
1724 def __init__(self, *args, **kwargs):
1728 return '<SrcUploader %s>' % self.uploader_id
1730 __all__.append('SrcUploader')
1732 ################################################################################
1734 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1735 ('SuiteID', 'suite_id'),
1736 ('Version', 'version'),
1737 ('Origin', 'origin'),
1739 ('Description', 'description'),
1740 ('Untouchable', 'untouchable'),
1741 ('Announce', 'announce'),
1742 ('Codename', 'codename'),
1743 ('OverrideCodename', 'overridecodename'),
1744 ('ValidTime', 'validtime'),
1745 ('Priority', 'priority'),
1746 ('NotAutomatic', 'notautomatic'),
1747 ('CopyChanges', 'copychanges'),
1748 ('CopyDotDak', 'copydotdak'),
1749 ('CommentsDir', 'commentsdir'),
1750 ('OverrideSuite', 'overridesuite'),
1751 ('ChangelogBase', 'changelogbase')]
1754 class Suite(object):
1755 def __init__(self, *args, **kwargs):
1759 return '<Suite %s>' % self.suite_name
1761 def __eq__(self, val):
1762 if isinstance(val, str):
1763 return (self.suite_name == val)
1764 # This signals to use the normal comparison operator
1765 return NotImplemented
1767 def __ne__(self, val):
1768 if isinstance(val, str):
1769 return (self.suite_name != val)
1770 # This signals to use the normal comparison operator
1771 return NotImplemented
1775 for disp, field in SUITE_FIELDS:
1776 val = getattr(self, field, None)
1778 ret.append("%s: %s" % (disp, val))
1780 return "\n".join(ret)
1782 __all__.append('Suite')
1784 def get_suite_architecture(suite, architecture, session=None):
1786 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1790 @param suite: Suite name to search for
1792 @type architecture: str
1793 @param architecture: Architecture name to search for
1795 @type session: Session
1796 @param session: Optional SQL session object (a temporary one will be
1797 generated if not supplied)
1799 @rtype: SuiteArchitecture
1800 @return: the SuiteArchitecture object or None
1804 session = DBConn().session()
1806 q = session.query(SuiteArchitecture)
1807 q = q.join(Architecture).filter_by(arch_string=architecture)
1808 q = q.join(Suite).filter_by(suite_name=suite)
1813 __all__.append('get_suite_architecture')
1815 def get_suite(suite, session=None):
1817 Returns Suite object for given C{suite name}.
1820 @param suite: The name of the suite
1822 @type session: Session
1823 @param session: Optional SQLA session object (a temporary one will be
1824 generated if not supplied)
1827 @return: Suite object for the requested suite name (None if not presenT)
1831 session = DBConn().session()
1832 q = session.query(Suite).filter_by(suite_name=suite)
1837 __all__.append('get_suite')
1839 ################################################################################
1841 class SuiteArchitecture(object):
1842 def __init__(self, *args, **kwargs):
1846 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1848 __all__.append('SuiteArchitecture')
1850 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1852 Returns list of Architecture objects for given C{suite} name
1855 @param source: Suite name to search for
1857 @type skipsrc: boolean
1858 @param skipsrc: Whether to skip returning the 'source' architecture entry
1861 @type skipall: boolean
1862 @param skipall: Whether to skip returning the 'all' architecture entry
1865 @type session: Session
1866 @param session: Optional SQL session object (a temporary one will be
1867 generated if not supplied)
1870 @return: list of Architecture objects for the given name (may be empty)
1874 session = DBConn().session()
1876 q = session.query(Architecture)
1877 q = q.join(SuiteArchitecture)
1878 q = q.join(Suite).filter_by(suite_name=suite)
1880 q = q.filter(Architecture.arch_string != 'source')
1882 q = q.filter(Architecture.arch_string != 'all')
1883 q = q.order_by('arch_string')
1886 __all__.append('get_suite_architectures')
1888 ################################################################################
1891 def __init__(self, *args, **kwargs):
1894 def __eq__(self, val):
1895 if isinstance(val, str):
1896 return (self.uid == val)
1897 # This signals to use the normal comparison operator
1898 return NotImplemented
1900 def __ne__(self, val):
1901 if isinstance(val, str):
1902 return (self.uid != val)
1903 # This signals to use the normal comparison operator
1904 return NotImplemented
1907 return '<Uid %s (%s)>' % (self.uid, self.name)
1909 __all__.append('Uid')
1911 def add_database_user(uidname, session=None):
1913 Adds a database user
1915 @type uidname: string
1916 @param uidname: The uid of the user to add
1918 @type session: SQLAlchemy
1919 @param session: Optional SQL session object (a temporary one will be
1920 generated if not supplied). If not passed, a commit will be performed at
1921 the end of the function, otherwise the caller is responsible for commiting.
1924 @return: the uid object for the given uidname
1926 privatetrans = False
1928 session = DBConn().session()
1932 session.execute("CREATE USER :uid", {'uid': uidname})
1936 traceback.print_exc()
1939 __all__.append('add_database_user')
1941 def get_or_set_uid(uidname, session=None):
1943 Returns uid object for given uidname.
1945 If no matching uidname is found, a row is inserted.
1947 @type uidname: string
1948 @param uidname: The uid to add
1950 @type session: SQLAlchemy
1951 @param session: Optional SQL session object (a temporary one will be
1952 generated if not supplied). If not passed, a commit will be performed at
1953 the end of the function, otherwise the caller is responsible for commiting.
1956 @return: the uid object for the given uidname
1958 privatetrans = False
1960 session = DBConn().session()
1964 q = session.query(Uid).filter_by(uid=uidname)
1978 traceback.print_exc()
1981 __all__.append('get_or_set_uid')
1984 def get_uid_from_fingerprint(fpr, session=None):
1986 session = DBConn().session()
1988 q = session.query(Uid)
1989 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1996 __all__.append('get_uid_from_fingerprint')
1998 ################################################################################
2000 class DBConn(Singleton):
2002 database module init.
2004 def __init__(self, *args, **kwargs):
2005 super(DBConn, self).__init__(*args, **kwargs)
2007 def _startup(self, *args, **kwargs):
2009 if kwargs.has_key('debug'):
2013 def __setuptables(self):
2014 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2015 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2016 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2017 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2018 self.tbl_component = Table('component', self.db_meta, autoload=True)
2019 self.tbl_config = Table('config', self.db_meta, autoload=True)
2020 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2021 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2022 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2023 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2024 self.tbl_files = Table('files', self.db_meta, autoload=True)
2025 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2026 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2027 self.tbl_location = Table('location', self.db_meta, autoload=True)
2028 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2029 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2030 self.tbl_override = Table('override', self.db_meta, autoload=True)
2031 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2032 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2033 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2034 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2035 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2036 self.tbl_section = Table('section', self.db_meta, autoload=True)
2037 self.tbl_source = Table('source', self.db_meta, autoload=True)
2038 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2039 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2040 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2041 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2042 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2044 def __setupmappers(self):
2045 mapper(Architecture, self.tbl_architecture,
2046 properties = dict(arch_id = self.tbl_architecture.c.id))
2048 mapper(Archive, self.tbl_archive,
2049 properties = dict(archive_id = self.tbl_archive.c.id,
2050 archive_name = self.tbl_archive.c.name))
2052 mapper(BinAssociation, self.tbl_bin_associations,
2053 properties = dict(ba_id = self.tbl_bin_associations.c.id,
2054 suite_id = self.tbl_bin_associations.c.suite,
2055 suite = relation(Suite),
2056 binary_id = self.tbl_bin_associations.c.bin,
2057 binary = relation(DBBinary)))
2059 mapper(DBBinary, self.tbl_binaries,
2060 properties = dict(binary_id = self.tbl_binaries.c.id,
2061 package = self.tbl_binaries.c.package,
2062 version = self.tbl_binaries.c.version,
2063 maintainer_id = self.tbl_binaries.c.maintainer,
2064 maintainer = relation(Maintainer),
2065 source_id = self.tbl_binaries.c.source,
2066 source = relation(DBSource),
2067 arch_id = self.tbl_binaries.c.architecture,
2068 architecture = relation(Architecture),
2069 poolfile_id = self.tbl_binaries.c.file,
2070 poolfile = relation(PoolFile),
2071 binarytype = self.tbl_binaries.c.type,
2072 fingerprint_id = self.tbl_binaries.c.sig_fpr,
2073 fingerprint = relation(Fingerprint),
2074 install_date = self.tbl_binaries.c.install_date,
2075 binassociations = relation(BinAssociation,
2076 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2078 mapper(Component, self.tbl_component,
2079 properties = dict(component_id = self.tbl_component.c.id,
2080 component_name = self.tbl_component.c.name))
2082 mapper(DBConfig, self.tbl_config,
2083 properties = dict(config_id = self.tbl_config.c.id))
2085 mapper(ContentAssociation, self.tbl_content_associations,
2086 properties = dict(ca_id = self.tbl_content_associations.c.id,
2087 filename_id = self.tbl_content_associations.c.filename,
2088 filename = relation(ContentFilename),
2089 filepath_id = self.tbl_content_associations.c.filepath,
2090 filepath = relation(ContentFilepath),
2091 binary_id = self.tbl_content_associations.c.binary_pkg,
2092 binary = relation(DBBinary)))
2095 mapper(ContentFilename, self.tbl_content_file_names,
2096 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2097 filename = self.tbl_content_file_names.c.file))
2099 mapper(ContentFilepath, self.tbl_content_file_paths,
2100 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2101 filepath = self.tbl_content_file_paths.c.path))
2103 mapper(DSCFile, self.tbl_dsc_files,
2104 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2105 source_id = self.tbl_dsc_files.c.source,
2106 source = relation(DBSource),
2107 poolfile_id = self.tbl_dsc_files.c.file,
2108 poolfile = relation(PoolFile)))
2110 mapper(PoolFile, self.tbl_files,
2111 properties = dict(file_id = self.tbl_files.c.id,
2112 filesize = self.tbl_files.c.size,
2113 location_id = self.tbl_files.c.location,
2114 location = relation(Location)))
2116 mapper(Fingerprint, self.tbl_fingerprint,
2117 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2118 uid_id = self.tbl_fingerprint.c.uid,
2119 uid = relation(Uid),
2120 keyring_id = self.tbl_fingerprint.c.keyring,
2121 keyring = relation(Keyring)))
2123 mapper(Keyring, self.tbl_keyrings,
2124 properties = dict(keyring_name = self.tbl_keyrings.c.name,
2125 keyring_id = self.tbl_keyrings.c.id))
2127 mapper(Location, self.tbl_location,
2128 properties = dict(location_id = self.tbl_location.c.id,
2129 component_id = self.tbl_location.c.component,
2130 component = relation(Component),
2131 archive_id = self.tbl_location.c.archive,
2132 archive = relation(Archive),
2133 archive_type = self.tbl_location.c.type))
2135 mapper(Maintainer, self.tbl_maintainer,
2136 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2138 mapper(NewComment, self.tbl_new_comments,
2139 properties = dict(comment_id = self.tbl_new_comments.c.id))
2141 mapper(Override, self.tbl_override,
2142 properties = dict(suite_id = self.tbl_override.c.suite,
2143 suite = relation(Suite),
2144 component_id = self.tbl_override.c.component,
2145 component = relation(Component),
2146 priority_id = self.tbl_override.c.priority,
2147 priority = relation(Priority),
2148 section_id = self.tbl_override.c.section,
2149 section = relation(Section),
2150 overridetype_id = self.tbl_override.c.type,
2151 overridetype = relation(OverrideType)))
2153 mapper(OverrideType, self.tbl_override_type,
2154 properties = dict(overridetype = self.tbl_override_type.c.type,
2155 overridetype_id = self.tbl_override_type.c.id))
2157 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2158 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2159 filepath_id = self.tbl_pending_content_associations.c.filepath,
2160 filepath = relation(ContentFilepath),
2161 filename_id = self.tbl_pending_content_associations.c.filename,
2162 filename = relation(ContentFilename)))
2164 mapper(Priority, self.tbl_priority,
2165 properties = dict(priority_id = self.tbl_priority.c.id))
2167 mapper(Queue, self.tbl_queue,
2168 properties = dict(queue_id = self.tbl_queue.c.id))
2170 mapper(QueueBuild, self.tbl_queue_build,
2171 properties = dict(suite_id = self.tbl_queue_build.c.suite,
2172 queue_id = self.tbl_queue_build.c.queue,
2173 queue = relation(Queue, backref='queuebuild')))
2175 mapper(Section, self.tbl_section,
2176 properties = dict(section_id = self.tbl_section.c.id))
2178 mapper(DBSource, self.tbl_source,
2179 properties = dict(source_id = self.tbl_source.c.id,
2180 version = self.tbl_source.c.version,
2181 maintainer_id = self.tbl_source.c.maintainer,
2182 maintainer = relation(Maintainer,
2183 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2184 poolfile_id = self.tbl_source.c.file,
2185 poolfile = relation(PoolFile),
2186 fingerprint_id = self.tbl_source.c.sig_fpr,
2187 fingerprint = relation(Fingerprint),
2188 changedby_id = self.tbl_source.c.changedby,
2189 changedby = relation(Maintainer,
2190 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2191 srcfiles = relation(DSCFile,
2192 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2193 srcassociations = relation(SrcAssociation,
2194 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2196 mapper(SrcAssociation, self.tbl_src_associations,
2197 properties = dict(sa_id = self.tbl_src_associations.c.id,
2198 suite_id = self.tbl_src_associations.c.suite,
2199 suite = relation(Suite),
2200 source_id = self.tbl_src_associations.c.source,
2201 source = relation(DBSource)))
2203 mapper(SrcUploader, self.tbl_src_uploaders,
2204 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2205 source_id = self.tbl_src_uploaders.c.source,
2206 source = relation(DBSource,
2207 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2208 maintainer_id = self.tbl_src_uploaders.c.maintainer,
2209 maintainer = relation(Maintainer,
2210 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2212 mapper(Suite, self.tbl_suite,
2213 properties = dict(suite_id = self.tbl_suite.c.id))
2215 mapper(SuiteArchitecture, self.tbl_suite_architectures,
2216 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2217 suite = relation(Suite, backref='suitearchitectures'),
2218 arch_id = self.tbl_suite_architectures.c.architecture,
2219 architecture = relation(Architecture)))
2221 mapper(Uid, self.tbl_uid,
2222 properties = dict(uid_id = self.tbl_uid.c.id,
2223 fingerprint = relation(Fingerprint)))
2225 ## Connection functions
2226 def __createconn(self):
2227 from config import Config
2231 connstr = "postgres://%s" % cnf["DB::Host"]
2232 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2233 connstr += ":%s" % cnf["DB::Port"]
2234 connstr += "/%s" % cnf["DB::Name"]
2237 connstr = "postgres:///%s" % cnf["DB::Name"]
2238 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2239 connstr += "?port=%s" % cnf["DB::Port"]
2241 self.db_pg = create_engine(connstr, echo=self.debug)
2242 self.db_meta = MetaData()
2243 self.db_meta.bind = self.db_pg
2244 self.db_smaker = sessionmaker(bind=self.db_pg,
2248 self.__setuptables()
2249 self.__setupmappers()
2252 return self.db_smaker()
2254 __all__.append('DBConn')