]> git.decadent.org.uk Git - dak.git/blobdiff - daklib/DBConn.py
Content generation seems to be working now
[dak.git] / daklib / DBConn.py
index 05bf32d1a922a0e4f3584b21b8ee3f4836252387..75afb5a72363039e47d981fe5485b920a609316b 100644 (file)
@@ -1,7 +1,14 @@
 #!/usr/bin/env python
 
-# DB access class
-# Copyright (C) 2008  Mark Hymers <mhy@debian.org>
+""" DB access class
+
+@contact: Debian FTPMaster <ftpmaster@debian.org>
+@copyright: 2000, 2001, 2002, 2003, 2004, 2006  James Troup <james@nocrew.org>
+@copyright: 2008-2009  Mark Hymers <mhy@debian.org>
+@copyright: 2009  Joerg Jaspert <joerg@debian.org>
+@copyright: 2009  Mike O'Connor <stew@debian.org>
+@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" )