X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=daklib%2Fdbconn.py;h=2237099d75eb296c44b95e478357c6d76ab82031;hb=e2e1c40f2b90edacd2e27fcb87cb406892e98928;hp=0156c8d4ea0fe0f0e2a959d4696132df5a6aa54d;hpb=48dc11caafef8b296d2b7d781e2b23b12364abcb;p=dak.git diff --git a/daklib/dbconn.py b/daklib/dbconn.py index 0156c8d4..2237099d 100755 --- a/daklib/dbconn.py +++ b/daklib/dbconn.py @@ -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,50 @@ 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. The function will fail if a session is provided and has + unflushed changes. + + RATIONALE: SQLAlchemy's session is not thread safe. This method clones + an existing object to allow several threads to work with their own + instances of an ORMObject. + + WARNING: Only persistent (committed) objects can be cloned. Changes + made to the original object that are not committed yet will get lost. + The session of the new object will always be rolled back to avoid + ressource leaks. + ''' + + 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__ + if session is None: + session = DBConn().session() + elif len(session.new) + len(session.dirty) + len(session.deleted) > 0: + raise RuntimeError( \ + 'Method clone() failed due to unflushed changes in session.') + new_object = session.query(object_class).get(primary_key) + session.rollback() + 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 +519,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 +531,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 +551,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 +1205,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 +1619,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 +1627,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']