<commit_before>#
# Copyright (c) 2005 rPath, Inc.
#
# This program is distributed under the terms of the Common Public License,
# version 1.0. A copy of this license should have been distributed with this
# source file in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/cpl.php.
#
# This program is distributed in the hope that it will be useful, but
# without any waranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the Common Public License for
# full details.
#

import sys
import re

class DBStoreError(Exception):
    pass

class DBStoreCursorError(DBStoreError):
    def __init__(self, msg, **kwargs):
        DBStoreError.__init__(self, msg, **kwargs)
                              
# base Cursor class. All backend drivers are expected to provide this
# interface
class BaseCursor:
    def __init__(self, dbh=None):
        self.dbh = dbh
        self._cursor = self._getCursor()
        self.description = None
        
    # this will need to be provided by each separate driver
    def _getCursor(self):
        assert(self.dbh)
        return self.dbh.cursor()
        
    def execute(self, sql, *args, **kw):
        assert(len(sql) > 0)
        assert(self.dbh and self._cursor)
        self.description = None
        # force dbi compliance here
        if len(kw) == 0:
            if len(args) == 0:
                return self._cursor.execute(sql)
            if len(args) == 1:
                return self._cursor.execute(sql, args[0])
            return self._cursor.execute(sql, *args)
        if len(args) == 1 and isinstance(args[0], dict):
            kw.update(args[0])
        return self._cursor.execute(sql, kw)
        
    # return the column names of the current select
    def __rowDict(self, row):
        assert(self._cursor and self._cursor.description)       
        if row is None:
            return None
        if len(row) != len(self._cursor.description):
            raise DBStoreCursorError("Cursor description doew not match row data",
                                     row = row, desc = self._cursor.description)
        if not self.description:
            self.description = [ x[0] for x in self._cursor.description ]
        return dict(zip(self.description, row))
                                                            
    # (a,b)
    def fetchone(self):
        return self._cursor.fetchone()
    # [(a1,b1),(a2, b2)]
    def fetchall(self):
        return self._cursor.fetchall()
    def fetchmany(self, count=1):
        return self._cursor.fetchmany(count)
    
    # { name_a : a, name_b : b }
    def fetchone_dict(self):
        try:
            return self.__rowDict(self.fetchone())
        except:
            pass
        return None
    # [ {name_a:a1, name_b:b1}, {name_a:a2, name_b:b2} ]
    def fetchall_dict(self):
        try:
            return [ self.__rowDict(row) for row in self.fetchall() ]
        except:
            pass
        return None
    def fetchmany_dict(self, count=1):
        try:
            return [ self.__rowDict(row) for row in self.fetchmany(count) ]
        except:
            pass
        return None


# A cursor class for the drivers that do not support bind
# parameters. Instead of :name they use a Python-esque %(name)s
# syntax. This is quite fragile...
class BindlessCursor(BaseCursor):
    def __mungeSQL(self, sql):
        regex = re.compile(':(\w+)')
        # edit the input query
        keys = set()
        for key in regex.findall(sql):
            keys.add(key)
            sql = re.sub(":" + key, "%("+key+")s", sql)
        sql = re.sub("[(]\s?[?]", "( %s", sql)
        sql = re.sub("[=]\s?[?]", "= %s", sql)
        sql = re.sub("[,]\s?[?]", ", %s", sql)
        return (sql, keys)

    # we need to "fix" the sql code before calling out
    def execute(self, sql, *params, **kw):
        self.description = None
        sql, keys = self.__mungeSQL(sql)
        # figure out possible invocations
        if len(params) == 0:
            assert (sorted(kw.keys()) == sorted(keys))
        elif len(params) == 1:
            assert(isinstance(params, tuple))
            if isinstance(params[0], tuple):
                params = params[0]
            p = params[0]
            # if it is a dictionary, it must contain bind arguments
            if hasattr(p, 'keys'):
                kw.update(p)
            else: # special case - single positional argument
                assert(len(keys)==0 and len(kw)==0)
                return self._cursor.execute(sql, params)
        else: # many params, we don't mix in bind arguments
            assert(len(keys)==0 and len(kw)==0)
            return self._cursor.execute(sql, params)
        # we have a dict of bind arguments
        return self._cursor.execute(sql, kw)

# A class to handle database operations
class BaseDatabase:
    def __init__(self, db):
        assert(db)
        self.type = "base"
        self.database = db
        self.dbh = None
        # stderr needs to be around to print errors. hold a reference
        self.stderr = sys.stderr
        self.__transaction = None
        # need to figure out a statement generic enough for all kinds of backends
        self.alive_check = "select 1 where 1 = 1"
        self.cursorClass = BaseCursor
        
    # the string syntax for databse connection is user:password@host/database
    def _connectData(self, names = ["user", "password", "host", "database"]):
        assert(self.database)
        # regexes are k00l and I am 1337 h@x0r
        regex = re.compile(
            "^(((?P<%s>[^:]+)(:(?P<%s>[^@]+))?@)?(?P<%s>[^/]+)/)?(?P<%s>.+)$" % tuple(names)
            )
        m = regex.match(self.database)
        assert(m)
        return m.groupdict()
        
    def connect(self):
        assert(self.database)        
        raise RuntimeError("This connect function has to be provided by the database driver")
    
    def close(self):
        if self.dbh:
            self.dbh.close()
        self.dbh = self.database = None

    def alive(self):
        assert(self.dbh)
        try:
            c = self.cursor()
            c.execute(self.alive_check)
        except:
            # database connection lost
            return 0
        else:
            del c
        return 1

    def cursor(self):
        assert (self.dbh)
        return self.cursorClass(self.dbh)

    def commit(self):<commit_msg>tighter parsing of the database connect strings<commit_after>#
# Copyright (c) 2005 rPath, Inc.
#
# This program is distributed under the terms of the Common Public License,
# version 1.0. A copy of this license should have been distributed with this
# source file in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/cpl.php.
#
# This program is distributed in the hope that it will be useful, but
# without any waranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the Common Public License for
# full details.
#

import sys
import re

class DBStoreError(Exception):
    pass

class DBStoreCursorError(DBStoreError):
    def __init__(self, msg, **kwargs):
        DBStoreError.__init__(self, msg, **kwargs)
                              
# base Cursor class. All backend drivers are expected to provide this
# interface
class BaseCursor:
    def __init__(self, dbh=None):
        self.dbh = dbh
        self._cursor = self._getCursor()
        self.description = None
        
    # this will need to be provided by each separate driver
    def _getCursor(self):
        assert(self.dbh)
        return self.dbh.cursor()
        
    def execute(self, sql, *args, **kw):
        assert(len(sql) > 0)
        assert(self.dbh and self._cursor)
        self.description = None
        # force dbi compliance here
        if len(kw) == 0:
            if len(args) == 0:
                return self._cursor.execute(sql)
            if len(args) == 1:
                return self._cursor.execute(sql, args[0])
            return self._cursor.execute(sql, *args)
        if len(args) == 1 and isinstance(args[0], dict):
            kw.update(args[0])
        return self._cursor.execute(sql, **kw)
        
    # return the column names of the current select
    def __rowDict(self, row):
        assert(self._cursor and self._cursor.description)       
        if row is None:
            return None
        if len(row) != len(self._cursor.description):
            raise DBStoreCursorError("Cursor description doew not match row data",
                                     row = row, desc = self._cursor.description)
        if not self.description:
            self.description = [ x[0] for x in self._cursor.description ]
        return dict(zip(self.description, row))
                                                            
    # (a,b)
    def fetchone(self):
        return self._cursor.fetchone()
    # [(a1,b1),(a2, b2)]
    def fetchall(self):
        return self._cursor.fetchall()
    def fetchmany(self, count=1):
        return self._cursor.fetchmany(count)
    
    # { name_a : a, name_b : b }
    def fetchone_dict(self):
        try:
            return self.__rowDict(self.fetchone())
        except:
            pass
        return None
    # [ {name_a:a1, name_b:b1}, {name_a:a2, name_b:b2} ]
    def fetchall_dict(self):
        try:
            return [ self.__rowDict(row) for row in self.fetchall() ]
        except:
            pass
        return None
    def fetchmany_dict(self, count=1):
        try:
            return [ self.__rowDict(row) for row in self.fetchmany(count) ]
        except:
            pass
        return None


# A cursor class for the drivers that do not support bind
# parameters. Instead of :name they use a Python-esque %(name)s
# syntax. This is quite fragile...
class BindlessCursor(BaseCursor):
    def __mungeSQL(self, sql):
        regex = re.compile(':(\w+)')
        # edit the input query
        keys = set()
        for key in regex.findall(sql):
            keys.add(key)
            sql = re.sub(":" + key, "%("+key+")s", sql)
        sql = re.sub("[(]\s?[?]", "( %s", sql)
        sql = re.sub("[=]\s?[?]", "= %s", sql)
        sql = re.sub("[,]\s?[?]", ", %s", sql)
        return (sql, keys)

    # we need to "fix" the sql code before calling out
    def execute(self, sql, *params, **kw):
        self.description = None
        sql, keys = self.__mungeSQL(sql)
        # figure out possible invocations
        if len(params) == 0:
            assert (sorted(kw.keys()) == sorted(keys))
        elif len(params) == 1:
            assert(isinstance(params, tuple))
            if isinstance(params[0], tuple):
                params = params[0]
            p = params[0]
            # if it is a dictionary, it must contain bind arguments
            if hasattr(p, 'keys'):
                kw.update(p)
            else: # special case - single positional argument
                assert(len(keys)==0 and len(kw)==0)
                return self._cursor.execute(sql, params)
        else: # many params, we don't mix in bind arguments
            assert(len(keys)==0 and len(kw)==0)
            return self._cursor.execute(sql, params)
        # we have a dict of bind arguments
        return self._cursor.execute(sql, kw)

# A class to handle database operations
class BaseDatabase:
    def __init__(self, db):
        assert(db)
        self.type = "base"
        self.database = db
        self.dbh = None
        # stderr needs to be around to print errors. hold a reference
        self.stderr = sys.stderr
        self.__transaction = None
        # need to figure out a statement generic enough for all kinds of backends
        self.alive_check = "select 1 where 1 = 1"
        self.cursorClass = BaseCursor
        
    # the string syntax for database connection is [[user[:password]@]host/]database
    def _connectData(self, names = ["user", "password", "host", "database"]):
        assert(self.database)
        assert(len(tuple(names)) == 4)
        # regexes are k001 and I am 1337 h@x0r
        regex = re.compile(
            "^(((?P<%s>[^:]+)(:(?P<%s>[^@]+))?@)?(?P<%s>[^/]+)/)?(?P<%s>.+)$" % tuple(names)
            )
        m = regex.match(self.database.strip())
        assert(m)
        return m.groupdict()
        
    def connect(self):
        assert(self.database)        
        raise RuntimeError("This connect function has to be provided by the database driver")
    
    def close(self):
        if self.dbh:
            self.dbh.close()
        self.dbh = self.database = None

    def alive(self):
        assert(self.dbh)
        try:
            c = self.cursor()
            c.execute(self.alive_check)
        except:
            # database connection lost
            return 0
        else:
            del c
        return 1

    def cursor(self):
        assert (self.dbh)
        return self.cursorClass(self.dbh)

    def commit(self):
{'fraction_non_alphanumeric': 0.11239656757585044, 'fraction_numerical': 0.007201961385228318, 'mean_word_length': 1.974025974025974, 'pattern_counts': {'":': 2, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2181378', 'n_tokens_mistral': 3820, 'n_tokens_neox': 3710, 'n_words': 1173}