]> git.decadent.org.uk Git - dak.git/blobdiff - daklib/dbconn.py
Implement ORMObject.session() and .clone().
[dak.git] / daklib / dbconn.py
index 0156c8d4ea0fe0f0e2a959d4696132df5a6aa54d..df38b777831c019abfbd64dd394c3d564cd26d69 100755 (executable)
@@ -55,7 +55,7 @@ from inspect import getargspec
 import sqlalchemy
 from sqlalchemy import create_engine, Table, MetaData, Column, Integer, desc
 from sqlalchemy.orm import sessionmaker, mapper, relation, object_session, \
-    backref, MapperExtension, EXT_CONTINUE
+    backref, MapperExtension, EXT_CONTINUE, object_mapper
 from sqlalchemy import types as sqltypes
 
 # Don't remove this, we re-export the exceptions to scripts which import us
@@ -287,6 +287,42 @@ class ORMObject(object):
         '''
         return session.query(cls).get(primary_key)
 
+    def session(self, replace = False):
+        '''
+        Returns the current session that is associated with the object. May
+        return None is object is in detached state.
+        '''
+
+        return object_session(self)
+
+    def clone(self, session = None):
+        '''
+        Clones the current object in a new session and returns the new clone. A
+        fresh session is created if the optional session parameter is not
+        provided.
+
+        RATIONALE: SQLAlchemy's session is not thread safe. This method allows
+        cloning of an existing object to allow several threads to work with
+        their own instances of an ORMObject.
+
+        WARNING: Only persistent (committed) objects can be cloned.
+        '''
+
+        if session is None:
+            session = DBConn().session()
+        if self.session() is None:
+            raise RuntimeError('Method clone() failed for detached object:\n%s' %
+                self)
+        self.session().flush()
+        mapper = object_mapper(self)
+        primary_key = mapper.primary_key_from_instance(self)
+        object_class = self.__class__
+        new_object = session.query(object_class).get(primary_key)
+        if new_object is None:
+            raise RuntimeError( \
+                'Method clone() failed for non-persistent object:\n%s' % self)
+        return new_object
+
 __all__.append('ORMObject')
 
 ################################################################################
@@ -475,10 +511,11 @@ def get_suites_binary_in(package, session=None):
 __all__.append('get_suites_binary_in')
 
 @session_wrapper
-def get_component_by_package_suite(package, suite_list, session=None):
+def get_component_by_package_suite(package, suite_list, arch_list=[], session=None):
     '''
     Returns the component name of the newest binary package in suite_list or
-    None if no package is found.
+    None if no package is found. The result can be optionally filtered by a list
+    of architecture names.
 
     @type package: str
     @param package: DBBinary package name to search for
@@ -486,13 +523,19 @@ def get_component_by_package_suite(package, suite_list, session=None):
     @type suite_list: list of str
     @param suite_list: list of suite_name items
 
+    @type arch_list: list of str
+    @param arch_list: optional list of arch_string items that defaults to []
+
     @rtype: str or NoneType
     @return: name of component or None
     '''
 
-    binary = session.query(DBBinary).filter_by(package = package). \
-        join(DBBinary.suites).filter(Suite.suite_name.in_(suite_list)). \
-        order_by(desc(DBBinary.version)).first()
+    q = session.query(DBBinary).filter_by(package = package). \
+        join(DBBinary.suites).filter(Suite.suite_name.in_(suite_list))
+    if len(arch_list) > 0:
+        q = q.join(DBBinary.architecture). \
+            filter(Architecture.arch_string.in_(arch_list))
+    binary = q.order_by(desc(DBBinary.version)).first()
     if binary is None:
         return None
     else:
@@ -500,23 +543,6 @@ def get_component_by_package_suite(package, suite_list, session=None):
 
 __all__.append('get_component_by_package_suite')
 
-@session_wrapper
-def get_binary_components(package, suitename, arch, session=None):
-    # Check for packages that have moved from one component to another
-    query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
-    WHERE b.package=:package AND s.suite_name=:suitename
-      AND (a.arch_string = :arch OR a.arch_string = 'all')
-      AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
-      AND f.location = l.id
-      AND l.component = c.id
-      AND b.file = f.id"""
-
-    vals = {'package': package, 'suitename': suitename, 'arch': arch}
-
-    return session.execute(query, vals)
-
-__all__.append('get_binary_components')
-
 ################################################################################
 
 class BinaryACL(object):
@@ -1171,8 +1197,8 @@ class PoolFile(ORMObject):
     def fullpath(self):
         return os.path.join(self.location.path, self.filename)
 
-    def is_valid(self, filesize = -1, md5sum = None):\
-        return self.filesize == filesize and self.md5sum == md5sum
+    def is_valid(self, filesize = -1, md5sum = None):
+        return self.filesize == long(filesize) and self.md5sum == md5sum
 
     def properties(self):
         return ['filename', 'file_id', 'filesize', 'md5sum', 'sha1sum', \
@@ -1585,8 +1611,6 @@ __all__.append('get_dbchange')
 
 ################################################################################
 
-# TODO: Why do we have a separate Location class? Can't it be fully integrated
-# into class Component?
 class Location(ORMObject):
     def __init__(self, path = None, component = None):
         self.path = path
@@ -1595,7 +1619,8 @@ class Location(ORMObject):
         self.archive_type = 'pool'
 
     def properties(self):
-        return ['path', 'archive_type', 'component', 'files_count']
+        return ['path', 'location_id', 'archive_type', 'component', \
+            'files_count']
 
     def not_null_constraints(self):
         return ['path', 'archive_type']