5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
7 @copyright: 2008-2009 Mark Hymers <mhy@debian.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 ################################################################################
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 # * mhy looks up the definition of "funny"
34 ################################################################################
40 from sqlalchemy import create_engine, Table, MetaData, select
41 from sqlalchemy.orm import sessionmaker, mapper, relation
43 # Don't remove this, we re-export the exceptions to scripts which import us
44 from sqlalchemy.exc import *
46 # Only import Config until Queue stuff is changed to store its config
48 from config import Config
49 from singleton import Singleton
50 from textutils import fix_maintainer
52 ################################################################################
54 __all__ = ['IntegrityError', 'SQLAlchemyError']
56 ################################################################################
58 class Architecture(object):
59 def __init__(self, *args, **kwargs):
62 def __eq__(self, val):
63 if isinstance(val, str):
64 return (self.arch_string== val)
65 # This signals to use the normal comparison operator
68 def __ne__(self, val):
69 if isinstance(val, str):
70 return (self.arch_string != val)
71 # This signals to use the normal comparison operator
75 return '<Architecture %s>' % self.arch_string
77 __all__.append('Architecture')
79 def get_architecture(architecture, session=None):
81 Returns database id for given C{architecture}.
83 @type architecture: string
84 @param architecture: The name of the architecture
86 @type session: Session
87 @param session: Optional SQLA session object (a temporary one will be
88 generated if not supplied)
91 @return: Architecture object for the given arch (None if not present)
95 session = DBConn().session()
96 q = session.query(Architecture).filter_by(arch_string=architecture)
101 __all__.append('get_architecture')
103 def get_architecture_suites(architecture, session=None):
105 Returns list of Suite objects for given C{architecture} name
108 @param source: Architecture name to search for
110 @type session: Session
111 @param session: Optional SQL session object (a temporary one will be
112 generated if not supplied)
115 @return: list of Suite objects for the given name (may be empty)
119 session = DBConn().session()
121 q = session.query(Suite)
122 q = q.join(SuiteArchitecture)
123 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
126 __all__.append('get_architecture_suites')
128 ################################################################################
130 class Archive(object):
131 def __init__(self, *args, **kwargs):
135 return '<Archive %s>' % self.name
137 __all__.append('Archive')
139 def get_archive(archive, session=None):
141 returns database id for given c{archive}.
143 @type archive: string
144 @param archive: the name of the arhive
146 @type session: Session
147 @param session: Optional SQLA session object (a temporary one will be
148 generated if not supplied)
151 @return: Archive object for the given name (None if not present)
154 archive = archive.lower()
156 session = DBConn().session()
157 q = session.query(Archive).filter_by(archive_name=archive)
162 __all__.append('get_archive')
164 ################################################################################
166 class BinAssociation(object):
167 def __init__(self, *args, **kwargs):
171 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
173 __all__.append('BinAssociation')
175 ################################################################################
177 class DBBinary(object):
178 def __init__(self, *args, **kwargs):
182 return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
184 __all__.append('DBBinary')
186 def get_suites_binary_in(package, session=None):
188 Returns list of Suite objects which given C{package} name is in
191 @param source: DBBinary package name to search for
194 @return: list of Suite objects for the given package
198 session = DBConn().session()
200 return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
202 __all__.append('get_suites_binary_in')
204 def get_binary_from_id(id, session=None):
206 Returns DBBinary object for given C{id}
209 @param id: Id of the required binary
211 @type session: Session
212 @param session: Optional SQLA session object (a temporary one will be
213 generated if not supplied)
216 @return: DBBinary object for the given binary (None if not present)
219 session = DBConn().session()
220 q = session.query(DBBinary).filter_by(binary_id=id)
225 __all__.append('get_binary_from_id')
227 def get_binaries_from_name(package, version=None, architecture=None, session=None):
229 Returns list of DBBinary objects for given C{package} name
232 @param package: DBBinary package name to search for
234 @type version: str or None
235 @param version: Version to search for (or None)
237 @type package: str, list or None
238 @param package: Architectures to limit to (or None if no limit)
240 @type session: Session
241 @param session: Optional SQL session object (a temporary one will be
242 generated if not supplied)
245 @return: list of DBBinary objects for the given name (may be empty)
248 session = DBConn().session()
250 q = session.query(DBBinary).filter_by(package=package)
252 if version is not None:
253 q = q.filter_by(version=version)
255 if architecture is not None:
256 if not isinstance(architecture, list):
257 architecture = [architecture]
258 q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
262 __all__.append('get_binaries_from_name')
264 def get_binaries_from_source_id(source_id, session=None):
266 Returns list of DBBinary objects for given C{source_id}
269 @param source_id: source_id to search for
271 @type session: Session
272 @param session: Optional SQL session object (a temporary one will be
273 generated if not supplied)
276 @return: list of DBBinary objects for the given name (may be empty)
279 session = DBConn().session()
280 return session.query(DBBinary).filter_by(source_id=source_id).all()
282 __all__.append('get_binaries_from_source_id')
285 def get_binary_from_name_suite(package, suitename, session=None):
286 ### For dak examine-package
287 ### XXX: Doesn't use object API yet
289 session = DBConn().session()
291 sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
292 FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
293 WHERE b.package=:package
295 AND fi.location = l.id
296 AND l.component = c.id
299 AND su.suite_name=:suitename
300 ORDER BY b.version DESC"""
302 return session.execute(sql, {'package': package, 'suitename': suitename})
304 __all__.append('get_binary_from_name_suite')
306 def get_binary_components(package, suitename, arch, session=None):
307 # Check for packages that have moved from one component to another
308 query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
309 WHERE b.package=:package AND s.suite_name=:suitename
310 AND (a.arch_string = :arch OR a.arch_string = 'all')
311 AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
312 AND f.location = l.id
313 AND l.component = c.id
316 vals = {'package': package, 'suitename': suitename, 'arch': arch}
319 session = DBConn().session()
320 return session.execute(query, vals)
322 __all__.append('get_binary_components')
324 ################################################################################
326 class Component(object):
327 def __init__(self, *args, **kwargs):
330 def __eq__(self, val):
331 if isinstance(val, str):
332 return (self.component_name == val)
333 # This signals to use the normal comparison operator
334 return NotImplemented
336 def __ne__(self, val):
337 if isinstance(val, str):
338 return (self.component_name != val)
339 # This signals to use the normal comparison operator
340 return NotImplemented
343 return '<Component %s>' % self.component_name
346 __all__.append('Component')
348 def get_component(component, session=None):
350 Returns database id for given C{component}.
352 @type component: string
353 @param component: The name of the override type
356 @return: the database id for the given component
359 component = component.lower()
361 session = DBConn().session()
362 q = session.query(Component).filter_by(component_name=component)
367 __all__.append('get_component')
369 ################################################################################
371 class DBConfig(object):
372 def __init__(self, *args, **kwargs):
376 return '<DBConfig %s>' % self.name
378 __all__.append('DBConfig')
380 ################################################################################
382 class ContentFilename(object):
383 def __init__(self, *args, **kwargs):
387 return '<ContentFilename %s>' % self.filename
389 __all__.append('ContentFilename')
391 def get_or_set_contents_file_id(filename, session=None):
393 Returns database id for given filename.
395 If no matching file is found, a row is inserted.
397 @type filename: string
398 @param filename: The filename
399 @type session: SQLAlchemy
400 @param session: Optional SQL session object (a temporary one will be
401 generated if not supplied). If not passed, a commit will be performed at
402 the end of the function, otherwise the caller is responsible for commiting.
405 @return: the database id for the given component
409 session = DBConn().session()
413 q = session.query(ContentFilename).filter_by(filename=filename)
415 cf = ContentFilename()
416 cf.filename = filename
420 return cf.cafilename_id
422 return q.one().cafilename_id
425 traceback.print_exc()
428 __all__.append('get_or_set_contents_file_id')
430 def get_contents(suite, overridetype, section=None, session=None):
432 Returns contents for a suite / overridetype combination, limiting
433 to a section if not None.
436 @param suite: Suite object
438 @type overridetype: OverrideType
439 @param overridetype: OverrideType object
441 @type section: Section
442 @param section: Optional section object to limit results to
444 @type session: SQLAlchemy
445 @param session: Optional SQL session object (a temporary one will be
446 generated if not supplied)
449 @return: ResultsProxy object set up to return tuples of (filename, section,
454 session = DBConn().session()
456 # find me all of the contents for a given suite
457 contents_q = """SELECT (p.path||'/'||n.file) AS fn,
461 FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
462 JOIN content_file_names n ON (c.filename=n.id)
463 JOIN binaries b ON (b.id=c.binary_pkg)
464 JOIN override o ON (o.package=b.package)
465 JOIN section s ON (s.id=o.section)
466 WHERE o.suite = :suiteid AND o.type = :overridetypeid
467 AND b.type=:overridetypename"""
469 vals = {'suiteid': suite.suite_id,
470 'overridetypeid': overridetype.overridetype_id,
471 'overridetypename': overridetype.overridetype}
473 if section is not None:
474 contents_q += " AND s.id = :sectionid"
475 vals['sectionid'] = section.section_id
477 contents_q += " ORDER BY fn"
479 return session.execute(contents_q, vals)
481 __all__.append('get_contents')
483 ################################################################################
485 class ContentFilepath(object):
486 def __init__(self, *args, **kwargs):
490 return '<ContentFilepath %s>' % self.filepath
492 __all__.append('ContentFilepath')
494 def get_or_set_contents_path_id(filepath, session):
496 Returns database id for given path.
498 If no matching file is found, a row is inserted.
500 @type filename: string
501 @param filename: The filepath
502 @type session: SQLAlchemy
503 @param session: Optional SQL session object (a temporary one will be
504 generated if not supplied). If not passed, a commit will be performed at
505 the end of the function, otherwise the caller is responsible for commiting.
508 @return: the database id for the given path
512 session = DBConn().session()
516 q = session.query(ContentFilepath).filter_by(filepath=filepath)
518 cf = ContentFilepath()
519 cf.filepath = filepath
523 return cf.cafilepath_id
525 return q.one().cafilepath_id
528 traceback.print_exc()
531 __all__.append('get_or_set_contents_path_id')
533 ################################################################################
535 class ContentAssociation(object):
536 def __init__(self, *args, **kwargs):
540 return '<ContentAssociation %s>' % self.ca_id
542 __all__.append('ContentAssociation')
544 def insert_content_paths(binary_id, fullpaths, session=None):
546 Make sure given path is associated with given binary id
549 @param binary_id: the id of the binary
550 @type fullpaths: list
551 @param fullpaths: the list of paths of the file being associated with the binary
552 @type session: SQLAlchemy session
553 @param session: Optional SQLAlchemy session. If this is passed, the caller
554 is responsible for ensuring a transaction has begun and committing the
555 results or rolling back based on the result code. If not passed, a commit
556 will be performed at the end of the function, otherwise the caller is
557 responsible for commiting.
559 @return: True upon success
565 session = DBConn().session()
569 for fullpath in fullpaths:
570 (path, file) = os.path.split(fullpath)
572 # Get the necessary IDs ...
573 ca = ContentAssociation()
574 ca.binary_id = binary_id
575 ca.filename_id = get_or_set_contents_file_id(file)
576 ca.filepath_id = get_or_set_contents_path_id(path)
579 # Only commit if we set up the session ourself
585 traceback.print_exc()
587 # Only rollback if we set up the session ourself
593 __all__.append('insert_content_paths')
595 ################################################################################
597 class DSCFile(object):
598 def __init__(self, *args, **kwargs):
602 return '<DSCFile %s>' % self.dscfile_id
604 __all__.append('DSCFile')
606 ################################################################################
608 class PoolFile(object):
609 def __init__(self, *args, **kwargs):
613 return '<PoolFile %s>' % self.filename
615 __all__.append('PoolFile')
617 def get_poolfile_by_name(filename, location_id=None, session=None):
619 Returns an array of PoolFile objects for the given filename and
620 (optionally) location_id
622 @type filename: string
623 @param filename: the filename of the file to check against the DB
625 @type location_id: int
626 @param location_id: the id of the location to look in (optional)
629 @return: array of PoolFile objects
632 if session is not None:
633 session = DBConn().session()
635 q = session.query(PoolFile).filter_by(filename=filename)
637 if location_id is not None:
638 q = q.join(Location).filter_by(location_id=location_id)
642 __all__.append('get_poolfile_by_name')
644 def get_poolfile_like_name(filename, session=None):
646 Returns an array of PoolFile objects which are like the given name
648 @type filename: string
649 @param filename: the filename of the file to check against the DB
652 @return: array of PoolFile objects
655 if session is not None:
656 session = DBConn().session()
658 # TODO: There must be a way of properly using bind parameters with %FOO%
659 q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
663 __all__.append('get_poolfile_like_name')
665 ################################################################################
667 class Fingerprint(object):
668 def __init__(self, *args, **kwargs):
672 return '<Fingerprint %s>' % self.fingerprint
674 __all__.append('Fingerprint')
676 ################################################################################
678 class Keyring(object):
679 def __init__(self, *args, **kwargs):
683 return '<Keyring %s>' % self.keyring_name
685 __all__.append('Keyring')
687 ################################################################################
689 class Location(object):
690 def __init__(self, *args, **kwargs):
694 return '<Location %s (%s)>' % (self.path, self.location_id)
696 __all__.append('Location')
698 def get_location(location, component=None, archive=None, session=None):
700 Returns Location object for the given combination of location, component
703 @type location: string
704 @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
706 @type component: string
707 @param component: the component name (if None, no restriction applied)
709 @type archive: string
710 @param archive_id: the archive name (if None, no restriction applied)
712 @rtype: Location / None
713 @return: Either a Location object or None if one can't be found
717 session = DBConn().session()
719 q = session.query(Location).filter_by(path=location)
721 if archive is not None:
722 q = q.join(Archive).filter_by(archive_name=archive)
724 if component is not None:
725 q = q.join(Component).filter_by(component_name=component)
732 __all__.append('get_location')
734 ################################################################################
736 class Maintainer(object):
737 def __init__(self, *args, **kwargs):
741 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
743 def get_split_maintainer(self):
744 if not hasattr(self, 'name') or self.name is None:
745 return ('', '', '', '')
747 return fix_maintainer(self.name.strip())
749 __all__.append('Maintainer')
751 ################################################################################
753 class NewComment(object):
754 def __init__(self, *args, **kwargs):
758 return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
760 __all__.append('NewComment')
762 def has_new_comment(package, version, session=None):
764 Returns true if the given combination of C{package}, C{version} has a comment.
766 @type package: string
767 @param package: name of the package
769 @type version: string
770 @param version: package version
772 @type session: Session
773 @param session: Optional SQLA session object (a temporary one will be
774 generated if not supplied)
781 session = DBConn().session()
783 q = session.query(NewComment)
784 q = q.filter_by(package=package)
785 q = q.filter_by(version=version)
788 __all__.append('has_new_comment')
790 def get_new_comments(package=None, version=None, comment_id=None, session=None):
792 Returns (possibly empty) list of NewComment objects for the given
795 @type package: string (optional)
796 @param package: name of the package
798 @type version: string (optional)
799 @param version: package version
801 @type comment_id: int (optional)
802 @param comment_id: An id of a comment
804 @type session: Session
805 @param session: Optional SQLA session object (a temporary one will be
806 generated if not supplied)
809 @return: A (possibly empty) list of NewComment objects will be returned
814 session = DBConn().session()
816 q = session.query(NewComment)
817 if package is not None: q = q.filter_by(package=package)
818 if version is not None: q = q.filter_by(version=version)
819 if comment_id is not None: q = q.filter_by(comment_id=comment_id)
823 __all__.append('get_new_comments')
825 ################################################################################
827 class Override(object):
828 def __init__(self, *args, **kwargs):
832 return '<Override %s (%s)>' % (self.package, self.suite_id)
834 __all__.append('Override')
836 def get_override(package, suite=None, component=None, overridetype=None, session=None):
838 Returns Override object for the given parameters
840 @type package: string
841 @param package: The name of the package
843 @type suite: string, list or None
844 @param suite: The name of the suite (or suites if a list) to limit to. If
845 None, don't limit. Defaults to None.
847 @type component: string, list or None
848 @param component: The name of the component (or components if a list) to
849 limit to. If None, don't limit. Defaults to None.
851 @type overridetype: string, list or None
852 @param overridetype: The name of the overridetype (or overridetypes if a list) to
853 limit to. If None, don't limit. Defaults to None.
855 @type session: Session
856 @param session: Optional SQLA session object (a temporary one will be
857 generated if not supplied)
860 @return: A (possibly empty) list of Override objects will be returned
864 session = DBConn().session()
866 q = session.query(Override)
867 q = q.filter_by(package=package)
869 if suite is not None:
870 if not isinstance(suite, list): suite = [suite]
871 q = q.join(Suite).filter(Suite.suite_name.in_(suite))
873 if component is not None:
874 if not isinstance(component, list): component = [component]
875 q = q.join(Component).filter(Component.component_name.in_(component))
877 if overridetype is not None:
878 if not isinstance(overridetype, list): overridetype = [overridetype]
879 q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
883 __all__.append('get_override')
886 ################################################################################
888 class OverrideType(object):
889 def __init__(self, *args, **kwargs):
893 return '<OverrideType %s>' % self.overridetype
895 __all__.append('OverrideType')
897 def get_override_type(override_type, session=None):
899 Returns OverrideType object for given C{override type}.
901 @type override_type: string
902 @param override_type: The name of the override type
904 @type session: Session
905 @param session: Optional SQLA session object (a temporary one will be
906 generated if not supplied)
909 @return: the database id for the given override type
913 session = DBConn().session()
914 q = session.query(OverrideType).filter_by(overridetype=override_type)
919 __all__.append('get_override_type')
921 ################################################################################
923 class PendingContentAssociation(object):
924 def __init__(self, *args, **kwargs):
928 return '<PendingContentAssociation %s>' % self.pca_id
930 __all__.append('PendingContentAssociation')
932 def insert_pending_content_paths(package, fullpaths, session=None):
934 Make sure given paths are temporarily associated with given
938 @param package: the package to associate with should have been read in from the binary control file
939 @type fullpaths: list
940 @param fullpaths: the list of paths of the file being associated with the binary
941 @type session: SQLAlchemy session
942 @param session: Optional SQLAlchemy session. If this is passed, the caller
943 is responsible for ensuring a transaction has begun and committing the
944 results or rolling back based on the result code. If not passed, a commit
945 will be performed at the end of the function
947 @return: True upon success, False if there is a problem
953 session = DBConn().session()
957 arch = get_architecture(package['Architecture'], session)
958 arch_id = arch.arch_id
960 # Remove any already existing recorded files for this package
961 q = session.query(PendingContentAssociation)
962 q = q.filter_by(package=package['Package'])
963 q = q.filter_by(version=package['Version'])
964 q = q.filter_by(architecture=arch_id)
968 for fullpath in fullpaths:
969 (path, file) = os.path.split(fullpath)
971 if path.startswith( "./" ):
974 pca = PendingContentAssociation()
975 pca.package = package['Package']
976 pca.version = package['Version']
977 pca.filename_id = get_or_set_contents_file_id(file, session)
978 pca.filepath_id = get_or_set_contents_path_id(path, session)
979 pca.architecture = arch_id
982 # Only commit if we set up the session ourself
988 traceback.print_exc()
990 # Only rollback if we set up the session ourself
996 __all__.append('insert_pending_content_paths')
998 ################################################################################
1000 class Priority(object):
1001 def __init__(self, *args, **kwargs):
1004 def __eq__(self, val):
1005 if isinstance(val, str):
1006 return (self.priority == val)
1007 # This signals to use the normal comparison operator
1008 return NotImplemented
1010 def __ne__(self, val):
1011 if isinstance(val, str):
1012 return (self.priority != val)
1013 # This signals to use the normal comparison operator
1014 return NotImplemented
1017 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1019 __all__.append('Priority')
1021 def get_priority(priority, session=None):
1023 Returns Priority object for given C{priority name}.
1025 @type priority: string
1026 @param priority: The name of the priority
1028 @type session: Session
1029 @param session: Optional SQLA session object (a temporary one will be
1030 generated if not supplied)
1033 @return: Priority object for the given priority
1037 session = DBConn().session()
1038 q = session.query(Priority).filter_by(priority=priority)
1043 __all__.append('get_priority')
1045 ################################################################################
1047 class Queue(object):
1048 def __init__(self, *args, **kwargs):
1052 return '<Queue %s>' % self.queue_name
1054 def autobuild_upload(self, changes, srcpath, session=None):
1056 Update queue_build database table used for incoming autobuild support.
1058 @type changes: Changes
1059 @param changes: changes object for the upload to process
1061 @type srcpath: string
1062 @param srcpath: path for the queue file entries/link destinations
1064 @type session: SQLAlchemy session
1065 @param session: Optional SQLAlchemy session. If this is passed, the
1066 caller is responsible for ensuring a transaction has begun and
1067 committing the results or rolling back based on the result code. If
1068 not passed, a commit will be performed at the end of the function,
1069 otherwise the caller is responsible for commiting.
1071 @rtype: NoneType or string
1072 @return: None if the operation failed, a string describing the error if not
1077 session = DBConn().session()
1080 # TODO: Remove by moving queue config into the database
1083 for suitename in changes.changes["distribution"].keys():
1084 # TODO: Move into database as:
1085 # buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1086 # buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1087 # This also gets rid of the SecurityQueueBuild hack below
1088 if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1092 s = get_suite(suitename, session)
1094 return "INTERNAL ERROR: Could not find suite %s" % suitename
1096 # TODO: Get from database as above
1097 dest_dir = conf["Dir::QueueBuild"]
1099 # TODO: Move into database as above
1100 if conf.FindB("Dinstall::SecurityQueueBuild"):
1101 dest_dir = os.path.join(dest_dir, suitename)
1103 for file_entry in changes.files.keys():
1104 src = os.path.join(srcpath, file_entry)
1105 dest = os.path.join(dest_dir, file_entry)
1107 # TODO: Move into database as above
1108 if Cnf.FindB("Dinstall::SecurityQueueBuild"):
1109 # Copy it since the original won't be readable by www-data
1110 utils.copy(src, dest)
1112 # Create a symlink to it
1113 os.symlink(src, dest)
1116 qb.suite_id = s.suite_id
1117 qb.queue_id = self.queue_id
1123 # If the .orig.tar.gz is in the pool, create a symlink to
1124 # it (if one doesn't already exist)
1125 if changes.orig_tar_id:
1126 # Determine the .orig.tar.gz file name
1127 for dsc_file in changes.dsc_files.keys():
1128 if dsc_file.endswith(".orig.tar.gz"):
1131 dest = os.path.join(dest_dir, filename)
1133 # If it doesn't exist, create a symlink
1134 if not os.path.exists(dest):
1135 q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1136 {'id': changes.orig_tar_id})
1139 return "[INTERNAL ERROR] Couldn't find id %s in files table." % (changes.orig_tar_id)
1141 src = os.path.join(res[0], res[1])
1142 os.symlink(src, dest)
1144 # Add it to the list of packages for later processing by apt-ftparchive
1146 qb.suite_id = s.suite_id
1147 qb.queue_id = self.queue_id
1152 # If it does, update things to ensure it's not removed prematurely
1154 qb = get_queue_build(dest, suite_id, session)
1165 __all__.append('Queue')
1167 def get_queue(queuename, session=None):
1169 Returns Queue object for given C{queue name}.
1171 @type queuename: string
1172 @param queuename: The name of the queue
1174 @type session: Session
1175 @param session: Optional SQLA session object (a temporary one will be
1176 generated if not supplied)
1179 @return: Queue object for the given queue
1183 session = DBConn().session()
1184 q = session.query(Queue).filter_by(queue_name=queuename)
1189 __all__.append('get_queue')
1191 ################################################################################
1193 class QueueBuild(object):
1194 def __init__(self, *args, **kwargs):
1198 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1200 __all__.append('QueueBuild')
1202 def get_queue_build(filename, suite_id, session=None):
1204 Returns QueueBuild object for given C{filename} and C{suite id}.
1206 @type filename: string
1207 @param filename: The name of the file
1210 @param suiteid: Suite ID
1212 @type session: Session
1213 @param session: Optional SQLA session object (a temporary one will be
1214 generated if not supplied)
1217 @return: Queue object for the given queue
1221 session = DBConn().session()
1222 q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite_id)
1227 __all__.append('get_queue_build')
1229 ################################################################################
1231 class Section(object):
1232 def __init__(self, *args, **kwargs):
1235 def __eq__(self, val):
1236 if isinstance(val, str):
1237 return (self.section == val)
1238 # This signals to use the normal comparison operator
1239 return NotImplemented
1241 def __ne__(self, val):
1242 if isinstance(val, str):
1243 return (self.section != val)
1244 # This signals to use the normal comparison operator
1245 return NotImplemented
1248 return '<Section %s>' % self.section
1250 __all__.append('Section')
1252 def get_section(section, session=None):
1254 Returns Section object for given C{section name}.
1256 @type section: string
1257 @param section: The name of the section
1259 @type session: Session
1260 @param session: Optional SQLA session object (a temporary one will be
1261 generated if not supplied)
1264 @return: Section object for the given section name
1268 session = DBConn().session()
1269 q = session.query(Section).filter_by(section=section)
1274 __all__.append('get_section')
1276 ################################################################################
1278 class DBSource(object):
1279 def __init__(self, *args, **kwargs):
1283 return '<DBSource %s (%s)>' % (self.source, self.version)
1285 __all__.append('DBSource')
1287 def source_exists(source, source_version, suites = ["any"], session=None):
1289 Ensure that source exists somewhere in the archive for the binary
1290 upload being processed.
1291 1. exact match => 1.0-3
1292 2. bin-only NMU => 1.0-3+b1 , 1.0-3.1+b1
1294 @type package: string
1295 @param package: package source name
1297 @type source_version: string
1298 @param source_version: expected source version
1301 @param suites: list of suites to check in, default I{any}
1303 @type session: Session
1304 @param session: Optional SQLA session object (a temporary one will be
1305 generated if not supplied)
1308 @return: returns 1 if a source with expected version is found, otherwise 0
1313 session = DBConn().session()
1317 for suite in suites:
1318 q = session.query(DBSource).filter_by(source=source)
1320 # source must exist in suite X, or in some other suite that's
1321 # mapped to X, recursively... silent-maps are counted too,
1322 # unreleased-maps aren't.
1323 maps = cnf.ValueList("SuiteMappings")[:]
1325 maps = [ m.split() for m in maps ]
1326 maps = [ (x[1], x[2]) for x in maps
1327 if x[0] == "map" or x[0] == "silent-map" ]
1330 if x[1] in s and x[0] not in s:
1333 q = q.join(SrcAssociation).join(Suite)
1334 q = q.filter(Suite.suite_name.in_(s))
1336 # Reduce the query results to a list of version numbers
1337 ql = [ j.version for j in q.all() ]
1340 if source_version in ql:
1344 from daklib.regexes import re_bin_only_nmu
1345 orig_source_version = re_bin_only_nmu.sub('', source_version)
1346 if orig_source_version in ql:
1349 # No source found so return not ok
1355 __all__.append('source_exists')
1357 def get_suites_source_in(source, session=None):
1359 Returns list of Suite objects which given C{source} name is in
1362 @param source: DBSource package name to search for
1365 @return: list of Suite objects for the given source
1369 session = DBConn().session()
1371 return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1373 __all__.append('get_suites_source_in')
1375 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1377 Returns list of DBSource objects for given C{source} name and other parameters
1380 @param source: DBSource package name to search for
1382 @type source: str or None
1383 @param source: DBSource version name to search for or None if not applicable
1385 @type dm_upload_allowed: bool
1386 @param dm_upload_allowed: If None, no effect. If True or False, only
1387 return packages with that dm_upload_allowed setting
1389 @type session: Session
1390 @param session: Optional SQL session object (a temporary one will be
1391 generated if not supplied)
1394 @return: list of DBSource objects for the given name (may be empty)
1397 session = DBConn().session()
1399 q = session.query(DBSource).filter_by(source=source)
1401 if version is not None:
1402 q = q.filter_by(version=version)
1404 if dm_upload_allowed is not None:
1405 q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1409 __all__.append('get_sources_from_name')
1411 def get_source_in_suite(source, suite, session=None):
1413 Returns list of DBSource objects for a combination of C{source} and C{suite}.
1415 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1416 - B{suite} - a suite name, eg. I{unstable}
1418 @type source: string
1419 @param source: source package name
1422 @param suite: the suite name
1425 @return: the version for I{source} in I{suite}
1429 session = DBConn().session()
1430 q = session.query(SrcAssociation)
1431 q = q.join('source').filter_by(source=source)
1432 q = q.join('suite').filter_by(suite_name=suite)
1435 # ???: Maybe we should just return the SrcAssociation object instead
1436 return q.one().source
1438 __all__.append('get_source_in_suite')
1440 ################################################################################
1442 class SrcAssociation(object):
1443 def __init__(self, *args, **kwargs):
1447 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1449 __all__.append('SrcAssociation')
1451 ################################################################################
1453 class SrcUploader(object):
1454 def __init__(self, *args, **kwargs):
1458 return '<SrcUploader %s>' % self.uploader_id
1460 __all__.append('SrcUploader')
1462 ################################################################################
1464 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1465 ('SuiteID', 'suite_id'),
1466 ('Version', 'version'),
1467 ('Origin', 'origin'),
1469 ('Description', 'description'),
1470 ('Untouchable', 'untouchable'),
1471 ('Announce', 'announce'),
1472 ('Codename', 'codename'),
1473 ('OverrideCodename', 'overridecodename'),
1474 ('ValidTime', 'validtime'),
1475 ('Priority', 'priority'),
1476 ('NotAutomatic', 'notautomatic'),
1477 ('CopyChanges', 'copychanges'),
1478 ('CopyDotDak', 'copydotdak'),
1479 ('CommentsDir', 'commentsdir'),
1480 ('OverrideSuite', 'overridesuite'),
1481 ('ChangelogBase', 'changelogbase')]
1484 class Suite(object):
1485 def __init__(self, *args, **kwargs):
1489 return '<Suite %s>' % self.suite_name
1491 def __eq__(self, val):
1492 if isinstance(val, str):
1493 return (self.suite_name == val)
1494 # This signals to use the normal comparison operator
1495 return NotImplemented
1497 def __ne__(self, val):
1498 if isinstance(val, str):
1499 return (self.suite_name != val)
1500 # This signals to use the normal comparison operator
1501 return NotImplemented
1505 for disp, field in SUITE_FIELDS:
1506 val = getattr(self, field, None)
1508 ret.append("%s: %s" % (disp, val))
1510 return "\n".join(ret)
1512 __all__.append('Suite')
1514 def get_suite_architecture(suite, architecture, session=None):
1516 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1520 @param suite: Suite name to search for
1522 @type architecture: str
1523 @param architecture: Architecture name to search for
1525 @type session: Session
1526 @param session: Optional SQL session object (a temporary one will be
1527 generated if not supplied)
1529 @rtype: SuiteArchitecture
1530 @return: the SuiteArchitecture object or None
1534 session = DBConn().session()
1536 q = session.query(SuiteArchitecture)
1537 q = q.join(Architecture).filter_by(arch_string=architecture)
1538 q = q.join(Suite).filter_by(suite_name=suite)
1543 __all__.append('get_suite_architecture')
1545 def get_suite(suite, session=None):
1547 Returns Suite object for given C{suite name}.
1550 @param suite: The name of the suite
1552 @type session: Session
1553 @param session: Optional SQLA session object (a temporary one will be
1554 generated if not supplied)
1557 @return: Suite object for the requested suite name (None if not presenT)
1561 session = DBConn().session()
1562 q = session.query(Suite).filter_by(suite_name=suite)
1567 __all__.append('get_suite')
1569 ################################################################################
1571 class SuiteArchitecture(object):
1572 def __init__(self, *args, **kwargs):
1576 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1578 __all__.append('SuiteArchitecture')
1580 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1582 Returns list of Architecture objects for given C{suite} name
1585 @param source: Suite name to search for
1587 @type skipsrc: boolean
1588 @param skipsrc: Whether to skip returning the 'source' architecture entry
1591 @type skipall: boolean
1592 @param skipall: Whether to skip returning the 'all' architecture entry
1595 @type session: Session
1596 @param session: Optional SQL session object (a temporary one will be
1597 generated if not supplied)
1600 @return: list of Architecture objects for the given name (may be empty)
1604 session = DBConn().session()
1606 q = session.query(Architecture)
1607 q = q.join(SuiteArchitecture)
1608 q = q.join(Suite).filter_by(suite_name=suite)
1610 q = q.filter(Architecture.arch_string != 'source')
1612 q = q.filter(Architecture.arch_string != 'all')
1613 q = q.order_by('arch_string')
1616 __all__.append('get_suite_architectures')
1618 ################################################################################
1621 def __init__(self, *args, **kwargs):
1624 def __eq__(self, val):
1625 if isinstance(val, str):
1626 return (self.uid == val)
1627 # This signals to use the normal comparison operator
1628 return NotImplemented
1630 def __ne__(self, val):
1631 if isinstance(val, str):
1632 return (self.uid != val)
1633 # This signals to use the normal comparison operator
1634 return NotImplemented
1637 return '<Uid %s (%s)>' % (self.uid, self.name)
1639 __all__.append('Uid')
1641 def add_database_user(uidname, session=None):
1643 Adds a database user
1645 @type uidname: string
1646 @param uidname: The uid of the user to add
1648 @type session: SQLAlchemy
1649 @param session: Optional SQL session object (a temporary one will be
1650 generated if not supplied). If not passed, a commit will be performed at
1651 the end of the function, otherwise the caller is responsible for commiting.
1654 @return: the uid object for the given uidname
1656 privatetrans = False
1658 session = DBConn().session()
1662 session.execute("CREATE USER :uid", {'uid': uidname})
1666 traceback.print_exc()
1669 __all__.append('add_database_user')
1671 def get_or_set_uid(uidname, session=None):
1673 Returns uid object for given uidname.
1675 If no matching uidname is found, a row is inserted.
1677 @type uidname: string
1678 @param uidname: The uid to add
1680 @type session: SQLAlchemy
1681 @param session: Optional SQL session object (a temporary one will be
1682 generated if not supplied). If not passed, a commit will be performed at
1683 the end of the function, otherwise the caller is responsible for commiting.
1686 @return: the uid object for the given uidname
1688 privatetrans = False
1690 session = DBConn().session()
1694 q = session.query(Uid).filter_by(uid=uidname)
1706 traceback.print_exc()
1709 __all__.append('get_or_set_uid')
1712 def get_uid_from_fingerprint(fpr, session=None):
1714 session = DBConn().session()
1716 q = session.query(Uid)
1717 q = q.join(Fingerprint).filter_by(fingerprint=fpr)
1724 __all__.append('get_uid_from_fingerprint')
1726 ################################################################################
1728 class DBConn(Singleton):
1730 database module init.
1732 def __init__(self, *args, **kwargs):
1733 super(DBConn, self).__init__(*args, **kwargs)
1735 def _startup(self, *args, **kwargs):
1737 if kwargs.has_key('debug'):
1741 def __setuptables(self):
1742 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1743 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1744 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1745 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1746 self.tbl_component = Table('component', self.db_meta, autoload=True)
1747 self.tbl_config = Table('config', self.db_meta, autoload=True)
1748 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1749 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1750 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1751 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1752 self.tbl_files = Table('files', self.db_meta, autoload=True)
1753 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1754 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1755 self.tbl_location = Table('location', self.db_meta, autoload=True)
1756 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
1757 self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
1758 self.tbl_override = Table('override', self.db_meta, autoload=True)
1759 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
1760 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
1761 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
1762 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
1763 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
1764 self.tbl_section = Table('section', self.db_meta, autoload=True)
1765 self.tbl_source = Table('source', self.db_meta, autoload=True)
1766 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
1767 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
1768 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
1769 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
1770 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
1772 def __setupmappers(self):
1773 mapper(Architecture, self.tbl_architecture,
1774 properties = dict(arch_id = self.tbl_architecture.c.id))
1776 mapper(Archive, self.tbl_archive,
1777 properties = dict(archive_id = self.tbl_archive.c.id,
1778 archive_name = self.tbl_archive.c.name))
1780 mapper(BinAssociation, self.tbl_bin_associations,
1781 properties = dict(ba_id = self.tbl_bin_associations.c.id,
1782 suite_id = self.tbl_bin_associations.c.suite,
1783 suite = relation(Suite),
1784 binary_id = self.tbl_bin_associations.c.bin,
1785 binary = relation(DBBinary)))
1787 mapper(DBBinary, self.tbl_binaries,
1788 properties = dict(binary_id = self.tbl_binaries.c.id,
1789 package = self.tbl_binaries.c.package,
1790 version = self.tbl_binaries.c.version,
1791 maintainer_id = self.tbl_binaries.c.maintainer,
1792 maintainer = relation(Maintainer),
1793 source_id = self.tbl_binaries.c.source,
1794 source = relation(DBSource),
1795 arch_id = self.tbl_binaries.c.architecture,
1796 architecture = relation(Architecture),
1797 poolfile_id = self.tbl_binaries.c.file,
1798 poolfile = relation(PoolFile),
1799 binarytype = self.tbl_binaries.c.type,
1800 fingerprint_id = self.tbl_binaries.c.sig_fpr,
1801 fingerprint = relation(Fingerprint),
1802 install_date = self.tbl_binaries.c.install_date,
1803 binassociations = relation(BinAssociation,
1804 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
1806 mapper(Component, self.tbl_component,
1807 properties = dict(component_id = self.tbl_component.c.id,
1808 component_name = self.tbl_component.c.name))
1810 mapper(DBConfig, self.tbl_config,
1811 properties = dict(config_id = self.tbl_config.c.id))
1813 mapper(ContentAssociation, self.tbl_content_associations,
1814 properties = dict(ca_id = self.tbl_content_associations.c.id,
1815 filename_id = self.tbl_content_associations.c.filename,
1816 filename = relation(ContentFilename),
1817 filepath_id = self.tbl_content_associations.c.filepath,
1818 filepath = relation(ContentFilepath),
1819 binary_id = self.tbl_content_associations.c.binary_pkg,
1820 binary = relation(DBBinary)))
1823 mapper(ContentFilename, self.tbl_content_file_names,
1824 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
1825 filename = self.tbl_content_file_names.c.file))
1827 mapper(ContentFilepath, self.tbl_content_file_paths,
1828 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
1829 filepath = self.tbl_content_file_paths.c.path))
1831 mapper(DSCFile, self.tbl_dsc_files,
1832 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
1833 source_id = self.tbl_dsc_files.c.source,
1834 source = relation(DBSource),
1835 poolfile_id = self.tbl_dsc_files.c.file,
1836 poolfile = relation(PoolFile)))
1838 mapper(PoolFile, self.tbl_files,
1839 properties = dict(file_id = self.tbl_files.c.id,
1840 filesize = self.tbl_files.c.size,
1841 location_id = self.tbl_files.c.location,
1842 location = relation(Location)))
1844 mapper(Fingerprint, self.tbl_fingerprint,
1845 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
1846 uid_id = self.tbl_fingerprint.c.uid,
1847 uid = relation(Uid),
1848 keyring_id = self.tbl_fingerprint.c.keyring,
1849 keyring = relation(Keyring)))
1851 mapper(Keyring, self.tbl_keyrings,
1852 properties = dict(keyring_name = self.tbl_keyrings.c.name,
1853 keyring_id = self.tbl_keyrings.c.id))
1855 mapper(Location, self.tbl_location,
1856 properties = dict(location_id = self.tbl_location.c.id,
1857 component_id = self.tbl_location.c.component,
1858 component = relation(Component),
1859 archive_id = self.tbl_location.c.archive,
1860 archive = relation(Archive),
1861 archive_type = self.tbl_location.c.type))
1863 mapper(Maintainer, self.tbl_maintainer,
1864 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
1866 mapper(NewComment, self.tbl_new_comments,
1867 properties = dict(comment_id = self.tbl_new_comments.c.id))
1869 mapper(Override, self.tbl_override,
1870 properties = dict(suite_id = self.tbl_override.c.suite,
1871 suite = relation(Suite),
1872 component_id = self.tbl_override.c.component,
1873 component = relation(Component),
1874 priority_id = self.tbl_override.c.priority,
1875 priority = relation(Priority),
1876 section_id = self.tbl_override.c.section,
1877 section = relation(Section),
1878 overridetype_id = self.tbl_override.c.type,
1879 overridetype = relation(OverrideType)))
1881 mapper(OverrideType, self.tbl_override_type,
1882 properties = dict(overridetype = self.tbl_override_type.c.type,
1883 overridetype_id = self.tbl_override_type.c.id))
1885 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
1886 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
1887 filepath_id = self.tbl_pending_content_associations.c.filepath,
1888 filepath = relation(ContentFilepath),
1889 filename_id = self.tbl_pending_content_associations.c.filename,
1890 filename = relation(ContentFilename)))
1892 mapper(Priority, self.tbl_priority,
1893 properties = dict(priority_id = self.tbl_priority.c.id))
1895 mapper(Queue, self.tbl_queue,
1896 properties = dict(queue_id = self.tbl_queue.c.id))
1898 mapper(QueueBuild, self.tbl_queue_build,
1899 properties = dict(suite_id = self.tbl_queue_build.c.suite,
1900 queue_id = self.tbl_queue_build.c.queue,
1901 queue = relation(Queue, backref='queuebuild')))
1903 mapper(Section, self.tbl_section,
1904 properties = dict(section_id = self.tbl_section.c.id))
1906 mapper(DBSource, self.tbl_source,
1907 properties = dict(source_id = self.tbl_source.c.id,
1908 version = self.tbl_source.c.version,
1909 maintainer_id = self.tbl_source.c.maintainer,
1910 maintainer = relation(Maintainer,
1911 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
1912 poolfile_id = self.tbl_source.c.file,
1913 poolfile = relation(PoolFile),
1914 fingerprint_id = self.tbl_source.c.sig_fpr,
1915 fingerprint = relation(Fingerprint),
1916 changedby_id = self.tbl_source.c.changedby,
1917 changedby = relation(Maintainer,
1918 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
1919 srcfiles = relation(DSCFile,
1920 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
1921 srcassociations = relation(SrcAssociation,
1922 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
1924 mapper(SrcAssociation, self.tbl_src_associations,
1925 properties = dict(sa_id = self.tbl_src_associations.c.id,
1926 suite_id = self.tbl_src_associations.c.suite,
1927 suite = relation(Suite),
1928 source_id = self.tbl_src_associations.c.source,
1929 source = relation(DBSource)))
1931 mapper(SrcUploader, self.tbl_src_uploaders,
1932 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
1933 source_id = self.tbl_src_uploaders.c.source,
1934 source = relation(DBSource,
1935 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
1936 maintainer_id = self.tbl_src_uploaders.c.maintainer,
1937 maintainer = relation(Maintainer,
1938 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
1940 mapper(Suite, self.tbl_suite,
1941 properties = dict(suite_id = self.tbl_suite.c.id))
1943 mapper(SuiteArchitecture, self.tbl_suite_architectures,
1944 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
1945 suite = relation(Suite, backref='suitearchitectures'),
1946 arch_id = self.tbl_suite_architectures.c.architecture,
1947 architecture = relation(Architecture)))
1949 mapper(Uid, self.tbl_uid,
1950 properties = dict(uid_id = self.tbl_uid.c.id,
1951 fingerprint = relation(Fingerprint)))
1953 ## Connection functions
1954 def __createconn(self):
1955 from config import Config
1959 connstr = "postgres://%s" % cnf["DB::Host"]
1960 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1961 connstr += ":%s" % cnf["DB::Port"]
1962 connstr += "/%s" % cnf["DB::Name"]
1965 connstr = "postgres:///%s" % cnf["DB::Name"]
1966 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1967 connstr += "?port=%s" % cnf["DB::Port"]
1969 self.db_pg = create_engine(connstr, echo=self.debug)
1970 self.db_meta = MetaData()
1971 self.db_meta.bind = self.db_pg
1972 self.db_smaker = sessionmaker(bind=self.db_pg,
1976 self.__setuptables()
1977 self.__setupmappers()
1980 return self.db_smaker()
1982 __all__.append('DBConn')