X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=daklib%2Fdbconn.py;h=d2f4b594d00b60c320cb57045679785216671ef9;hb=eac8189d964d4d5f34a13e692caab0e732045a15;hp=f6b35d949d3b41b61b3adc4235b74b8d87ab0a30;hpb=6b008273d7253d37a5a87c52a5a36dbb1f45e3e4;p=dak.git diff --git a/daklib/dbconn.py b/daklib/dbconn.py index f6b35d94..d2f4b594 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,64 @@ __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_from_name_suite(package, suitename, session=None): + ### For dak examine-package + ### XXX: Doesn't use object API yet + if session is None: + session = DBConn().session() + + sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name + FROM binaries b, files fi, location l, component c, bin_associations ba, suite su + WHERE b.package=:package + AND b.file = fi.id + AND fi.location = l.id + AND l.component = c.id + AND ba.bin=b.id + AND ba.suite = su.id + AND su.suite_name=:suitename + ORDER BY b.version DESC""" + + return session.execute(sql, {'package': package, 'suitename': suitename}) + +__all__.append('get_binary_from_name_suite') + +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 +282,8 @@ def get_component(component, session=None): __all__.append('get_component') +################################################################################ + class DBConfig(object): def __init__(self, *args, **kwargs): pass @@ -244,6 +293,8 @@ class DBConfig(object): __all__.append('DBConfig') +################################################################################ + class ContentFilename(object): def __init__(self, *args, **kwargs): pass @@ -263,13 +314,16 @@ def get_or_set_contents_file_id(filename, session=None): @param filename: The filename @type session: SQLAlchemy @param session: Optional SQL session object (a temporary one will be - generated if not supplied) + generated if not supplied). If not passed, a commit will be performed at + the end of the function, otherwise the caller is responsible for commiting. @rtype: int @return: the database id for the given component """ + privatetrans = False if session is None: session = DBConn().session() + privatetrans = True try: q = session.query(ContentFilename).filter_by(filename=filename) @@ -277,6 +331,8 @@ def get_or_set_contents_file_id(filename, session=None): cf = ContentFilename() cf.filename = filename session.add(cf) + if privatetrans: + session.commit() return cf.cafilename_id else: return q.one().cafilename_id @@ -287,6 +343,61 @@ def get_or_set_contents_file_id(filename, session=None): __all__.append('get_or_set_contents_file_id') +def get_contents(suite, overridetype, section=None, session=None): + """ + Returns contents for a suite / overridetype combination, limiting + to a section if not None. + + @type suite: Suite + @param suite: Suite object + + @type overridetype: OverrideType + @param overridetype: OverrideType object + + @type section: Section + @param section: Optional section object to limit results to + + @type session: SQLAlchemy + @param session: Optional SQL session object (a temporary one will be + generated if not supplied) + + @rtype: ResultsProxy + @return: ResultsProxy object set up to return tuples of (filename, section, + package, arch_id) + """ + + if session is None: + session = DBConn().session() + + # find me all of the contents for a given suite + contents_q = """SELECT (p.path||'/'||n.file) AS fn, + s.section, + b.package, + b.architecture + FROM content_associations c join content_file_paths p ON (c.filepath=p.id) + JOIN content_file_names n ON (c.filename=n.id) + JOIN binaries b ON (b.id=c.binary_pkg) + JOIN override o ON (o.package=b.package) + JOIN section s ON (s.id=o.section) + WHERE o.suite = :suiteid AND o.type = :overridetypeid + AND b.type=:overridetypename""" + + vals = {'suiteid': suite.suite_id, + 'overridetypeid': overridetype.overridetype_id, + 'overridetypename': overridetype.overridetype} + + if section is not None: + contents_q += " AND s.id = :sectionid" + vals['sectionid'] = section.section_id + + contents_q += " ORDER BY fn" + + return session.execute(contents_q, vals) + +__all__.append('get_contents') + +################################################################################ + class ContentFilepath(object): def __init__(self, *args, **kwargs): pass @@ -306,13 +417,16 @@ def get_or_set_contents_path_id(filepath, session): @param filename: The filepath @type session: SQLAlchemy @param session: Optional SQL session object (a temporary one will be - generated if not supplied) + generated if not supplied). If not passed, a commit will be performed at + the end of the function, otherwise the caller is responsible for commiting. @rtype: int @return: the database id for the given path """ + privatetrans = False if session is None: session = DBConn().session() + privatetrans = True try: q = session.query(ContentFilepath).filter_by(filepath=filepath) @@ -320,6 +434,8 @@ def get_or_set_contents_path_id(filepath, session): cf = ContentFilepath() cf.filepath = filepath session.add(cf) + if privatetrans: + session.commit() return cf.cafilepath_id else: return q.one().cafilepath_id @@ -330,6 +446,8 @@ def get_or_set_contents_path_id(filepath, session): __all__.append('get_or_set_contents_path_id') +################################################################################ + class ContentAssociation(object): def __init__(self, *args, **kwargs): pass @@ -351,7 +469,8 @@ def insert_content_paths(binary_id, fullpaths, session=None): @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 + will be performed at the end of the function, otherwise the caller is + responsible for commiting. @return: True upon success """ @@ -389,6 +508,8 @@ def insert_content_paths(binary_id, fullpaths, session=None): __all__.append('insert_content_paths') +################################################################################ + class DSCFile(object): def __init__(self, *args, **kwargs): pass @@ -398,6 +519,8 @@ class DSCFile(object): __all__.append('DSCFile') +################################################################################ + class PoolFile(object): def __init__(self, *args, **kwargs): pass @@ -434,6 +557,8 @@ def get_poolfile_by_name(filename, location_id=None, session=None): __all__.append('get_poolfile_by_name') +################################################################################ + class Fingerprint(object): def __init__(self, *args, **kwargs): pass @@ -443,6 +568,8 @@ class Fingerprint(object): __all__.append('Fingerprint') +################################################################################ + class Keyring(object): def __init__(self, *args, **kwargs): pass @@ -452,6 +579,8 @@ class Keyring(object): __all__.append('Keyring') +################################################################################ + class Location(object): def __init__(self, *args, **kwargs): pass @@ -497,6 +626,8 @@ def get_location(location, component=None, archive=None, session=None): __all__.append('get_location') +################################################################################ + class Maintainer(object): def __init__(self, *args, **kwargs): pass @@ -504,8 +635,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 @@ -515,6 +654,8 @@ class Override(object): __all__.append('Override') +################################################################################ + class OverrideType(object): def __init__(self, *args, **kwargs): pass @@ -541,13 +682,15 @@ def get_override_type(override_type, session=None): """ if session is None: session = DBConn().session() - q = session.query(Priority).filter_by(priority=priority) + q = session.query(OverrideType).filter_by(overridetype=override_type) if q.count() == 0: return None return q.one() __all__.append('get_override_type') +################################################################################ + class PendingContentAssociation(object): def __init__(self, *args, **kwargs): pass @@ -623,6 +766,8 @@ def insert_pending_content_paths(package, fullpaths, session=None): __all__.append('insert_pending_content_paths') +################################################################################ + class Priority(object): def __init__(self, *args, **kwargs): pass @@ -656,6 +801,8 @@ def get_priority(priority, session=None): __all__.append('get_priority') +################################################################################ + class Queue(object): def __init__(self, *args, **kwargs): pass @@ -665,6 +812,8 @@ class Queue(object): __all__.append('Queue') +################################################################################ + class QueueBuild(object): def __init__(self, *args, **kwargs): pass @@ -674,6 +823,8 @@ class QueueBuild(object): __all__.append('QueueBuild') +################################################################################ + class Section(object): def __init__(self, *args, **kwargs): pass @@ -707,38 +858,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} @@ -765,6 +927,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 @@ -774,6 +938,8 @@ class SrcAssociation(object): __all__.append('SrcAssociation') +################################################################################ + class SrcUploader(object): def __init__(self, *args, **kwargs): pass @@ -783,6 +949,28 @@ class SrcUploader(object): __all__.append('SrcUploader') +################################################################################ + +SUITE_FIELDS = [ ('SuiteName', 'suite_name'), + ('SuiteID', 'suite_id'), + ('Version', 'version'), + ('Origin', 'origin'), + ('Label', 'label'), + ('Description', 'description'), + ('Untouchable', 'untouchable'), + ('Announce', 'announce'), + ('Codename', 'codename'), + ('OverrideCodename', 'overridecodename'), + ('ValidTime', 'validtime'), + ('Priority', 'priority'), + ('NotAutomatic', 'notautomatic'), + ('CopyChanges', 'copychanges'), + ('CopyDotDak', 'copydotdak'), + ('CommentsDir', 'commentsdir'), + ('OverrideSuite', 'overridesuite'), + ('ChangelogBase', 'changelogbase')] + + class Suite(object): def __init__(self, *args, **kwargs): pass @@ -790,6 +978,15 @@ class Suite(object): def __repr__(self): return '' % self.suite_name + def details(self): + ret = [] + for disp, field in SUITE_FIELDS: + val = getattr(self, field, None) + if val is not None: + ret.append("%s: %s" % (disp, val)) + + return "\n".join(ret) + __all__.append('Suite') def get_suite_architecture(suite, architecture, session=None): @@ -847,6 +1044,8 @@ def get_suite(suite, session=None): __all__.append('get_suite') +################################################################################ + class SuiteArchitecture(object): def __init__(self, *args, **kwargs): pass @@ -856,13 +1055,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) @@ -876,11 +1083,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 @@ -890,6 +1104,91 @@ class Uid(object): __all__.append('Uid') +def add_database_user(uidname, session=None): + """ + Adds a database user + + @type uidname: string + @param uidname: The uid of the user to add + + @type session: SQLAlchemy + @param session: Optional SQL session object (a temporary one will be + generated if not supplied). If not passed, a commit will be performed at + the end of the function, otherwise the caller is responsible for commiting. + + @rtype: Uid + @return: the uid object for the given uidname + """ + privatetrans = False + if session is None: + session = DBConn().session() + privatetrans = True + + try: + session.execute("CREATE USER :uid", {'uid': uidname}) + if privatetrans: + session.commit() + except: + traceback.print_exc() + raise + +__all__.append('add_database_user') + +def get_or_set_uid(uidname, session=None): + """ + Returns uid object for given uidname. + + If no matching uidname is found, a row is inserted. + + @type uidname: string + @param uidname: The uid to add + + @type session: SQLAlchemy + @param session: Optional SQL session object (a temporary one will be + generated if not supplied). If not passed, a commit will be performed at + the end of the function, otherwise the caller is responsible for commiting. + + @rtype: Uid + @return: the uid object for the given uidname + """ + privatetrans = False + if session is None: + session = DBConn().session() + privatetrans = True + + try: + q = session.query(Uid).filter_by(uid=uidname) + if q.count() < 1: + uid = Uid() + uid.uid = uidname + session.add(uid) + if privatetrans: + session.commit() + return uid + else: + return q.one() + + except: + traceback.print_exc() + raise + +__all__.append('get_or_set_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): @@ -948,16 +1247,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, @@ -983,7 +1282,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, @@ -997,7 +1296,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))) @@ -1066,7 +1365,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, @@ -1089,12 +1388,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, @@ -1105,12 +1404,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):