X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=daklib%2Fdbconn.py;h=113c38aa5fef3bf2d7a31015fa7622440ad73be4;hb=4258a4e33218d4b6c271bd19b0a0723dba1fbed5;hp=f1988ca649d22b5cc6ed6e5b82fbb610ffa13beb;hpb=9124714136139d384b898de4f06920d5a348679e;p=dak.git diff --git a/daklib/dbconn.py b/daklib/dbconn.py index f1988ca6..113c38aa 100755 --- a/daklib/dbconn.py +++ b/daklib/dbconn.py @@ -44,10 +44,11 @@ from sqlalchemy.orm import sessionmaker, mapper, relation from sqlalchemy.exc import * from singleton import Singleton +from textutils import fix_maintainer ################################################################################ -__all__ = [] +__all__ = ['IntegrityError', 'SQLAlchemyError'] ################################################################################ @@ -109,6 +110,8 @@ def get_architecture_suites(architecture, session=None): __all__.append('get_architecture_suites') +################################################################################ + class Archive(object): def __init__(self, *args, **kwargs): pass @@ -143,6 +146,8 @@ def get_archive(archive, session=None): __all__.append('get_archive') +################################################################################ + class BinAssociation(object): def __init__(self, *args, **kwargs): pass @@ -152,18 +157,20 @@ class BinAssociation(object): __all__.append('BinAssociation') -class Binary(object): +################################################################################ + +class DBBinary(object): def __init__(self, *args, **kwargs): pass def __repr__(self): - return '' % (self.package, self.version, self.architecture) + return '' % (self.package, self.version, self.architecture) -__all__.append('Binary') +__all__.append('DBBinary') def get_binary_from_id(id, session=None): """ - Returns Binary object for given C{id} + Returns DBBinary object for given C{id} @type id: int @param id: Id of the required binary @@ -172,12 +179,12 @@ def get_binary_from_id(id, session=None): @param session: Optional SQLA session object (a temporary one will be generated if not supplied) - @rtype: Binary - @return: Binary object for the given binary (None if not present) + @rtype: DBBinary + @return: DBBinary object for the given binary (None if not present) """ if session is None: session = DBConn().session() - q = session.query(Binary).filter_by(binary_id=id) + q = session.query(DBBinary).filter_by(binary_id=id) if q.count() == 0: return None return q.one() @@ -186,24 +193,43 @@ __all__.append('get_binary_from_id') def get_binaries_from_name(package, session=None): """ - Returns list of Binary objects for given C{package} name + Returns list of DBBinary objects for given C{package} name @type package: str - @param package: Binary package name to search for + @param package: DBBinary package name to search for @type session: Session @param session: Optional SQL session object (a temporary one will be generated if not supplied) @rtype: list - @return: list of Binary objects for the given name (may be empty) + @return: list of DBBinary objects for the given name (may be empty) """ if session is None: session = DBConn().session() - return session.query(Binary).filter_by(package=package).all() + return session.query(DBBinary).filter_by(package=package).all() __all__.append('get_binaries_from_name') +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} + + if session is None: + session = DBConn().session() + return session.execute(query, vals) + +__all__.append('get_binary_components') +################################################################################ + class Component(object): def __init__(self, *args, **kwargs): pass @@ -235,6 +261,8 @@ def get_component(component, session=None): __all__.append('get_component') +################################################################################ + class DBConfig(object): def __init__(self, *args, **kwargs): pass @@ -244,6 +272,8 @@ class DBConfig(object): __all__.append('DBConfig') +################################################################################ + class ContentFilename(object): def __init__(self, *args, **kwargs): pass @@ -253,6 +283,42 @@ class ContentFilename(object): __all__.append('ContentFilename') +def get_or_set_contents_file_id(filename, session=None): + """ + Returns database id for given filename. + + If no matching file is found, a row is inserted. + + @type filename: string + @param filename: The filename + @type session: SQLAlchemy + @param session: Optional SQL session object (a temporary one will be + generated if not supplied) + + @rtype: int + @return: the database id for the given component + """ + if session is None: + session = DBConn().session() + + try: + q = session.query(ContentFilename).filter_by(filename=filename) + if q.count() < 1: + cf = ContentFilename() + cf.filename = filename + session.add(cf) + return cf.cafilename_id + else: + return q.one().cafilename_id + + except: + traceback.print_exc() + raise + +__all__.append('get_or_set_contents_file_id') + +################################################################################ + class ContentFilepath(object): def __init__(self, *args, **kwargs): pass @@ -262,6 +328,42 @@ class ContentFilepath(object): __all__.append('ContentFilepath') +def get_or_set_contents_path_id(filepath, session): + """ + Returns database id for given path. + + If no matching file is found, a row is inserted. + + @type filename: string + @param filename: The filepath + @type session: SQLAlchemy + @param session: Optional SQL session object (a temporary one will be + generated if not supplied) + + @rtype: int + @return: the database id for the given path + """ + if session is None: + session = DBConn().session() + + try: + q = session.query(ContentFilepath).filter_by(filepath=filepath) + if q.count() < 1: + cf = ContentFilepath() + cf.filepath = filepath + session.add(cf) + return cf.cafilepath_id + else: + return q.one().cafilepath_id + + except: + traceback.print_exc() + raise + +__all__.append('get_or_set_contents_path_id') + +################################################################################ + class ContentAssociation(object): def __init__(self, *args, **kwargs): pass @@ -271,6 +373,58 @@ class ContentAssociation(object): __all__.append('ContentAssociation') +def insert_content_paths(binary_id, fullpaths, session=None): + """ + Make sure given path is associated with given binary id + + @type binary_id: int + @param binary_id: the id of the binary + @type fullpaths: list + @param fullpaths: the list of paths of the file being associated with the binary + @type session: SQLAlchemy session + @param session: Optional SQLAlchemy session. If this is passed, the caller + is responsible for ensuring a transaction has begun and committing the + results or rolling back based on the result code. If not passed, a commit + will be performed at the end of the function + + @return: True upon success + """ + + privatetrans = False + + if session is None: + session = DBConn().session() + privatetrans = True + + try: + for fullpath in fullpaths: + (path, file) = os.path.split(fullpath) + + # Get the necessary IDs ... + ca = ContentAssociation() + ca.binary_id = binary_id + ca.filename_id = get_or_set_contents_file_id(file) + ca.filepath_id = get_or_set_contents_path_id(path) + session.add(ca) + + # Only commit if we set up the session ourself + if privatetrans: + session.commit() + + return True + except: + traceback.print_exc() + + # Only rollback if we set up the session ourself + if privatetrans: + session.rollback() + + return False + +__all__.append('insert_content_paths') + +################################################################################ + class DSCFile(object): def __init__(self, *args, **kwargs): pass @@ -280,6 +434,8 @@ class DSCFile(object): __all__.append('DSCFile') +################################################################################ + class PoolFile(object): def __init__(self, *args, **kwargs): pass @@ -289,6 +445,35 @@ class PoolFile(object): __all__.append('PoolFile') +def get_poolfile_by_name(filename, location_id=None, session=None): + """ + Returns an array of PoolFile objects for the given filename and + (optionally) location_id + + @type filename: string + @param filename: the filename of the file to check against the DB + + @type location_id: int + @param location_id: the id of the location to look in (optional) + + @rtype: array + @return: array of PoolFile objects + """ + + if session is not None: + session = DBConn().session() + + q = session.query(PoolFile).filter_by(filename=filename) + + if location_id is not None: + q = q.join(Location).filter_by(location_id=location_id) + + return q.all() + +__all__.append('get_poolfile_by_name') + +################################################################################ + class Fingerprint(object): def __init__(self, *args, **kwargs): pass @@ -298,6 +483,8 @@ class Fingerprint(object): __all__.append('Fingerprint') +################################################################################ + class Keyring(object): def __init__(self, *args, **kwargs): pass @@ -307,6 +494,8 @@ class Keyring(object): __all__.append('Keyring') +################################################################################ + class Location(object): def __init__(self, *args, **kwargs): pass @@ -316,6 +505,44 @@ class Location(object): __all__.append('Location') +def get_location(location, component=None, archive=None, session=None): + """ + Returns Location object for the given combination of location, component + and archive + + @type location: string + @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/} + + @type component: string + @param component: the component name (if None, no restriction applied) + + @type archive: string + @param archive_id: the archive name (if None, no restriction applied) + + @rtype: Location / None + @return: Either a Location object or None if one can't be found + """ + + if session is None: + session = DBConn().session() + + q = session.query(Location).filter_by(path=location) + + if archive is not None: + q = q.join(Archive).filter_by(archive_name=archive) + + if component is not None: + q = q.join(Component).filter_by(component_name=component) + + if q.count() < 1: + return None + else: + return q.one() + +__all__.append('get_location') + +################################################################################ + class Maintainer(object): def __init__(self, *args, **kwargs): pass @@ -323,8 +550,16 @@ class Maintainer(object): def __repr__(self): return '''''' % (self.name, self.maintainer_id) + def get_split_maintainer(self): + if not hasattr(self, 'name') or self.name is None: + return ('', '', '', '') + + return fix_maintainer(self.name.strip()) + __all__.append('Maintainer') +################################################################################ + class Override(object): def __init__(self, *args, **kwargs): pass @@ -334,6 +569,8 @@ class Override(object): __all__.append('Override') +################################################################################ + class OverrideType(object): def __init__(self, *args, **kwargs): pass @@ -367,6 +604,8 @@ def get_override_type(override_type, session=None): __all__.append('get_override_type') +################################################################################ + class PendingContentAssociation(object): def __init__(self, *args, **kwargs): pass @@ -376,6 +615,74 @@ class PendingContentAssociation(object): __all__.append('PendingContentAssociation') +def insert_pending_content_paths(package, fullpaths, session=None): + """ + Make sure given paths are temporarily associated with given + package + + @type package: dict + @param package: the package to associate with should have been read in from the binary control file + @type fullpaths: list + @param fullpaths: the list of paths of the file being associated with the binary + @type session: SQLAlchemy session + @param session: Optional SQLAlchemy session. If this is passed, the caller + is responsible for ensuring a transaction has begun and committing the + results or rolling back based on the result code. If not passed, a commit + will be performed at the end of the function + + @return: True upon success, False if there is a problem + """ + + privatetrans = False + + if session is None: + session = DBConn().session() + privatetrans = True + + try: + arch = get_architecture(package['Architecture'], session) + arch_id = arch.arch_id + + # Remove any already existing recorded files for this package + q = session.query(PendingContentAssociation) + q = q.filter_by(package=package['Package']) + q = q.filter_by(version=package['Version']) + q = q.filter_by(architecture=arch_id) + q.delete() + + # Insert paths + for fullpath in fullpaths: + (path, file) = os.path.split(fullpath) + + if path.startswith( "./" ): + path = path[2:] + + pca = PendingContentAssociation() + pca.package = package['Package'] + pca.version = package['Version'] + pca.filename_id = get_or_set_contents_file_id(file, session) + pca.filepath_id = get_or_set_contents_path_id(path, session) + pca.architecture = arch_id + session.add(pca) + + # Only commit if we set up the session ourself + if privatetrans: + session.commit() + + return True + except: + traceback.print_exc() + + # Only rollback if we set up the session ourself + if privatetrans: + session.rollback() + + return False + +__all__.append('insert_pending_content_paths') + +################################################################################ + class Priority(object): def __init__(self, *args, **kwargs): pass @@ -409,6 +716,8 @@ def get_priority(priority, session=None): __all__.append('get_priority') +################################################################################ + class Queue(object): def __init__(self, *args, **kwargs): pass @@ -418,6 +727,8 @@ class Queue(object): __all__.append('Queue') +################################################################################ + class QueueBuild(object): def __init__(self, *args, **kwargs): pass @@ -427,6 +738,8 @@ class QueueBuild(object): __all__.append('QueueBuild') +################################################################################ + class Section(object): def __init__(self, *args, **kwargs): pass @@ -460,38 +773,49 @@ def get_section(section, session=None): __all__.append('get_section') -class Source(object): +################################################################################ + +class DBSource(object): def __init__(self, *args, **kwargs): pass def __repr__(self): - return '' % (self.source, self.version) + return '' % (self.source, self.version) -__all__.append('Source') +__all__.append('DBSource') -def get_sources_from_name(source, session=None): +def get_sources_from_name(source, dm_upload_allowed=None, session=None): """ - Returns list of Source objects for given C{source} name + Returns list of DBSource objects for given C{source} name @type source: str - @param source: Source package name to search for + @param source: DBSource package name to search for + + @type dm_upload_allowed: bool + @param dm_upload_allowed: If None, no effect. If True or False, only + return packages with that dm_upload_allowed setting @type session: Session @param session: Optional SQL session object (a temporary one will be generated if not supplied) @rtype: list - @return: list of Source objects for the given name (may be empty) + @return: list of DBSource objects for the given name (may be empty) """ if session is None: session = DBConn().session() - return session.query(Source).filter_by(source=source).all() + + q = session.query(DBSource).filter_by(source=source) + if dm_upload_allowed is not None: + q = q.filter_by(dm_upload_allowed=dm_upload_allowed) + + return q.all() __all__.append('get_sources_from_name') def get_source_in_suite(source, suite, session=None): """ - Returns list of Source objects for a combination of C{source} and C{suite}. + Returns list of DBSource objects for a combination of C{source} and C{suite}. - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc} - B{suite} - a suite name, eg. I{unstable} @@ -518,6 +842,8 @@ def get_source_in_suite(source, suite, session=None): __all__.append('get_source_in_suite') +################################################################################ + class SrcAssociation(object): def __init__(self, *args, **kwargs): pass @@ -527,6 +853,8 @@ class SrcAssociation(object): __all__.append('SrcAssociation') +################################################################################ + class SrcUploader(object): def __init__(self, *args, **kwargs): pass @@ -536,6 +864,8 @@ class SrcUploader(object): __all__.append('SrcUploader') +################################################################################ + class Suite(object): def __init__(self, *args, **kwargs): pass @@ -600,6 +930,8 @@ def get_suite(suite, session=None): __all__.append('get_suite') +################################################################################ + class SuiteArchitecture(object): def __init__(self, *args, **kwargs): pass @@ -609,13 +941,21 @@ class SuiteArchitecture(object): __all__.append('SuiteArchitecture') -def get_suite_architectures(suite, session=None): +def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None): """ Returns list of Architecture objects for given C{suite} name @type source: str @param source: Suite name to search for + @type skipsrc: boolean + @param skipsrc: Whether to skip returning the 'source' architecture entry + (Default False) + + @type skipall: boolean + @param skipall: Whether to skip returning the 'all' architecture entry + (Default False) + @type session: Session @param session: Optional SQL session object (a temporary one will be generated if not supplied) @@ -629,11 +969,18 @@ def get_suite_architectures(suite, session=None): q = session.query(Architecture) q = q.join(SuiteArchitecture) - q = q.join(Suite).filter_by(suite_name=suite).order_by('arch_string') + q = q.join(Suite).filter_by(suite_name=suite) + if skipsrc: + q = q.filter(Architecture.arch_string != 'source') + if skipall: + q = q.filter(Architecture.arch_string != 'all') + q = q.order_by('arch_string') return q.all() __all__.append('get_suite_architectures') +################################################################################ + class Uid(object): def __init__(self, *args, **kwargs): pass @@ -643,6 +990,20 @@ class Uid(object): __all__.append('Uid') +def get_uid_from_fingerprint(fpr, session=None): + if session is None: + session = DBConn().session() + + q = session.query(Uid) + q = q.join(Fingerprint).filter_by(fingerprint=fpr) + + if q.count() != 1: + return None + else: + return q.one() + +__all__.append('get_uid_from_fingerprint') + ################################################################################ class DBConn(Singleton): @@ -701,16 +1062,16 @@ class DBConn(Singleton): suite_id = self.tbl_bin_associations.c.suite, suite = relation(Suite), binary_id = self.tbl_bin_associations.c.bin, - binary = relation(Binary))) + binary = relation(DBBinary))) - mapper(Binary, self.tbl_binaries, + mapper(DBBinary, self.tbl_binaries, properties = dict(binary_id = self.tbl_binaries.c.id, package = self.tbl_binaries.c.package, version = self.tbl_binaries.c.version, maintainer_id = self.tbl_binaries.c.maintainer, maintainer = relation(Maintainer), source_id = self.tbl_binaries.c.source, - source = relation(Source), + source = relation(DBSource), arch_id = self.tbl_binaries.c.architecture, architecture = relation(Architecture), poolfile_id = self.tbl_binaries.c.file, @@ -736,7 +1097,7 @@ class DBConn(Singleton): filepath_id = self.tbl_content_associations.c.filepath, filepath = relation(ContentFilepath), binary_id = self.tbl_content_associations.c.binary_pkg, - binary = relation(Binary))) + binary = relation(DBBinary))) mapper(ContentFilename, self.tbl_content_file_names, @@ -750,7 +1111,7 @@ class DBConn(Singleton): mapper(DSCFile, self.tbl_dsc_files, properties = dict(dscfile_id = self.tbl_dsc_files.c.id, source_id = self.tbl_dsc_files.c.source, - source = relation(Source), + source = relation(DBSource), poolfile_id = self.tbl_dsc_files.c.file, poolfile = relation(PoolFile))) @@ -819,7 +1180,7 @@ class DBConn(Singleton): mapper(Section, self.tbl_section, properties = dict(section_id = self.tbl_section.c.id)) - mapper(Source, self.tbl_source, + mapper(DBSource, self.tbl_source, properties = dict(source_id = self.tbl_source.c.id, version = self.tbl_source.c.version, maintainer_id = self.tbl_source.c.maintainer, @@ -842,12 +1203,12 @@ class DBConn(Singleton): suite_id = self.tbl_src_associations.c.suite, suite = relation(Suite), source_id = self.tbl_src_associations.c.source, - source = relation(Source))) + source = relation(DBSource))) mapper(SrcUploader, self.tbl_src_uploaders, properties = dict(uploader_id = self.tbl_src_uploaders.c.id, source_id = self.tbl_src_uploaders.c.source, - source = relation(Source, + source = relation(DBSource, primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)), maintainer_id = self.tbl_src_uploaders.c.maintainer, maintainer = relation(Maintainer, @@ -858,12 +1219,13 @@ class DBConn(Singleton): mapper(SuiteArchitecture, self.tbl_suite_architectures, properties = dict(suite_id = self.tbl_suite_architectures.c.suite, - suite = relation(Suite), + suite = relation(Suite, backref='suitearchitectures'), arch_id = self.tbl_suite_architectures.c.architecture, architecture = relation(Architecture))) mapper(Uid, self.tbl_uid, - properties = dict(uid_id = self.tbl_uid.c.id)) + properties = dict(uid_id = self.tbl_uid.c.id, + fingerprint = relation(Fingerprint))) ## Connection functions def __createconn(self): @@ -894,250 +1256,5 @@ class DBConn(Singleton): def session(self): return self.db_smaker() - def prepare(self,name,statement): - if not self.prepared_statements.has_key(name): - pgc.execute(statement) - self.prepared_statements[name] = statement - - - def get_location_id(self, location, component, archive): - """ - Returns database id for the location behind the given combination of - - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/} - - B{component} - the id of the component as returned by L{get_component_id} - - B{archive} - the id of the archive as returned by L{get_archive_id} - Results are kept in a cache during runtime to minimize database queries. - - @type location: string - @param location: the path of the location - - @type component: int - @param component: the id of the component - - @type archive: int - @param archive: the id of the archive - - @rtype: int - @return: the database id for the location - - """ - - archive_id = self.get_archive_id(archive) - - if not archive_id: - return None - - res = None - - if component: - component_id = self.get_component_id(component) - if component_id: - res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND component=%(component)s AND archive=%(archive)s", - {'location': location, - 'archive': int(archive_id), - 'component': component_id}, cachename='location') - else: - res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND archive=%(archive)d", - {'location': location, 'archive': archive_id, 'component': ''}, cachename='location') - - return res - - - -def get_files_id (self, filename, size, md5sum, location_id): - """ - Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an - existing copy. - - The database is queried using the C{filename} and C{location_id}. If a file does exist - at that location, the existing size and md5sum are checked against the provided - parameters. A size or checksum mismatch returns -2. If more than one entry is - found within the database, a -1 is returned, no result returns None, otherwise - the file id. - - @type filename: string - @param filename: the filename of the file to check against the DB - - @type size: int - @param size: the size of the file to check against the DB - - @type md5sum: string - @param md5sum: the md5sum of the file to check against the DB - - @type location_id: int - @param location_id: the id of the location as returned by L{get_location_id} - - @rtype: int / None - @return: Various return values are possible: - - -2: size/checksum error - - -1: more than one file found in database - - None: no file found in database - - int: file id - - """ - values = {'filename' : filename, - 'location' : location_id} - - if not res: - query = """SELECT id, size, md5sum - FROM files - WHERE filename = %(filename)s AND location = %(location)s""" - - cursor = self.db_con.cursor() - cursor.execute( query, values ) - - if cursor.rowcount == 0: - res = None - - elif cursor.rowcount != 1: - res = -1 - - else: - row = cursor.fetchone() - - if row[1] != int(size) or row[2] != md5sum: - res = -2 - - else: - res = row[0] - - return res - - -def get_or_set_contents_file_id(self, filename): - """ - Returns database id for given filename. - - If no matching file is found, a row is inserted. - - @type filename: string - @param filename: The filename - - @rtype: int - @return: the database id for the given component - """ - try: - values={'value': filename} - query = "SELECT id FROM content_file_names WHERE file = %(value)s" - if not id: - c = self.db_con.cursor() - c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id", - values ) - - id = c.fetchone()[0] - - return id - except: - traceback.print_exc() - raise - -def get_or_set_contents_path_id(self, path): - """ - Returns database id for given path. - - If no matching file is found, a row is inserted. - - @type path: string - @param path: The filename - - @rtype: int - @return: the database id for the given component - """ - try: - values={'value': path} - query = "SELECT id FROM content_file_paths WHERE path = %(value)s" - if not id: - c = self.db_con.cursor() - c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id", - values ) - - id = c.fetchone()[0] - - return id - except: - traceback.print_exc() - raise - - -def insert_content_paths(self, bin_id, fullpaths): - """ - Make sure given path is associated with given binary id - - @type bin_id: int - @param bin_id: the id of the binary - @type fullpaths: list - @param fullpaths: the list of paths of the file being associated with the binary - - @return: True upon success - """ +__all__.append('DBConn') - c = self.db_con.cursor() - - c.execute("BEGIN WORK") - try: - - for fullpath in fullpaths: - (path, file) = os.path.split(fullpath) - - # Get the necessary IDs ... - file_id = self.get_or_set_contents_file_id(file) - path_id = self.get_or_set_contents_path_id(path) - - c.execute("""INSERT INTO content_associations - (binary_pkg, filepath, filename) - VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) ) - - c.execute("COMMIT") - return True - except: - traceback.print_exc() - c.execute("ROLLBACK") - return False - -def insert_pending_content_paths(self, package, fullpaths): - """ - Make sure given paths are temporarily associated with given - package - - @type package: dict - @param package: the package to associate with should have been read in from the binary control file - @type fullpaths: list - @param fullpaths: the list of paths of the file being associated with the binary - - @return: True upon success - """ - - c = self.db_con.cursor() - - c.execute("BEGIN WORK") - try: - arch_id = self.get_architecture_id(package['Architecture']) - - # Remove any already existing recorded files for this package - c.execute("""DELETE FROM pending_content_associations - WHERE package=%(Package)s - AND version=%(Version)s - AND architecture=%(ArchID)s""", {'Package': package['Package'], - 'Version': package['Version'], - 'ArchID': arch_id}) - - for fullpath in fullpaths: - (path, file) = os.path.split(fullpath) - - if path.startswith( "./" ): - path = path[2:] - # Get the necessary IDs ... - file_id = self.get_or_set_contents_file_id(file) - path_id = self.get_or_set_contents_path_id(path) - - c.execute("""INSERT INTO pending_content_associations - (package, version, architecture, filepath, filename) - VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')""" - % (arch_id, path_id, file_id), package ) - - c.execute("COMMIT") - return True - except: - traceback.print_exc() - c.execute("ROLLBACK") - return False