X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=daklib%2Fdbconn.py;h=bb7473dc08b85d4f64c0268e1faeb094c3aca786;hb=317c2e8d618daddd49531c18a8367b533c2dd75c;hp=5bd1d76595717043fb744d1715d33612e8d9c442;hpb=603d8ace7d4f942c999c29556bde38ec2516b9a8;p=dak.git diff --git a/daklib/dbconn.py b/daklib/dbconn.py index 5bd1d765..bb7473dc 100755 --- a/daklib/dbconn.py +++ b/daklib/dbconn.py @@ -35,6 +35,7 @@ import os import psycopg2 +import psycopg2.extras import traceback from singleton import Singleton @@ -113,6 +114,14 @@ class DBConn(Singleton): 'suite_version': Cache(lambda x: '%s_%s' % (x['source'], x['suite'])), } + self.prepared_statements = {} + + def prepare(self,name,statement): + if not self.prepared_statements.has_key(name): + c = self.cursor() + c.execute(statement) + self.prepared_statements[name] = statement + def clear_caches(self): self.__init_caches() @@ -124,6 +133,17 @@ class DBConn(Singleton): return self.db_con.commit() ## Get functions + def __get_single_row(self, query, values): + c = self.db_con.cursor(cursor_factory=psycopg2.extras.DictCursor) + c.execute(query, values) + + if c.rowcount < 1: + return None + + res = c.fetchone() + + return res + def __get_single_id(self, query, values, cachename=None): # This is a bit of a hack but it's an internal function only if cachename is not None: @@ -160,7 +180,7 @@ class DBConn(Singleton): @return: the database id for the given suite """ - return self.__get_id('id', 'suite', 'suite_name', suite) + return int(self.__get_id('id', 'suite', 'suite_name', suite)) def get_section_id(self, section): """ @@ -202,7 +222,7 @@ class DBConn(Singleton): @return: the database id for the given override type """ - return self.__get_id('id', 'override_type', 'override_type', override_type) + return self.__get_id('id', 'override_type', 'type', override_type) def get_architecture_id(self, architecture): """ @@ -308,6 +328,15 @@ class DBConn(Singleton): return self.__get_single_id("SELECT id FROM source s WHERE s.source=%(source)s AND s.version=%(version)s", {'source': source, 'version': version}, cachename='source') + def get_suite(self, suite): + if isinstance(suite, str): + suite_id = self.get_suite_id(suite.lower()) + elif type(suite) == int: + suite_id = suite + + return self.__get_single_row("SELECT * FROM suite WHERE id = %(id)s", + {'id': suite_id}) + def get_suite_version(self, source, suite): """ Returns database id for a combination of C{source} and C{suite}. @@ -390,11 +419,11 @@ class DBConn(Singleton): else: row = cursor.fetchone() - if row[1] != size or row[2] != md5sum: + if row[1] != int(size) or row[2] != md5sum: res = -2 else: - self.caches[cachename].SetValue(values, row[0]) + self.caches['files'].SetValue(values, row[0]) res = row[0] return res @@ -413,18 +442,22 @@ class DBConn(Singleton): @rtype: int @return: the database id for the given component """ - values={'value': filename} - query = "SELECT id FROM content_file_names WHERE file = %(value)s" - id = self.__get_single_id(query, values, cachename='content_file_names') - 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] - self.caches['content_file_names'].SetValue(values, id) - - return id + try: + values={'value': filename} + query = "SELECT id FROM content_file_names WHERE file = %(value)s" + id = self.__get_single_id(query, values, cachename='content_file_names') + 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] + self.caches['content_file_names'].SetValue(values, id) + + return id + except: + traceback.print_exc() + raise def get_or_set_contents_path_id(self, path): """ @@ -439,20 +472,50 @@ class DBConn(Singleton): @rtype: int @return: the database id for the given component """ - values={'value': path} - query = "SELECT id FROM content_file_paths WHERE path = %(value)s" - id = self.__get_single_id(query, values, cachename='content_path_names') - if not id: - c = self.db_con.cursor() - c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id", - values ) + try: + values={'value': path} + query = "SELECT id FROM content_file_paths WHERE path = %(value)s" + id = self.__get_single_id(query, values, cachename='content_path_names') + 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] + self.caches['content_path_names'].SetValue(values, id) + + return id + except: + traceback.print_exc() + raise + + def get_suite_architectures(self, suite): + """ + Returns list of architectures for C{suite}. + + @type suite: string, int + @param suite: the suite name or the suite_id + + @rtype: list + @return: the list of architectures for I{suite} + """ - id = c.fetchone()[0] - self.caches['content_path_names'].SetValue(values, id) + suite_id = None + if type(suite) == str: + suite_id = self.get_suite_id(suite) + elif type(suite) == int: + suite_id = suite + else: + return None + + c = self.db_con.cursor() + c.execute( """SELECT a.arch_string FROM suite_architectures sa + JOIN architecture a ON (a.id = sa.architecture) + WHERE suite='%s'""" % suite_id ) - return id + return map(lambda x: x[0], c.fetchall()) - def insert_content_paths(self, package, fullpaths): + def insert_content_paths(self, bin_id, fullpaths): """ Make sure given path is associated with given binary id @@ -469,25 +532,96 @@ class DBConn(Singleton): c.execute("BEGIN WORK") try: - # Remove any already existing recorded files for this package - c.execute("""DELETE FROM temp_content_associations + 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""", package ) + 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 temp_content_associations - (package, version, filepath, filename) - VALUES (%%(Package)s, %%(Version)s, '%d', '%d')""" % (path_id, file_id), - package ) + 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 + +################################################################################ + +class Suite(object): + # This should be kept in sync with the suites table; + # we should probably just do introspection on the table + # (or maybe use an ORM) + _fieldnames = ['announce', 'changelogbase', 'codename', 'commentsdir', + 'copychanges', 'copydotdak', 'description', 'id', + 'label', 'notautomatic', 'origin', 'overridecodename', + 'overridesuite', 'policy_engine', 'priority', 'suite_name', + 'untouchable', 'validtime', 'version'] + + def __init_fields(self): + for k in self._fieldnames: + setattr(self, k, None) + + def __init__(self, suite): + self.__init_fields() + if suite is not None: + db_conn = DBConn() + suite_data = db_conn.get_suite(suite) + if suite_data is not None: + for k in suite_data.keys(): + setattr(self, k, suite_data[k]) + + +