X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;ds=sidebyside;f=daklib%2FDBConn.py;h=75afb5a72363039e47d981fe5485b920a609316b;hb=9af1235da5224336ede312fb5c3e0fe791bc19f0;hp=05bf32d1a922a0e4f3584b21b8ee3f4836252387;hpb=81d0c91b0d085b66aa40a9e147698f618b825d62;p=dak.git diff --git a/daklib/DBConn.py b/daklib/DBConn.py index 05bf32d1..75afb5a7 100644 --- a/daklib/DBConn.py +++ b/daklib/DBConn.py @@ -1,7 +1,14 @@ #!/usr/bin/env python -# DB access class -# Copyright (C) 2008 Mark Hymers +""" DB access class + +@contact: Debian FTPMaster +@copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup +@copyright: 2008-2009 Mark Hymers +@copyright: 2009 Joerg Jaspert +@copyright: 2009 Mike O'Connor +@license: GNU General Public License version 2 or later +""" # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,8 +33,8 @@ ################################################################################ +import os import psycopg2 -from psycopg2.extras import DictCursor from Singleton import Singleton from Config import Config @@ -53,8 +60,7 @@ class Cache(object): class DBConn(Singleton): """ - A DBConn object is a singleton containing - information about the connection to the SQL Database + database module init. """ def __init__(self, *args, **kwargs): super(DBConn, self).__init__(*args, **kwargs) @@ -65,7 +71,13 @@ class DBConn(Singleton): ## Connection functions def __createconn(self): - connstr = Config().GetDBConnString() + cnf = Config() + connstr = "dbname=%s" % cnf["DB::Name"] + if cnf["DB::Host"]: + connstr += " host=%s" % cnf["DB::Host"] + if cnf["DB::Port"] and cnf["DB::Port"] != "-1": + connstr += " port=%s" % cnf["DB::Port"] + self.db_con = psycopg2.connect(connstr) def reconnect(self): @@ -79,13 +91,15 @@ class DBConn(Singleton): ## Cache functions def __init_caches(self): - self.caches = {'suite': Cache(), + self.caches = {'suite': Cache(), 'section': Cache(), 'priority': Cache(), 'override_type': Cache(), 'architecture': Cache(), 'archive': Cache(), 'component': Cache(), + 'content_path_names': Cache(), + 'content_file_names': Cache(), 'location': Cache(lambda x: '%s_%s_%s' % (x['location'], x['component'], x['location'])), 'maintainer': {}, # TODO 'keyring': {}, # TODO @@ -126,35 +140,133 @@ class DBConn(Singleton): if cachename is not None: self.caches[cachename].SetValue(values, res) - + return res - + def __get_id(self, retfield, table, qfield, value): query = "SELECT %s FROM %s WHERE %s = %%(value)s" % (retfield, table, qfield) return self.__get_single_id(query, {'value': value}, cachename=table) def get_suite_id(self, suite): + """ + Returns database id for given C{suite}. + Results are kept in a cache during runtime to minimize database queries. + + @type suite: string + @param suite: The name of the suite + + @rtype: int + @return: the database id for the given suite + + """ return self.__get_id('id', 'suite', 'suite_name', suite) def get_section_id(self, section): + """ + Returns database id for given C{section}. + Results are kept in a cache during runtime to minimize database queries. + + @type section: string + @param section: The name of the section + + @rtype: int + @return: the database id for the given section + + """ return self.__get_id('id', 'section', 'section', section) def get_priority_id(self, priority): + """ + Returns database id for given C{priority}. + Results are kept in a cache during runtime to minimize database queries. + + @type priority: string + @param priority: The name of the priority + + @rtype: int + @return: the database id for the given priority + + """ return self.__get_id('id', 'priority', 'priority', priority) def get_override_type_id(self, override_type): + """ + Returns database id for given override C{type}. + Results are kept in a cache during runtime to minimize database queries. + + @type type: string + @param type: The name of the override type + + @rtype: int + @return: the database id for the given override type + + """ return self.__get_id('id', 'override_type', 'override_type', override_type) def get_architecture_id(self, architecture): + """ + Returns database id for given C{architecture}. + Results are kept in a cache during runtime to minimize database queries. + + @type architecture: string + @param architecture: The name of the override type + + @rtype: int + @return: the database id for the given architecture + + """ return self.__get_id('id', 'architecture', 'arch_string', architecture) def get_archive_id(self, archive): + """ + returns database id for given c{archive}. + results are kept in a cache during runtime to minimize database queries. + + @type archive: string + @param archive: the name of the override type + + @rtype: int + @return: the database id for the given archive + + """ return self.__get_id('id', 'archive', 'lower(name)', archive) def get_component_id(self, component): + """ + Returns database id for given C{component}. + Results are kept in a cache during runtime to minimize database queries. + + @type component: string + @param component: The name of the override type + + @rtype: int + @return: the database id for the given component + + """ return self.__get_id('id', 'component', 'lower(name)', component) 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: @@ -174,10 +286,44 @@ class DBConn(Singleton): return res def get_source_id(self, source, version): + """ + Returns database id for the combination of C{source} and C{version} + - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc} + - B{version} + Results are kept in a cache during runtime to minimize database queries. + + @type source: string + @param source: source package name + + @type version: string + @param version: the source version + + @rtype: int + @return: the database id for the source + + """ 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_version(self, source, suite): + """ + Returns database id 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} + + Results are kept in a cache during runtime to minimize database queries. + + @type source: string + @param source: source package name + + @type suite: string + @param suite: the suite name + + @rtype: string + @return: the version for I{source} in I{suite} + + """ return self.__get_single_id(""" SELECT s.version FROM source s, suite su, src_associations sa WHERE sa.source=s.id @@ -185,3 +331,84 @@ class DBConn(Singleton): AND su.suite_name=%(suite)s AND s.source=%(source)""", {'suite': suite, 'source': source}, cachename='suite_version') + + def get_or_set_contents_file_id(self, filename): + """ + Returns database id for given filename. + + Results are kept in a cache during runtime to minimize database queries. + 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 + """ + 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 + + def get_or_set_contents_path_id(self, path): + """ + Returns database id for given path. + + Results are kept in a cache during runtime to minimize database queries. + 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 + """ + 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 + + 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 fullpath: string + @param fullpath: the path of the file being associated with the binary + """ + + c = self.db_con.cursor() + + for fullpath in fullpaths: + c.execute( "BEGIN WORK" ) + (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) + + # Determine if we're inserting a duplicate row + + c.execute("SELECT 1 FROM content_associations WHERE binary_pkg = '%d' AND filepath = '%d' AND filename = '%d'" % (int(bin_id), path_id, file_id)) + if not c.fetchone(): + # no, we are not, do the insert + + c.execute("INSERT INTO content_associations VALUES (DEFAULT, '%d', '%d', '%d')" % (bin_id, path_id, file_id)) + c.execute( "COMMIT" )