From f63aaa316dee7a8b16ef8914040cdbba09e13972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 4 Feb 2009 15:29:28 +0100 Subject: [PATCH] Remove sql_db and add database in backend --- setup.py | 2 +- trytond/backend/database.py | 113 +++++++++ trytond/backend/postgresql/__init__.py | 1 + trytond/backend/postgresql/database.py | 284 ++++++++++++++++++++++ trytond/{ => backend/postgresql}/init.sql | 0 trytond/osv/osv.py | 6 +- trytond/pooler.py | 7 +- trytond/report/report.py | 4 +- trytond/server.py | 4 +- trytond/sql_db.py | 225 ----------------- trytond/web_service/db.py | 96 +++----- trytond/wizard/wizard.py | 4 +- 12 files changed, 443 insertions(+), 303 deletions(-) create mode 100644 trytond/backend/database.py create mode 100644 trytond/backend/postgresql/database.py rename trytond/{ => backend/postgresql}/init.sql (100%) delete mode 100644 trytond/sql_db.py diff --git a/setup.py b/setup.py index 312715d89b..dd7e4ececc 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ setup(name=PACKAGE, VERSION.rsplit('.', 1)[0] + '/', packages=find_packages(exclude=['*.modules.*', 'modules.*', 'modules']), package_data={ - 'trytond': ['init.sql'], + 'trytond.backend.postgresql': ['init.sql'], 'trytond.ir': ['*.xml', '*.csv'], 'trytond.ir.module': ['*.xml'], 'trytond.ir.ui': ['*.xml', '*.rng', '*.rnc'], diff --git a/trytond/backend/database.py b/trytond/backend/database.py new file mode 100644 index 0000000000..0fb54e0086 --- /dev/null +++ b/trytond/backend/database.py @@ -0,0 +1,113 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +DatabaseIntegrityError = None + +class DatabaseInterface(object): + ''' + Define generic interface for database connection + ''' + + def __init__(self, database_name=None): + self.database_name = database_name + + def connect(self): + ''' + Connect to the database + + :return: the database + ''' + raise + + def cursor(self, autocommit=False): + ''' + Retreive a cursor on the database + + :param autocommit: a boolean to active autocommit + :return: a Cursor + ''' + raise + + def close(self): + ''' + Close all connection + ''' + raise + + def create(self, cursor, database_name): + ''' + Create a database + + :param database_name: the database name + ''' + raise + + def drop(self, cursor, database_name): + ''' + Drop a database + + :param cursor: a cursor on an other database + :param database_name: the database name + ''' + raise + + @staticmethod + def list(cursor): + ''' + Get the list of database + + :return: a list of database name + ''' + raise + + @staticmethod + def init(cursor): + ''' + Initialize a database + + :param cursor: a cursor on the database + ''' + raise + +class CursorInterface(object): + ''' + Define generic interface for database cursor + ''' + sql_log = False + IN_MAX = 1000 + + def __init__(self): + raise + + def execute(self, sql, params=None): + ''' + Execute a query + + :param sql: a sql query string + :param params: a tuple or list of parameters + ''' + raise + + def close(self): + ''' + Close the cursor + ''' + raise + + def commit(self): + ''' + Commit the cursor + ''' + raise + + def rollback(self): + ''' + Rollback the cursor + ''' + raise + + def test(self): + ''' + Test if it is a Tryton database. + ''' + raise diff --git a/trytond/backend/postgresql/__init__.py b/trytond/backend/postgresql/__init__.py index 7a9bf02624..9598cf8965 100644 --- a/trytond/backend/postgresql/__init__.py +++ b/trytond/backend/postgresql/__init__.py @@ -1,5 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. +from database import * from fields import * from table import * diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py new file mode 100644 index 0000000000..ed1f11eb95 --- /dev/null +++ b/trytond/backend/postgresql/database.py @@ -0,0 +1,284 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.backend.database import DatabaseInterface, CursorInterface +from trytond.config import CONFIG +from trytond.security import Session +from psycopg2.pool import ThreadedConnectionPool +from psycopg2.extensions import cursor +from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE +from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT +from psycopg2.extensions import register_type, register_adapter +from psycopg2.extensions import UNICODE, AsIs +from psycopg2 import IntegrityError as DatabaseIntegrityError +import time +import logging +import re +import os +if os.name == 'posix': + import pwd + +RE_FROM = re.compile('.* from "?([a-zA-Z_0-9]+)"?.*$') +RE_INTO = re.compile('.* into "?([a-zA-Z_0-9]+)"?.*$') + + +class Database(DatabaseInterface): + + def __init__(self, database_name='template1'): + super(Database, self).__init__(database_name=database_name) + self._connpool = None + + def connect(self): + host = CONFIG['db_host'] and "host=%s" % CONFIG['db_host'] or '' + port = CONFIG['db_port'] and "port=%s" % CONFIG['db_port'] or '' + name = "dbname=%s" % self.database_name + user = CONFIG['db_user'] and "user=%s" % CONFIG['db_user'] or '' + password = CONFIG['db_password'] \ + and "password=%s" % CONFIG['db_password'] or '' + maxconn = int(CONFIG['db_maxconn']) or 64 + dsn = '%s %s %s %s %s' % (host, port, name, user, password) + self._connpool = ThreadedConnectionPool(0, maxconn, dsn) + return self + + def cursor(self, autocommit=False): + if self._connpool is None: + self.connect() + conn = self._connpool.getconn() + if autocommit: + conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) + else: + conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE) + return Cursor(self._connpool, conn, self.database_name) + + def close(self): + if self._connpool is None: + return + self._connpool.closeall() + self._connpool = None + + def create(self, cursor, database_name): + cursor.execute('CREATE DATABASE "' + database_name + '" ' \ + 'TEMPLATE template0 ENCODING \'unicode\'') + + def drop(self, cursor, database_name): + cursor.execute('DROP DATABASE "' + database_name + '"') + + @staticmethod + def list(cursor): + db_user = CONFIG['db_user'] + if not db_user and os.name == 'posix': + db_user = pwd.getpwuid(os.getuid())[0] + if not db_user: + cursor.execute("SELECT usename " \ + "FROM pg_user " \ + "WHERE usesysid = (" \ + "SELECT datdba " \ + "FROM pg_database " \ + "WHERE datname = %s)", + (tools.CONFIG["db_name"],)) + res = cursor.fetchone() + db_user = res and res[0] + if db_user: + cursor.execute("SELECT datname " \ + "FROM pg_database " \ + "WHERE datdba = (" \ + "SELECT usesysid " \ + "FROM pg_user " \ + "WHERE usename=%s) " \ + "AND datname not in " \ + "('template0', 'template1', 'postgres') " \ + "ORDER BY datname", + (db_user,)) + else: + cursor.execute("SELECT datname " \ + "FROM pg_database " \ + "WHERE datname not in " \ + "('template0', 'template1','postgres') " \ + "ORDER BY datname") + res = [] + for db_name, in cursor.fetchall(): + try: + database = Database(db_name).connect() + except: + continue + cursor2 = database.cursor() + if cursor2.test(): + res.append(db_name) + cursor2.close() + database.close() + return res + + @staticmethod + def init(cursor): + sql_file = os.path.join(os.path.dirname(__file__), 'init.sql') + for line in file(sql_file).read().split(';'): + if (len(line)>0) and (not line.isspace()): + cursor.execute(line) + + for i in ('ir', 'workflow', 'res', 'webdav'): + root_path = os.path.join(os.path.dirname(__file__), '..', '..') + tryton_file = os.path.join(root_path, i, '__tryton__.py') + mod_path = os.path.join(root_path, i) + info = eval(file(tryton_file).read()) + active = info.get('active', False) + if active: + state = 'to install' + else: + state = 'uninstalled' + cursor.execute('SELECT NEXTVAL(\'ir_module_module_id_seq\')') + module_id = cursor.fetchone()[0] + cursor.execute('INSERT INTO ir_module_module ' \ + '(id, author, website, name, shortdesc, ' \ + 'description, state) ' \ + 'VALUES (%s, %s, %s, %s, %s, %s, %s)', + (module_id, info.get('author', ''), + info.get('website', ''), i, info.get('name', False), + info.get('description', ''), state)) + dependencies = info.get('depends', []) + for dependency in dependencies: + cursor.execute('INSERT INTO ir_module_module_dependency ' \ + '(module, name) VALUES (%s, %s)', + (module_id, dependency)) + + +class _Cursor(cursor): + + def __build_dict(self, row): + res = {} + for i in range(len(self.description)): + res[self.description[i][0]] = row[i] + return res + + def dictfetchone(self): + row = self.fetchone() + if row: + return self.__build_dict(row) + else: + return row + + def dictfetchmany(self, size): + res = [] + rows = self.fetchmany(size) + for row in rows: + res.append(self.__build_dict(row)) + return res + + def dictfetchall(self): + res = [] + rows = self.fetchall() + for row in rows: + res.append(self.__build_dict(row)) + return res + + +class Cursor(CursorInterface): + + def __init__(self, connpool, conn, database_name): + self._connpool = connpool + self._conn = conn + self.database_name = database_name + self.dbname = self.database_name #XX to remove + self.cursor = conn.cursor(cursor_factory=_Cursor) + self.sql_from_log = {} + self.sql_into_log = {} + self.count = { + 'from': 0, + 'into': 0, + } + + def __getattr__(self, name): + return getattr(self.cursor, name) + + def execute(self, sql, params=None): + if params is None: + params = () + + if self.sql_log: + now = time.time() + + try: + if params: + res = self.cursor.execute(sql, params) + else: + res = self.cursor.execute(sql) + except: + logger = logging.getLogger('sql') + logger.error('Wrong SQL: ' + sql % tuple("'%s'" % unicode(x) + for x in params or [])) + raise + if self.sql_log: + res_from = RE_FROM.match(sql.lower()) + if res_from: + self.sql_from_log.setdefault(res_from.group(1), [0, 0]) + self.sql_from_log[res_from.group(1)][0] += 1 + self.sql_from_log[res_from.group(1)][1] += mdt.now() - now + self.count['from'] += 1 + res_into = RE_INTO.match(sql.lower()) + if res_into: + self.sql_into_log.setdefault(res_into.group(1), [0, 0]) + self.sql_into_log[res_into.group(1)][0] += 1 + self.sql_into_log[res_into.group(1)][1] += mdt.now() - now + self.count['into'] += 1 + return res + + def _print_log(self, sql_type='from'): + logger = logging.getLogger('sql') + logger.info("SQL LOG %s:" % (sql_type,)) + if sql_type == 'from': + logs = self.sql_from_log.items() + else: + logs = self.sql_into_log.items() + logs.sort(lambda x, y: cmp(x[1][1], y[1][1])) + amount = 0 + for log in logs: + logger.info("table:", log[0], ":", str(log[1][1]), "/", log[1][0]) + amount += log[1][1] + logger.info("SUM:%s/%d"% (amount, self.count[sql_type])) + + def close(self): + if self.sql_log: + self._print_log('from') + self._print_log('into') + self.cursor.close() + + # This force the cursor to be freed, and thus, available again. It is + # important because otherwise we can overload the server very easily + # because of a cursor shortage (because cursors are not garbage + # collected as fast as they should). The problem is probably due in + # part because browse records keep a reference to the cursor. + del self.cursor + self._connpool.putconn(self._conn) + + def commit(self): + self._conn.commit() + + def rollback(self): + self._conn.rollback() + + def test(self): + self.cursor.execute("SELECT relname " \ + "FROM pg_class " \ + "WHERE relkind = 'r' AND relname in (" \ + "'inherit', " + "'ir_model', " + "'ir_model_field', " + "'ir_ui_view', " + "'ir_ui_menu', " + "'res_user', " + "'res_group', " + "'res_group_user_rel', " + "'wkf', " + "'wkf_activity', " + "'wkf_transition', " + "'wkf_instance', " + "'wkf_workitem', " + "'wkf_witm_trans', " + "'ir_module_module', " + "'ir_module_module_dependency, '" + "'ir_translation, '" + "'ir_lang'" + ")") + return len(self.cursor.fetchall()) != 0 + +register_type(UNICODE) +register_adapter(Session, AsIs) diff --git a/trytond/init.sql b/trytond/backend/postgresql/init.sql similarity index 100% rename from trytond/init.sql rename to trytond/backend/postgresql/init.sql diff --git a/trytond/osv/osv.py b/trytond/osv/osv.py index 3c81b84862..9c5b6f1d73 100644 --- a/trytond/osv/osv.py +++ b/trytond/osv/osv.py @@ -5,9 +5,9 @@ from orm import ORM from trytond.netsvc import Service, LocalService from trytond import pooler +from trytond.backend import DatabaseIntegrityError import copy import sys -from trytond.sql_db import IntegrityError import traceback from trytond.tools import Cache, find_language_context import time @@ -54,7 +54,7 @@ class OSVService(Service): traceback.format_exception(*sys.exc_info())) logger = logging.getLogger("web-service") logger.error('Exception in call: \n' + tb_s) - if isinstance(exception, IntegrityError): + if isinstance(exception, DatabaseIntegrityError): for key in self._sql_errors.keys(): if key in exception[0]: msg = self._sql_errors[key] @@ -105,7 +105,7 @@ class OSVService(Service): traceback.format_exception(*sys.exc_info())) logger = logging.getLogger() logger.error('Exception in call: \n' + tb_s) - if isinstance(exception, IntegrityError): + if isinstance(exception, DatabaseIntegrityError): for key in self._sql_errors.keys(): if key in exception[0]: raise Exception('UserError', 'Constraint Error', diff --git a/trytond/pooler.py b/trytond/pooler.py index 54d1103c5a..7a3f52856f 100644 --- a/trytond/pooler.py +++ b/trytond/pooler.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -from trytond.sql_db import db_connect +from trytond.backend import Database import logging from threading import RLock @@ -25,7 +25,7 @@ def get_db_and_pool(db_name, update_module=False, wizard=False, report=False, else: logging.getLogger('pooler').info( 'Connecting to %s' % (db_name)) - database = db_connect(db_name) + database = Database(db_name).connect() cursor = database.cursor() if not cursor.test(): raise Exception('UserError', 'Invalid database!') @@ -105,7 +105,8 @@ def get_db_only(db_name, verbose=True, blocking=True): if verbose: logging.getLogger('pooler').info( 'Connecting to %s' % (db_name)) - database = db_connect(db_name) + database = Database(db_name) + database.connect() _DB[db_name] = database finally: lock.release() diff --git a/trytond/report/report.py b/trytond/report/report.py index 7b88d72411..35ccf87847 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -28,7 +28,7 @@ import tempfile from genshi.filters import Translator import traceback from trytond.config import CONFIG -from trytond.sql_db import IntegrityError +from trytond.backend import DatabaseIntegrityError import inspect import mx.DateTime import logging @@ -64,7 +64,7 @@ class ReportService(Service): traceback.format_exception(*sys.exc_info())) logging.getLogger("web-service").error( 'Exception in call: ' + tb_s) - if isinstance(exception, IntegrityError): + if isinstance(exception, DatabaseIntegrityError): pool = pooler.get_pool(cursor.dbname) for key in pool._sql_errors.keys(): if key in exception[0]: diff --git a/trytond/server.py b/trytond/server.py index 85d5462086..8e00af1fd4 100644 --- a/trytond/server.py +++ b/trytond/server.py @@ -12,7 +12,7 @@ import netsvc import time import psycopg2 import pooler -import sql_db +from backend import Database import config from config import CONFIG import web_service @@ -98,7 +98,7 @@ class TrytonServer(object): if CONFIG['init']: if not cursor.test(): self.logger.info("init db") - sql_db.init_db(cursor) + Database.init(cursor) init[db_name] = True cursor.commit() cursor.close() diff --git a/trytond/sql_db.py b/trytond/sql_db.py deleted file mode 100644 index f603d697d2..0000000000 --- a/trytond/sql_db.py +++ /dev/null @@ -1,225 +0,0 @@ -#This file is part of Tryton. The COPYRIGHT file at the top level of -#this repository contains the full copyright notices and license terms. -from psycopg2.pool import ThreadedConnectionPool -from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE, cursor, AsIs -from psycopg2 import IntegrityError -import psycopg2 -import re -import os -from mx import DateTime as mdt -import zipfile -import version -from config import CONFIG -import logging -from trytond.security import Session -from trytond.backend.postgresql import * - -RE_FROM = re.compile('.* from "?([a-zA-Z_0-9]+)"?.*$') -RE_INTO = re.compile('.* into "?([a-zA-Z_0-9]+)"?.*$') - -class tryton_cursor(cursor): - - def __build_dict(self, row): - res = {} - for i in range(len(self.description)): - res[self.description[i][0]] = row[i] - return res - - def dictfetchone(self): - row = self.fetchone() - if row: - return self.__build_dict(row) - else: - return row - - def dictfetchmany(self, size): - res = [] - rows = self.fetchmany(size) - for row in rows: - res.append(self.__build_dict(row)) - return res - - def dictfetchall(self): - res = [] - rows = self.fetchall() - for row in rows: - res.append(self.__build_dict(row)) - return res - - -class FakeCursor(object): - nbr = 0 - _tables = {} - sql_log = False - IN_MAX = 1000 - - def __init__(self, connpool, conn, dbname, cursor_factory): - self._connpool = connpool - self.conn = conn - self.cursor_factory = cursor_factory - self.cursor = conn.cursor(cursor_factory=self.cursor_factory) - self.dbname = dbname - self.sql_from_log = {} - self.sql_into_log = {} - self.count = { - 'from': 0, - 'into': 0, - } - - def execute(self, sql, params=None): - if not params: - params = () - - if self.sql_log: - now = mdt.now() - try: - if params: - res = self.cursor.execute(sql, params) - else: - res = self.cursor.execute(sql) - except: - logger = logging.getLogger('sql') - logger.error('Wrong SQL: ' + sql % tuple("'%s'" % unicode(x) - for x in params or [])) - raise - if self.sql_log: - res_from = RE_FROM.match(sql.lower()) - if res_from: - self.sql_from_log.setdefault(res_from.group(1), [0, 0]) - self.sql_from_log[res_from.group(1)][0] += 1 - self.sql_from_log[res_from.group(1)][1] += mdt.now() - now - self.count['from'] += 1 - res_into = RE_INTO.match(sql.lower()) - if res_into: - self.sql_into_log.setdefault(res_into.group(1), [0, 0]) - self.sql_into_log[res_into.group(1)][0] += 1 - self.sql_into_log[res_into.group(1)][1] += mdt.now() - now - self.count['into'] += 1 - return res - - def print_log(self, sql_type='from'): - print "SQL LOG %s:" % (sql_type,) - if sql_type == 'from': - logs = self.sql_from_log.items() - else: - logs = self.sql_into_log.items() - logs.sort(lambda x, y: cmp(x[1][1], y[1][1])) - amount = 0 - for log in logs: - print "table:", log[0], ":", str(log[1][1]), "/", log[1][0] - amount += log[1][1] - print "SUM:%s/%d"% (amount, self.count[sql_type]) - - def close(self): - if self.sql_log: - self.print_log('from') - self.print_log('into') - self.cursor.close() - - # This force the cursor to be freed, and thus, available again. It is - # important because otherwise we can overload the server very easily - # because of a cursor shortage (because cursors are not garbage - # collected as fast as they should). The problem is probably due in - # part because browse records keep a reference to the cursor. - del self.cursor - self._connpool.putconn(self.conn) - - def commit(self): - self.conn.commit() - - def rollback(self): - self.conn.rollback() - - def __getattr__(self, name): - return getattr(self.cursor, name) - - def test(self): - ''' - Test if it is a Tryton database. - ''' - self.cursor.execute("SELECT relname " \ - "FROM pg_class " \ - "WHERE relkind = 'r' AND relname in (" \ - "'inherit', " - "'ir_model', " - "'ir_model_field', " - "'ir_ui_view', " - "'ir_ui_menu', " - "'res_user', " - "'res_group', " - "'res_group_user_rel', " - "'wkf', " - "'wkf_activity', " - "'wkf_transition', " - "'wkf_instance', " - "'wkf_workitem', " - "'wkf_witm_trans', " - "'ir_module_module', " - "'ir_module_module_dependency, '" - "'ir_translation, '" - "'ir_lang'" - ")") - return len(self.cursor.fetchall()) != 0 - - -class FakeDB: - - def __init__(self, connpool, dbname): - self._connpool = connpool - self.dbname = dbname - - def cursor(self, cursor_factory=tryton_cursor): - conn = self._connpool.getconn() - conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE) - return FakeCursor(self._connpool, conn, self.dbname, - cursor_factory=cursor_factory) - - def close(self): - self._connpool.closeall() - -def db_connect(db_name): - host = CONFIG['db_host'] and "host=%s" % CONFIG['db_host'] or '' - port = CONFIG['db_port'] and "port=%s" % CONFIG['db_port'] or '' - name = "dbname=%s" % db_name - user = CONFIG['db_user'] and "user=%s" % CONFIG['db_user'] or '' - password = CONFIG['db_password'] \ - and "password=%s" % CONFIG['db_password'] or '' - maxconn = int(CONFIG['db_maxconn']) or 64 - dsn = '%s %s %s %s %s' % (host, port, name, user, password) - connpool = ThreadedConnectionPool(0, maxconn, dsn) - return FakeDB(connpool, db_name) - -def init_db(cursor): - sql_file = os.path.join(os.path.dirname(__file__), 'init.sql') - for line in file(sql_file).read().split(';'): - if (len(line)>0) and (not line.isspace()): - cursor.execute(line) - - for i in ('ir', 'workflow', 'res', 'webdav'): - root_path = os.path.dirname(__file__) - tryton_file = os.path.join(root_path, i, '__tryton__.py') - mod_path = os.path.join(root_path, i) - info = eval(file(tryton_file).read()) - active = info.get('active', False) - if active: - state = 'to install' - else: - state = 'uninstalled' - cursor.execute('SELECT NEXTVAL(\'ir_module_module_id_seq\')') - module_id = cursor.fetchone()[0] - cursor.execute('INSERT INTO ir_module_module ' \ - '(id, author, website, name, shortdesc, ' \ - 'description, state) ' \ - 'VALUES (%s, %s, %s, %s, %s, %s, %s)', - (module_id, info.get('author', ''), - info.get('website', ''), i, info.get('name', False), - info.get('description', ''), state)) - dependencies = info.get('depends', []) - for dependency in dependencies: - cursor.execute('INSERT INTO ir_module_module_dependency ' \ - '(module, name) VALUES (%s, %s)', - (module_id, dependency)) - -psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) -psycopg2.extensions.register_adapter(Session, - psycopg2.extensions.AsIs) diff --git a/trytond/web_service/db.py b/trytond/web_service/db.py index 12773bdb69..faf35900d9 100644 --- a/trytond/web_service/db.py +++ b/trytond/web_service/db.py @@ -5,12 +5,11 @@ import logging import threading from trytond.netsvc import Service from trytond import security -from trytond import sql_db +from trytond.backend import Database from trytond import pooler from trytond import tools import base64 import os -from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT import sha @@ -33,23 +32,30 @@ class DB(Service): res = False logger = logging.getLogger('web-service') - database = sql_db.db_connect('template1') - cursor = database.cursor() - cursor.conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) + database = Database().connect() + cursor = database.cursor(autocommit=True) try: try: - cursor.execute('CREATE DATABASE "' + db_name + '" '\ - 'TEMPLATE template0 ENCODING \'unicode\'') + database.create(cursor, db_name) cursor.commit() cursor.close() + database.close() - cursor = sql_db.db_connect(db_name).cursor() - sql_db.init_db(cursor) + database = Database(db_name).connect() + cursor = database.cursor() + database.init(cursor) cursor.commit() cursor.close() + database.close() + cursor = None + database = None + pool = pooler.get_pool(db_name, update_module=True, lang=[lang]) - cursor = sql_db.db_connect(db_name).cursor() + database = Database(db_name).connect() + cursor = database.cursor() + + #XXX replace with model write if lang != 'en_US': cursor.execute('UPDATE ir_lang ' \ 'SET translatable = True ' \ @@ -79,6 +85,8 @@ class DB(Service): finally: if cursor: cursor.close() + if database: + database.close() return res def drop(self, password, db_name): @@ -86,12 +94,11 @@ class DB(Service): pooler.close_db(db_name) logger = logging.getLogger('web-service') - database = sql_db.db_connect('template1') - cursor = database.cursor() - cursor.conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) + database = Database().connect() + cursor = database.cursor(autocommit=True) try: try: - cursor.execute('DROP DATABASE "' + db_name + '"') + database.drop(cursor, db_name) cursor.commit() except: logger.error('DROP DB: %s failed' % (db_name,)) @@ -104,6 +111,7 @@ class DB(Service): logger.info('DROP DB: %s' % (db_name)) finally: cursor.close() + database.close() return True def dump(self, password, db_name): @@ -146,13 +154,12 @@ class DB(Service): 'RESTORE DB: %s doesn\'t work with password' % (db_name,)) raise Exception, "Couldn't restore database with password" - database = sql_db.db_connect('template1') - cursor = database.cursor() - cursor.conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) - cursor.execute('CREATE DATABASE "' + db_name + '" ' \ - 'TEMPLATE template0 ENCODING \'unicode\'') + database = Database().connect() + cursor = database.cursor(autocommit=True) + database.create(cursor, db_name) cursor.commit() cursor.close() + database.close() cmd = ['pg_restore'] if tools.CONFIG['db_user']: @@ -190,62 +197,21 @@ class DB(Service): def db_exist(self, db_name): try: - database = sql_db.db_connect(db_name) + database = Database(db_name).connect() cursor = database.cursor() cursor.close() + database.close() return True except: return False def list(self): - database = sql_db.db_connect('template1') + database = Database().connect() try: cursor = database.cursor() - db_user = tools.CONFIG["db_user"] - if not db_user and os.name == 'posix': - import pwd - db_user = pwd.getpwuid(os.getuid())[0] - if not db_user: - cursor.execute("SELECT usename " \ - "FROM pg_user " \ - "WHERE usesysid = (" \ - "SELECT datdba " \ - "FROM pg_database " \ - "WHERE datname = %s)", - (tools.CONFIG["db_name"],)) - res = cursor.fetchone() - db_user = res and res[0] - if db_user: - cursor.execute("SELECT datname " \ - "FROM pg_database " \ - "WHERE datdba = (" \ - "SELECT usesysid " \ - "FROM pg_user " \ - "WHERE usename=%s) " \ - "AND datname not in " \ - "('template0', 'template1', 'postgres') " \ - "ORDER BY datname", - (db_user,)) - else: - cursor.execute("SELECT datname " \ - "FROM pg_database " \ - "WHERE datname not in " \ - "('template0', 'template1','postgres') " \ - "ORDER BY datname") - res = [] - for db_name, in cursor.fetchall(): - database = pooler.get_db_only(db_name, verbose=False, - blocking=False) - if not database: - continue - cursor2 = database.cursor() - if not cursor2.test(): - cursor2.close() - pooler.close_db(db_name) - else: - cursor2.close() - res.append(db_name) + res = database.list(cursor) cursor.close() + database.close() except: res = [] return res diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py index 015fed0730..823cecc0cd 100644 --- a/trytond/wizard/wizard.py +++ b/trytond/wizard/wizard.py @@ -7,7 +7,7 @@ from xml import dom from trytond.osv import OSV from trytond.config import CONFIG import sys -from trytond.sql_db import IntegrityError +from trytond.backend import DatabaseIntegrityError import traceback import logging @@ -42,7 +42,7 @@ class WizardService(Service): traceback.format_exception(*sys.exc_info())) logging.getLogger("web-service").error( 'Exception in call: ' + tb_s) - if isinstance(exception, IntegrityError): + if isinstance(exception, DatabaseIntegrityError): pool = pooler.get_pool(cursor.dbname) for key in pool._sql_errors.keys(): if key in exception[0]: