Remove sql_db and add database in backend
This commit is contained in:
1 parent
0448226997
commit
f63aaa316d
12 files changed
+443
-303
No files matched your search
@@ -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'],
|
||||
|
||||
@@ -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
|
||||
@@ -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 *
|
||||
@@ -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)
|
||||
File renamed without changes.
+3
-3
@@ -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',
|
||||
|
||||
+4
-3
@@ -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()
|
||||
|
||||
@@ -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]:
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
+31
-65
@@ -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
|
||||
|
||||
@@ -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]:
|
||||
|
||||
Reference in new issue
Block a user