merge fpdb_db.py into Database.py

This commit is contained in:
sqlcoder 2010-01-26 23:54:04 +00:00
parent 47baee65f9
commit 11bc48e126
10 changed files with 215 additions and 241 deletions

View File

@ -39,11 +39,30 @@ import string
import re import re
import Queue import Queue
import codecs import codecs
import logging
import math
# pyGTK modules # pyGTK modules
# Other library modules
try:
import sqlalchemy.pool as pool
use_pool = True
except ImportError:
logging.info("Not using sqlalchemy connection pool.")
use_pool = False
try:
from numpy import var
use_numpy = True
except ImportError:
logging.info("Not using numpy to define variance in sqlite.")
use_numpy = False
# FreePokerTools modules # FreePokerTools modules
import fpdb_db
import Configuration import Configuration
import SQL import SQL
import Card import Card
@ -54,6 +73,27 @@ from Exceptions import *
log = Configuration.get_logger("logging.conf") log = Configuration.get_logger("logging.conf")
encoder = codecs.lookup('utf-8') encoder = codecs.lookup('utf-8')
DB_VERSION = 118
# Variance created as sqlite has a bunch of undefined aggregate functions.
class VARIANCE:
def __init__(self):
self.store = []
def step(self, value):
self.store.append(value)
def finalize(self):
return float(var(self.store))
class sqlitemath:
def mod(self, a, b):
return a%b
class Database: class Database:
MYSQL_INNODB = 2 MYSQL_INNODB = 2
@ -190,7 +230,22 @@ class Database:
log.info("Creating Database instance, sql = %s" % sql) log.info("Creating Database instance, sql = %s" % sql)
self.config = c self.config = c
self.__connected = False self.__connected = False
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql self.settings = {}
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
db_params = c.get_db_parameters()
self.import_options = c.get_import_parameters()
self.backend = db_params['db-backend']
self.db_server = db_params['db-server']
self.database = db_params['db-databaseName']
self.host = db_params['db-host']
# where possible avoid creating new SQL instance by using the global one passed in
if sql is None:
self.sql = SQL.Sql(db_server = self.db_server)
else:
self.sql = sql
# connect to db
self.do_connect(c) self.do_connect(c)
if self.backend == self.PGSQL: if self.backend == self.PGSQL:
@ -200,12 +255,6 @@ class Database:
#ISOLATION_LEVEL_SERIALIZABLE = 2 #ISOLATION_LEVEL_SERIALIZABLE = 2
# where possible avoid creating new SQL instance by using the global one passed in
if sql is None:
self.sql = SQL.Sql(db_server = self.db_server)
else:
self.sql = sql
if self.backend == self.SQLITE and self.database == ':memory:' and self.wrongDbVersion: if self.backend == self.SQLITE and self.database == ':memory:' and self.wrongDbVersion:
log.info("sqlite/:memory: - creating") log.info("sqlite/:memory: - creating")
self.recreate_tables() self.recreate_tables()
@ -228,8 +277,6 @@ class Database:
self.h_date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD) for hero self.h_date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD) for hero
self.date_nhands_ago = {} # dates N hands ago per player - not used yet self.date_nhands_ago = {} # dates N hands ago per player - not used yet
self.cursor = self.fdb.cursor
self.saveActions = False if self.import_options['saveActions'] == False else True self.saveActions = False if self.import_options['saveActions'] == False else True
self.connection.rollback() # make sure any locks taken so far are released self.connection.rollback() # make sure any locks taken so far are released
@ -240,14 +287,20 @@ class Database:
self.hud_style = style self.hud_style = style
def do_connect(self, c): def do_connect(self, c):
if c is None:
raise FpdbError('Configuration not defined')
db = c.get_db_parameters()
try: try:
self.fdb.do_connect(c) self.connect(backend=db['db-backend'],
host=db['db-host'],
database=db['db-databaseName'],
user=db['db-user'],
password=db['db-password'])
except: except:
# error during connect # error during connect
self.__connected = False self.__connected = False
raise raise
self.connection = self.fdb.db
self.wrongDbVersion = self.fdb.wrongDbVersion
db_params = c.get_db_parameters() db_params = c.get_db_parameters()
self.import_options = c.get_import_parameters() self.import_options = c.get_import_parameters()
@ -257,11 +310,118 @@ class Database:
self.host = db_params['db-host'] self.host = db_params['db-host']
self.__connected = True self.__connected = True
def connect(self, backend=None, host=None, database=None,
user=None, password=None):
"""Connects a database with the given parameters"""
if backend is None:
raise FpdbError('Database backend not defined')
self.backend = backend
self.host = host
self.user = user
self.password = password
self.database = database
self.connection = None
self.cursor = None
if backend == Database.MYSQL_INNODB:
import MySQLdb
if use_pool:
MySQLdb = pool.manage(MySQLdb, pool_size=5)
try:
self.connection = MySQLdb.connect(host=host, user=user, passwd=password, db=database, use_unicode=True)
#TODO: Add port option
except MySQLdb.Error, ex:
if ex.args[0] == 1045:
raise FpdbMySQLAccessDenied(ex.args[0], ex.args[1])
elif ex.args[0] == 2002 or ex.args[0] == 2003: # 2002 is no unix socket, 2003 is no tcp socket
raise FpdbMySQLNoDatabase(ex.args[0], ex.args[1])
else:
print "*** WARNING UNKNOWN MYSQL ERROR", ex
elif backend == Database.PGSQL:
import psycopg2
import psycopg2.extensions
if use_pool:
psycopg2 = pool.manage(psycopg2, pool_size=5)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
# If DB connection is made over TCP, then the variables
# host, user and password are required
# For local domain-socket connections, only DB name is
# needed, and everything else is in fact undefined and/or
# flat out wrong
# sqlcoder: This database only connect failed in my windows setup??
# Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere?
connected = False
if self.host == "localhost" or self.host == "127.0.0.1":
try:
self.connection = psycopg2.connect(database = database)
connected = True
except:
# direct connection failed so try user/pass/... version
pass
if not connected:
try:
self.connection = psycopg2.connect(host = host,
user = user,
password = password,
database = database)
except Exception, ex:
if 'Connection refused' in ex.args[0]:
# meaning eg. db not running
raise FpdbPostgresqlNoDatabase(errmsg = ex.args[0])
elif 'password authentication' in ex.args[0]:
raise FpdbPostgresqlAccessDenied(errmsg = ex.args[0])
else:
msg = ex.args[0]
print msg
raise FpdbError(msg)
elif backend == Database.SQLITE:
logging.info("Connecting to SQLite: %(database)s" % {'database':database})
import sqlite3
if use_pool:
sqlite3 = pool.manage(sqlite3, pool_size=1)
else:
logging.warning("SQLite won't work well without 'sqlalchemy' installed.")
if database != ":memory:":
if not os.path.isdir(self.config.dir_databases):
print "Creating directory: '%s'" % (self.config.dir_databases)
logging.info("Creating directory: '%s'" % (self.config.dir_databases))
os.mkdir(self.config.dir_databases)
database = os.path.join(self.config.dir_databases, database)
logging.info(" sqlite db: " + database)
self.connection = sqlite3.connect(database, detect_types=sqlite3.PARSE_DECLTYPES )
sqlite3.register_converter("bool", lambda x: bool(int(x)))
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
self.connection.create_function("floor", 1, math.floor)
tmp = sqlitemath()
self.connection.create_function("mod", 2, tmp.mod)
if use_numpy:
self.connection.create_aggregate("variance", 1, VARIANCE)
else:
logging.warning("Some database functions will not work without NumPy support")
else:
raise FpdbError("unrecognised database backend:"+backend)
self.cursor = self.connection.cursor()
self.cursor.execute(self.sql.query['set tx level'])
self.wrongDbVersion = False
try:
self.cursor.execute("SELECT * FROM Settings")
settings = self.cursor.fetchone()
if settings[0] != DB_VERSION:
logging.error("outdated or too new database version (%s) - please recreate tables"
% (settings[0]))
self.wrongDbVersion = True
except:# _mysql_exceptions.ProgrammingError:
if database != ":memory:": print "failed to read settings table - please recreate tables"
self.wrongDbVersion = True
#end def connect
def commit(self): def commit(self):
self.fdb.db.commit() self.connection.commit()
def rollback(self): def rollback(self):
self.fdb.db.rollback() self.connection.rollback()
def connected(self): def connected(self):
return self.__connected return self.__connected
@ -274,11 +434,18 @@ class Database:
def disconnect(self, due_to_error=False): def disconnect(self, due_to_error=False):
"""Disconnects the DB (rolls back if param is true, otherwise commits""" """Disconnects the DB (rolls back if param is true, otherwise commits"""
self.fdb.disconnect(due_to_error) if due_to_error:
self.connection.rollback()
else:
self.connection.commit()
self.cursor.close()
self.connection.close()
def reconnect(self, due_to_error=False): def reconnect(self, due_to_error=False):
"""Reconnects the DB""" """Reconnects the DB"""
self.fdb.reconnect(due_to_error=False) #print "started reconnect"
self.disconnect(due_to_error)
self.connect(self.backend, self.host, self.database, self.user, self.password)
def get_backend_name(self): def get_backend_name(self):
"""Returns the name of the currently used backend""" """Returns the name of the currently used backend"""
@ -291,6 +458,9 @@ class Database:
else: else:
raise FpdbError("invalid backend") raise FpdbError("invalid backend")
def get_db_info(self):
return (self.host, self.database, self.user, self.password)
def get_table_name(self, hand_id): def get_table_name(self, hand_id):
c = self.connection.cursor() c = self.connection.cursor()
c.execute(self.sql.query['get_table_name'], (hand_id, )) c.execute(self.sql.query['get_table_name'], (hand_id, ))
@ -1267,7 +1437,7 @@ class Database:
try: try:
self.get_cursor().execute(self.sql.query['lockForInsert']) self.get_cursor().execute(self.sql.query['lockForInsert'])
except: except:
print "Error during fdb.lock_for_insert:", str(sys.exc_value) print "Error during lock_for_insert:", str(sys.exc_value)
#end def lock_for_insert #end def lock_for_insert
########################### ###########################

View File

@ -27,8 +27,8 @@ import gobject
#import pokereval #import pokereval
import Configuration import Configuration
import fpdb_db import Database
import FpdbSQLQueries import SQL
import Charset import Charset
class Filters(threading.Thread): class Filters(threading.Thread):
@ -790,10 +790,10 @@ def main(argv=None):
config = Configuration.Config() config = Configuration.Config()
db = None db = None
db = fpdb_db.fpdb_db() db = Database.Database()
db.do_connect(config) db.do_connect(config)
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name()) qdict = SQL.SQL(db.get_backend_name())
i = Filters(db, config, qdict) i = Filters(db, config, qdict)
main_window = gtk.Window() main_window = gtk.Window()

View File

@ -300,7 +300,6 @@ if __name__== "__main__":
(options, argv) = parser.parse_args() (options, argv) = parser.parse_args()
config = Configuration.Config() config = Configuration.Config()
# db = fpdb_db.fpdb_db()
settings = {} settings = {}
settings['minPrint'] = options.minPrint settings['minPrint'] = options.minPrint

View File

@ -27,7 +27,6 @@ from time import time, strftime
import Card import Card
import fpdb_import import fpdb_import
import Database import Database
import fpdb_db
import Filters import Filters
import Charset import Charset

View File

@ -205,7 +205,7 @@ dealt whether they were seen in a 'dealt to' line
def insert(self, db): def insert(self, db):
""" Function to insert Hand into database """ Function to insert Hand into database
Should not commit, and do minimal selects. Callers may want to cache commits Should not commit, and do minimal selects. Callers may want to cache commits
db: a connected fpdb_db object""" db: a connected Database object"""
self.stats.getStats(self) self.stats.getStats(self)

View File

@ -58,6 +58,27 @@ class Sql:
self.query['drop_table'] = """DROP TABLE IF EXISTS """ self.query['drop_table'] = """DROP TABLE IF EXISTS """
##################################################################
# Set transaction isolation level
##################################################################
if db_server == 'mysql' or db_server == 'postgresql':
self.query['set tx level'] = """SET SESSION TRANSACTION
ISOLATION LEVEL READ COMMITTED"""
elif db_server == 'sqlite':
self.query['set tx level'] = """ """
################################
# Select basic info
################################
self.query['getSiteId'] = """SELECT id from Sites where name = %s"""
self.query['getGames'] = """SELECT DISTINCT category from Gametypes"""
self.query['getLimits'] = """SELECT DISTINCT bigBlind from Gametypes ORDER by bigBlind DESC"""
################################ ################################
# Create Settings # Create Settings
################################ ################################

View File

@ -185,7 +185,7 @@ class Tourney(object):
def old_insert_from_Hand(self, db): def old_insert_from_Hand(self, db):
""" Function to insert Hand into database """ Function to insert Hand into database
Should not commit, and do minimal selects. Callers may want to cache commits Should not commit, and do minimal selects. Callers may want to cache commits
db: a connected fpdb_db object""" db: a connected Database object"""
# TODO: # TODO:
# Players - base playerid and siteid tuple # Players - base playerid and siteid tuple
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)

View File

@ -112,7 +112,6 @@ import GuiGraphViewer
import GuiSessionViewer import GuiSessionViewer
import SQL import SQL
import Database import Database
import FpdbSQLQueries
import Configuration import Configuration
import Exceptions import Exceptions

View File

@ -16,219 +16,6 @@
#In the "official" distribution you can find the license in #In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
import os
import re
import sys
import logging
import math
from time import time, strftime
from Exceptions import *
try:
import sqlalchemy.pool as pool
use_pool = True
except ImportError:
logging.info("Not using sqlalchemy connection pool.")
use_pool = False
try:
from numpy import var
use_numpy = True
except ImportError:
logging.info("Not using numpy to define variance in sqlite.")
use_numpy = False
import FpdbSQLQueries
import Configuration
DB_VERSION = 118
# Variance created as sqlite has a bunch of undefined aggregate functions.
class VARIANCE:
def __init__(self):
self.store = []
def step(self, value):
self.store.append(value)
def finalize(self):
return float(var(self.store))
class sqlitemath:
def mod(self, a, b):
return a%b
class fpdb_db:
MYSQL_INNODB = 2
PGSQL = 3
SQLITE = 4
def __init__(self):
"""Simple constructor, doesnt really do anything"""
self.db = None
self.cursor = None
self.sql = {}
#end def __init__
def do_connect(self, config=None):
"""Connects a database using information in config"""
if config is None:
raise FpdbError('Configuration not defined')
self.config = config
self.settings = {}
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
db = config.get_db_parameters()
self.connect(backend=db['db-backend'],
host=db['db-host'],
database=db['db-databaseName'],
user=db['db-user'],
password=db['db-password'])
#end def do_connect
def connect(self, backend=None, host=None, database=None,
user=None, password=None):
"""Connects a database with the given parameters"""
if backend is None:
raise FpdbError('Database backend not defined')
self.backend = backend
self.host = host
self.user = user
self.password = password
self.database = database
createTables = False
if backend == fpdb_db.MYSQL_INNODB:
import MySQLdb
if use_pool:
MySQLdb = pool.manage(MySQLdb, pool_size=5)
try:
self.db = MySQLdb.connect(host=host, user=user, passwd=password, db=database, use_unicode=True)
#TODO: Add port option
except MySQLdb.Error, ex:
if ex.args[0] == 1045:
raise FpdbMySQLAccessDenied(ex.args[0], ex.args[1])
elif ex.args[0] == 2002 or ex.args[0] == 2003: # 2002 is no unix socket, 2003 is no tcp socket
raise FpdbMySQLNoDatabase(ex.args[0], ex.args[1])
else:
print "*** WARNING UNKNOWN MYSQL ERROR", ex
elif backend == fpdb_db.PGSQL:
import psycopg2
import psycopg2.extensions
if use_pool:
psycopg2 = pool.manage(psycopg2, pool_size=5)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
# If DB connection is made over TCP, then the variables
# host, user and password are required
# For local domain-socket connections, only DB name is
# needed, and everything else is in fact undefined and/or
# flat out wrong
# sqlcoder: This database only connect failed in my windows setup??
# Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere?
connected = False
if self.host == "localhost" or self.host == "127.0.0.1":
try:
self.db = psycopg2.connect(database = database)
connected = True
except:
# direct connection failed so try user/pass/... version
pass
if not connected:
try:
self.db = psycopg2.connect(host = host,
user = user,
password = password,
database = database)
except Exception, ex:
if 'Connection refused' in ex.args[0]:
# meaning eg. db not running
raise FpdbPostgresqlNoDatabase(errmsg = ex.args[0])
elif 'password authentication' in ex.args[0]:
raise FpdbPostgresqlAccessDenied(errmsg = ex.args[0])
else:
msg = ex.args[0]
print msg
raise FpdbError(msg)
elif backend == fpdb_db.SQLITE:
logging.info("Connecting to SQLite: %(database)s" % {'database':database})
import sqlite3
if use_pool:
sqlite3 = pool.manage(sqlite3, pool_size=1)
else:
logging.warning("SQLite won't work well without 'sqlalchemy' installed.")
if database != ":memory:":
if not os.path.isdir(self.config.dir_databases):
print "Creating directory: '%s'" % (self.config.dir_databases)
logging.info("Creating directory: '%s'" % (self.config.dir_databases))
os.mkdir(self.config.dir_databases)
createTables = True # not needed? just test for no settings table
database = os.path.join(self.config.dir_databases, database)
logging.info(" sqlite db: " + database)
self.db = sqlite3.connect(database, detect_types=sqlite3.PARSE_DECLTYPES )
sqlite3.register_converter("bool", lambda x: bool(int(x)))
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
self.db.create_function("floor", 1, math.floor)
tmp = sqlitemath()
self.db.create_function("mod", 2, tmp.mod)
if use_numpy:
self.db.create_aggregate("variance", 1, VARIANCE)
else:
logging.warning("Some database functions will not work without NumPy support")
else:
raise FpdbError("unrecognised database backend:"+backend)
self.cursor = self.db.cursor()
# Set up query dictionary as early in the connection process as we can.
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
self.cursor.execute(self.sql.query['set tx level'])
self.wrongDbVersion = False
try:
self.cursor.execute("SELECT * FROM Settings")
settings = self.cursor.fetchone()
if settings[0] != DB_VERSION:
logging.error("outdated or too new database version (%s) - please recreate tables"
% (settings[0]))
self.wrongDbVersion = True
except:# _mysql_exceptions.ProgrammingError:
if database != ":memory:": print "failed to read settings table - please recreate tables"
self.wrongDbVersion = True
#end def connect
def disconnect(self, due_to_error=False):
"""Disconnects the DB"""
if due_to_error:
self.db.rollback()
else:
self.db.commit()
self.cursor.close()
self.db.close()
#end def disconnect
def reconnect(self, due_to_error=False):
"""Reconnects the DB"""
#print "started fpdb_db.reconnect"
self.disconnect(due_to_error)
self.connect(self.backend, self.host, self.database, self.user, self.password)
def get_backend_name(self):
"""Returns the name of the currently used backend"""
if self.backend==2:
return "MySQL InnoDB"
elif self.backend==3:
return "PostgreSQL"
elif self.backend==4:
return "SQLite"
else:
raise FpdbError("invalid backend")
#end def get_backend_name
def get_db_info(self):
return (self.host, self.database, self.user, self.password)
#end def get_db_info
#end class fpdb_db #end class fpdb_db

View File

@ -35,7 +35,6 @@ import gtk
# fpdb/FreePokerTools modules # fpdb/FreePokerTools modules
import fpdb_db
import Database import Database
import Configuration import Configuration
import Exceptions import Exceptions