Follow PEP-0249 for Transaction and backends
issue4949 review19781002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Follow PEP-0249 for Database, Transaction and Cursor
|
||||
* Add Python3 support
|
||||
* Make TestCase create and drop its database
|
||||
* Add with_transaction decorator for tests
|
||||
|
||||
@@ -3,6 +3,7 @@ Copyright (C) 2007-2015 Cédric Krier.
|
||||
Copyright (C) 2007-2013 Bertrand Chenal.
|
||||
Copyright (C) 2008-2015 B2CK SPRL.
|
||||
Copyright (C) 2011 Openlabs Technologies & Consulting (P) Ltd.
|
||||
Copyright (C) 2011-2016 Nicolas Évrard.
|
||||
|
||||
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
|
||||
|
||||
+27
-18
@@ -7,20 +7,22 @@ Transaction
|
||||
|
||||
.. class:: Transaction
|
||||
|
||||
This class is a `singleton`_ that contains thread-local parameters of the
|
||||
database transaction.
|
||||
|
||||
.. _`singleton`: http://en.wikipedia.org/wiki/Singleton_pattern
|
||||
|
||||
|
||||
.. attribute:: Transaction.cursor
|
||||
|
||||
The database cursor.
|
||||
This class represents a Tryton transaction that contains thread-local
|
||||
parameters of a database connection. The Transaction instances are
|
||||
`context manager`_ that will commit or rollback the database transaction. In
|
||||
the event of an exception the transaction is rolled back, otherwise it is
|
||||
commited.
|
||||
|
||||
.. attribute:: Transaction.database
|
||||
|
||||
The database.
|
||||
|
||||
.. attribute:: Transaction.readonly
|
||||
|
||||
.. attribute:: Transaction.connection
|
||||
|
||||
The database connection as defined by the `PEP-0249`_.
|
||||
|
||||
.. attribute:: Transaction.user
|
||||
|
||||
The id of the user.
|
||||
@@ -43,14 +45,20 @@ database transaction.
|
||||
|
||||
Count the number of modification made in this transaction.
|
||||
|
||||
.. method:: Transaction.cursor()
|
||||
|
||||
Returns a cursor object using the ``Transaction.connection``.
|
||||
|
||||
.. method:: Transaction.start(database_name, user[, readonly[, context[, close[, autocommit]]]])
|
||||
|
||||
Start a new transaction and return a `context manager`_.
|
||||
|
||||
.. method:: Transaction.stop()
|
||||
|
||||
Stop a started transaction. This method should not be called directly as it
|
||||
will be by the context manager when exiting the `with` statement.
|
||||
Stop a started transaction and pop it from the stack of transactions.
|
||||
|
||||
This method should not be called directly as it will be by the context
|
||||
manager when exiting the `with` statement.
|
||||
|
||||
.. method:: Transaction.set_context(context, \**kwargs)
|
||||
|
||||
@@ -63,15 +71,16 @@ database transaction.
|
||||
`set_context` will put the previous user id in the context to simulate the
|
||||
record rules. The user will be restored when exiting the `with` statement.
|
||||
|
||||
.. method:: Transaction.set_cursor(cursor)
|
||||
.. method:: Transaction.set_current_transaction(transaction)
|
||||
|
||||
Modify the cursor of the transaction and return a `context manager`_. The
|
||||
previous cursor will be restored when exiting the `with` statement.
|
||||
Add a specific ``transaction`` on the top of the transaction stack. A
|
||||
transaction is commited or rollbacked only when its last reference is
|
||||
popped from the stack.
|
||||
|
||||
.. method:: Transaction.new_cursor([autocommit[, readonly]])
|
||||
.. method:: Transaction.new_transaction([autocommit[, readonly]])
|
||||
|
||||
Change the cursor of the transaction with a new one on the same database
|
||||
and return a `context manager`_. The previous cursor will be restored when
|
||||
exiting the `with` statement and the new one will be closed.
|
||||
Create a new transaction with the same database, user and context as the
|
||||
original transaction and adds it to the stack of transactions.
|
||||
|
||||
.. _`context manager`: http://docs.python.org/reference/datamodel.html#context-managers
|
||||
.. _`PEP-0249`: https://www.python.org/dev/peps/pep-0249/
|
||||
|
||||
+39
-109
@@ -1,7 +1,5 @@
|
||||
# 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.config import config
|
||||
|
||||
DatabaseIntegrityError = None
|
||||
DatabaseOperationalError = None
|
||||
|
||||
@@ -11,12 +9,13 @@ class DatabaseInterface(object):
|
||||
Define generic interface for database connection
|
||||
'''
|
||||
flavor = None
|
||||
IN_MAX = 1000
|
||||
|
||||
def __new__(cls, database_name=''):
|
||||
def __new__(cls, name=''):
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self, database_name=''):
|
||||
self.database_name = database_name
|
||||
def __init__(self, name=''):
|
||||
self.name = name
|
||||
|
||||
def connect(self):
|
||||
'''
|
||||
@@ -26,12 +25,18 @@ class DatabaseInterface(object):
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def cursor(self, autocommit=False, readonly=False):
|
||||
'''
|
||||
Retreive a cursor on the database
|
||||
def get_connection(self, autocommit, readonly=False):
|
||||
'''Retrieve a connection on the database
|
||||
|
||||
:param autocommit: a boolean to active autocommit
|
||||
:return: a Cursor
|
||||
:param autocommit: a boolean to activate autocommit
|
||||
:param readonly: a boolean to specify if the transaction is readonly
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def put_connection(self, connection, close=False):
|
||||
'''Release the connection
|
||||
|
||||
:param close: if close is True the connection is discarded
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -41,21 +46,21 @@ class DatabaseInterface(object):
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def create(cursor, database_name):
|
||||
@classmethod
|
||||
def create(cls, connection, database_name):
|
||||
'''
|
||||
Create a database
|
||||
|
||||
:param database_name: the database name
|
||||
:param connection: the connection to the database
|
||||
:param database_name: the new database name
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def drop(cursor, database_name):
|
||||
def drop(self, connection, database_name):
|
||||
'''
|
||||
Drop a database
|
||||
|
||||
:param cursor: a cursor on an other database
|
||||
:param connection: the connection to the database
|
||||
:param database_name: the database name
|
||||
'''
|
||||
raise NotImplementedError
|
||||
@@ -81,8 +86,7 @@ class DatabaseInterface(object):
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def list(cursor):
|
||||
def list(self):
|
||||
'''
|
||||
Get the list of database
|
||||
|
||||
@@ -90,107 +94,60 @@ class DatabaseInterface(object):
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def init(cursor):
|
||||
def init(self):
|
||||
'''
|
||||
Initialize a database
|
||||
|
||||
:param cursor: a cursor on the database
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CursorInterface(object):
|
||||
'''
|
||||
Define generic interface for database cursor
|
||||
'''
|
||||
IN_MAX = 1000
|
||||
cache_keys = {'language', 'fuzzy_translation', '_datetime'}
|
||||
|
||||
def __init__(self):
|
||||
self.cache = {}
|
||||
|
||||
def get_cache(self):
|
||||
from trytond.cache import LRUDict
|
||||
from trytond.transaction import Transaction
|
||||
user = Transaction().user
|
||||
context = Transaction().context
|
||||
keys = tuple(((key, context[key]) for key in sorted(self.cache_keys)
|
||||
if key in context))
|
||||
return self.cache.setdefault((user, keys),
|
||||
LRUDict(config.getint('cache', 'model')))
|
||||
|
||||
def execute(self, sql, params=None):
|
||||
'''
|
||||
Execute a query
|
||||
|
||||
:param sql: a sql query string
|
||||
:param params: a tuple or list of parameters
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def close(self, close=False):
|
||||
'''
|
||||
Close the cursor
|
||||
|
||||
:param close: boolean to not release cursor in pool
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def commit(self):
|
||||
'''
|
||||
Commit the cursor
|
||||
'''
|
||||
for cache in self.cache.itervalues():
|
||||
cache.clear()
|
||||
|
||||
def rollback(self):
|
||||
'''
|
||||
Rollback the cursor
|
||||
'''
|
||||
for cache in self.cache.itervalues():
|
||||
cache.clear()
|
||||
|
||||
def test(self):
|
||||
'''
|
||||
Test if it is a Tryton database.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def nextid(self, table):
|
||||
def nextid(self, connection, table):
|
||||
'''
|
||||
Return the next sequenced id for a table.
|
||||
|
||||
:param connection: a connection on the database
|
||||
:param table: the table name
|
||||
:return: an integer
|
||||
'''
|
||||
|
||||
def setnextid(self, table, value):
|
||||
def setnextid(self, connection, table, value):
|
||||
'''
|
||||
Set the current sequenced id for a table.
|
||||
|
||||
:param connection: a connection on the database
|
||||
:param table: the table name
|
||||
'''
|
||||
|
||||
def currid(self, table):
|
||||
def currid(self, connection, table):
|
||||
'''
|
||||
Return the current sequenced id for a table.
|
||||
|
||||
:param connection: a connection on the database
|
||||
:param table: the table name
|
||||
:return: an integer
|
||||
'''
|
||||
|
||||
def lastid(self):
|
||||
def update_auto_increment(self, connection, table, value):
|
||||
'''
|
||||
Return the last id inserted.
|
||||
Update auto_increment value of table
|
||||
|
||||
:return: an integer
|
||||
:param connection: a connection on the database
|
||||
:param table: the table name
|
||||
:param value: the auto_increment value
|
||||
'''
|
||||
pass
|
||||
|
||||
def lock(self, table):
|
||||
@classmethod
|
||||
def lock(cls, connection, table):
|
||||
'''
|
||||
Lock the table
|
||||
|
||||
:param connection: a connection on the database
|
||||
:param table: the table name
|
||||
'''
|
||||
raise NotImplementedError
|
||||
@@ -203,15 +160,6 @@ class CursorInterface(object):
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def update_auto_increment(self, table, value):
|
||||
'''
|
||||
Update auto_increment value of table
|
||||
|
||||
:param table: the table name
|
||||
:param value: the auto_increment value
|
||||
'''
|
||||
pass
|
||||
|
||||
def has_returning(self):
|
||||
'''
|
||||
Return True if database implements RETURNING clause in INSERT or UPDATE
|
||||
@@ -219,26 +167,8 @@ class CursorInterface(object):
|
||||
|
||||
:return: a boolean
|
||||
'''
|
||||
return False
|
||||
|
||||
def has_multirow_insert(self):
|
||||
'Return True if database supports multirow insert'
|
||||
return False
|
||||
|
||||
def __build_dict(self, row):
|
||||
return dict((desc[0], row[i])
|
||||
for i, desc in enumerate(self.description))
|
||||
|
||||
def dictfetchone(self):
|
||||
row = self.fetchone()
|
||||
if row:
|
||||
return self.__build_dict(row)
|
||||
else:
|
||||
return row
|
||||
|
||||
def dictfetchmany(self, size):
|
||||
rows = self.fetchmany(size)
|
||||
return [self.__build_dict(row) for row in rows]
|
||||
|
||||
def dictfetchall(self):
|
||||
rows = self.fetchall()
|
||||
return [self.__build_dict(row) for row in rows]
|
||||
|
||||
@@ -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.backend.database import DatabaseInterface, CursorInterface
|
||||
from trytond.backend.database import DatabaseInterface
|
||||
from trytond.config import config, parse_uri
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
@@ -15,8 +15,7 @@ import urllib
|
||||
from sql import Flavor, Expression
|
||||
from sql.functions import Extract, Overlay, CharLength
|
||||
|
||||
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError',
|
||||
'Cursor']
|
||||
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
|
||||
|
||||
|
||||
class MySQLExtract(Extract):
|
||||
@@ -82,12 +81,12 @@ class Database(DatabaseInterface):
|
||||
def connect(self):
|
||||
return self
|
||||
|
||||
def cursor(self, autocommit=False, readonly=False):
|
||||
def get_connection(self, autocommit=False, readonly=False):
|
||||
conv = MySQLdb.converters.conversions.copy()
|
||||
conv[float] = lambda value, _: repr(value)
|
||||
conv[MySQLdb.constants.FIELD_TYPE.TIME] = MySQLdb.times.Time_or_None
|
||||
args = {
|
||||
'db': self.database_name,
|
||||
'db': self.name,
|
||||
'sql_mode': 'traditional,postgresql',
|
||||
'use_unicode': True,
|
||||
'charset': 'utf8',
|
||||
@@ -104,21 +103,26 @@ class Database(DatabaseInterface):
|
||||
if uri.password:
|
||||
args['passwd'] = urllib.unquote_plus(uri.password)
|
||||
conn = MySQLdb.connect(**args)
|
||||
cursor = Cursor(conn, self.database_name)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SET time_zone = `UTC`')
|
||||
return cursor
|
||||
return conn
|
||||
|
||||
def put_connection(self, connection, close=False):
|
||||
connection.close()
|
||||
|
||||
def close(self):
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def create(cls, cursor, database_name):
|
||||
def create(cls, connection, database_name):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('CREATE DATABASE `' + database_name + '` '
|
||||
'DEFAULT CHARACTER SET = \'utf8\'')
|
||||
cls._list_cache = None
|
||||
|
||||
@classmethod
|
||||
def drop(cls, cursor, database_name):
|
||||
def drop(cls, connection, database_name):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('DROP DATABASE `' + database_name + '`')
|
||||
cls._list_cache = None
|
||||
|
||||
@@ -195,13 +199,14 @@ class Database(DatabaseInterface):
|
||||
Database._list_cache = None
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def list(cursor):
|
||||
def list(self):
|
||||
now = time.time()
|
||||
timeout = config.getint('session', 'timeout')
|
||||
res = Database._list_cache
|
||||
if res and abs(Database._list_cache_timestamp - now) < timeout:
|
||||
return res
|
||||
conn = self.get_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SHOW DATABASES')
|
||||
res = []
|
||||
for db_name, in cursor.fetchall():
|
||||
@@ -209,20 +214,19 @@ class Database(DatabaseInterface):
|
||||
database = Database(db_name).connect()
|
||||
except Exception:
|
||||
continue
|
||||
cursor2 = database.cursor()
|
||||
if cursor2.test():
|
||||
if database.test():
|
||||
res.append(db_name)
|
||||
cursor2.close(close=True)
|
||||
else:
|
||||
cursor2.close()
|
||||
database.close()
|
||||
database.close()
|
||||
self.put_connection(conn)
|
||||
Database._list_cache = res
|
||||
Database._list_cache_timestamp = now
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def init(cursor):
|
||||
def init(self):
|
||||
from trytond.modules import get_module_info
|
||||
|
||||
connection = self.get_connection()
|
||||
cursor = connection.cursor()
|
||||
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
|
||||
with open(sql_file) as fp:
|
||||
for line in fp.read().split(';'):
|
||||
@@ -246,40 +250,15 @@ class Database(DatabaseInterface):
|
||||
'VALUES (%s, now(), %s, %s)',
|
||||
(0, module_id, dependency))
|
||||
|
||||
|
||||
class Cursor(CursorInterface):
|
||||
|
||||
def __init__(self, conn, database_name):
|
||||
super(Cursor, self).__init__()
|
||||
self._conn = conn
|
||||
self.database_name = database_name
|
||||
self.dbname = self.database_name # XXX to remove
|
||||
self.cursor = conn.cursor()
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.cursor, name)
|
||||
|
||||
def execute(self, sql, params=None):
|
||||
if params:
|
||||
return self.cursor.execute(sql, params)
|
||||
else:
|
||||
return self.cursor.execute(sql)
|
||||
|
||||
def close(self, close=False):
|
||||
self.cursor.close()
|
||||
self.rollback()
|
||||
|
||||
def commit(self):
|
||||
super(Cursor, self).commit()
|
||||
self._conn.commit()
|
||||
|
||||
def rollback(self):
|
||||
super(Cursor, self).rollback()
|
||||
self._conn.rollback()
|
||||
connection.commit()
|
||||
self.put_connection(connection)
|
||||
|
||||
def test(self):
|
||||
self.cursor.execute("SHOW TABLES")
|
||||
for table, in self.cursor.fetchall():
|
||||
is_tryton_database = False
|
||||
connection = self.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SHOW TABLES")
|
||||
for table, in cursor.fetchall():
|
||||
if table in (
|
||||
'ir_model',
|
||||
'ir_model_field',
|
||||
@@ -292,14 +271,17 @@ class Cursor(CursorInterface):
|
||||
'ir_translation',
|
||||
'ir_lang',
|
||||
):
|
||||
return True
|
||||
return False
|
||||
is_tryton_database = True
|
||||
break
|
||||
self.put_connection(connection)
|
||||
return is_tryton_database
|
||||
|
||||
def lastid(self):
|
||||
self.cursor.execute('SELECT LAST_INSERT_ID()')
|
||||
return self.cursor.fetchone()[0]
|
||||
def lastid(self, cursor):
|
||||
# This call is not thread safe
|
||||
cursor.execute('SELECT LAST_INSERT_ID()')
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def lock(self, table):
|
||||
def lock(self, connection, table):
|
||||
# Lock of table doesn't work because MySQL require
|
||||
# that the session locks all tables that will be accessed
|
||||
# but 'FLUSH TABLES WITH READ LOCK' creates deadlock
|
||||
@@ -311,15 +293,7 @@ class Cursor(CursorInterface):
|
||||
def has_multirow_insert(self):
|
||||
return True
|
||||
|
||||
def limit_clause(self, select, limit=None, offset=None):
|
||||
if offset and limit is None:
|
||||
limit = 18446744073709551610 # max bigint
|
||||
if limit is not None:
|
||||
select += ' LIMIT %d' % limit
|
||||
if offset is not None and offset != 0:
|
||||
select += ' OFFSET %d' % offset
|
||||
return select
|
||||
|
||||
def update_auto_increment(self, table, value):
|
||||
self.cursor.execute('ALTER TABLE `%s` AUTO_INCREMENT = %%s' % table,
|
||||
def update_auto_increment(self, connection, table, value):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('ALTER TABLE `%s` AUTO_INCREMENT = %%s' % table,
|
||||
(value,))
|
||||
|
||||
+150
-123
@@ -1,6 +1,7 @@
|
||||
# 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.transaction import Transaction
|
||||
from trytond.backend.table import TableHandlerInterface
|
||||
import logging
|
||||
|
||||
@@ -9,8 +10,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class TableHandler(TableHandlerInterface):
|
||||
|
||||
def __init__(self, cursor, model, module_name=None, history=False):
|
||||
super(TableHandler, self).__init__(cursor, model,
|
||||
def __init__(self, model, module_name=None, history=False):
|
||||
super(TableHandler, self).__init__(model,
|
||||
module_name=module_name, history=history)
|
||||
self._columns = {}
|
||||
self._constraints = []
|
||||
@@ -19,15 +20,16 @@ class TableHandler(TableHandlerInterface):
|
||||
self._field2module = {}
|
||||
self._model = model
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
# Create new table if necessary
|
||||
if not self.table_exist(self.cursor, self.table_name):
|
||||
if not self.table_exist(self.table_name):
|
||||
if not self.history:
|
||||
self.cursor.execute('CREATE TABLE `%s` ('
|
||||
cursor.execute('CREATE TABLE `%s` ('
|
||||
'id BIGINT AUTO_INCREMENT NOT NULL, '
|
||||
'PRIMARY KEY(id)'
|
||||
') ENGINE=InnoDB;' % self.table_name)
|
||||
else:
|
||||
self.cursor.execute('CREATE TABLE `%s` ('
|
||||
cursor.execute('CREATE TABLE `%s` ('
|
||||
'__id BIGINT AUTO_INCREMENT NOT NULL, '
|
||||
'id BIGINT, '
|
||||
'PRIMARY KEY(__id)'
|
||||
@@ -36,56 +38,60 @@ class TableHandler(TableHandlerInterface):
|
||||
self._update_definitions()
|
||||
if 'id' not in self._columns:
|
||||
if not self.history:
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'ADD COLUMN id BIGINT AUTO_INCREMENT '
|
||||
'NOT NULL PRIMARY KEY' % self.table_name)
|
||||
else:
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'ADD COLUMN id BIGINT' % self.table_name)
|
||||
self._update_definitions()
|
||||
if self.history and '__id' not in self._columns:
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'ADD COLUMN __id BIGINT AUTO_INCREMENT '
|
||||
'NOT NULL PRIMARY KEY' % self.table_name)
|
||||
self._update_definitions()
|
||||
|
||||
@staticmethod
|
||||
def table_exist(cursor, table_name):
|
||||
def table_exist(table_name):
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
cursor.execute("SELECT table_name FROM information_schema.tables "
|
||||
"WHERE table_schema = %s AND table_name = %s",
|
||||
(cursor.database_name, table_name))
|
||||
(transaction.database.name, table_name))
|
||||
return bool(cursor.rowcount)
|
||||
|
||||
@staticmethod
|
||||
def table_rename(cursor, old_name, new_name):
|
||||
def table_rename(old_name, new_name):
|
||||
cursor = Transaction().connection.cursor()
|
||||
# Rename table
|
||||
if (TableHandler.table_exist(cursor, old_name)
|
||||
and not TableHandler.table_exist(cursor, new_name)):
|
||||
if (TableHandler.table_exist(old_name)
|
||||
and not TableHandler.table_exist(new_name)):
|
||||
cursor.execute('ALTER TABLE `%s` RENAME TO `%s`'
|
||||
% (old_name, new_name))
|
||||
# Rename history table
|
||||
old_history = old_name + '__history'
|
||||
new_history = new_name + '__history'
|
||||
if (TableHandler.table_exist(cursor, old_history)
|
||||
and not TableHandler.table_exist(cursor, new_history)):
|
||||
if (TableHandler.table_exist(old_history)
|
||||
and not TableHandler.table_exist(new_history)):
|
||||
cursor.execute('ALTER TABLE `%s` RENAME TO `%s`'
|
||||
% (old_history, new_history))
|
||||
|
||||
@staticmethod
|
||||
def sequence_exist(cursor, sequence_name):
|
||||
def sequence_exist(sequence_name):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def sequence_rename(cursor, old_name, new_name):
|
||||
def sequence_rename(old_name, new_name):
|
||||
pass
|
||||
|
||||
def column_exist(self, column_name):
|
||||
return column_name in self._columns
|
||||
|
||||
def column_rename(self, old_name, new_name, exception=False):
|
||||
cursor = Transaction().connection.cursor()
|
||||
if (self.column_exist(old_name)
|
||||
and not self.column_exist(new_name)):
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'RENAME COLUMN `%s` TO `%s`'
|
||||
% (self.table_name, old_name, new_name))
|
||||
self._update_definitions()
|
||||
@@ -96,14 +102,16 @@ class TableHandler(TableHandlerInterface):
|
||||
self.table_name, new_name))
|
||||
|
||||
def _update_definitions(self):
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
# Fetch columns definitions from the table
|
||||
self.cursor.execute("SELECT column_name, character_maximum_length, "
|
||||
cursor.execute("SELECT column_name, character_maximum_length, "
|
||||
"data_type, is_nullable, column_default "
|
||||
"FROM information_schema.columns "
|
||||
"WHERE table_schema = %s AND table_name = %s",
|
||||
(self.cursor.database_name, self.table_name))
|
||||
(transaction.database.name, self.table_name))
|
||||
self._columns = {}
|
||||
for line in self.cursor.fetchall():
|
||||
for line in cursor.fetchall():
|
||||
column, size, typname, nullable, default = line
|
||||
self._columns[column] = {
|
||||
'size': size,
|
||||
@@ -113,13 +121,13 @@ class TableHandler(TableHandlerInterface):
|
||||
}
|
||||
|
||||
# fetch constraints for the table
|
||||
self.cursor.execute("SELECT constraint_name, constraint_type "
|
||||
cursor.execute("SELECT constraint_name, constraint_type "
|
||||
"FROM information_schema.table_constraints "
|
||||
"WHERE table_schema = %s AND table_name = %s",
|
||||
(self.cursor.database_name, self.table_name))
|
||||
(transaction.database.name, self.table_name))
|
||||
self._constraints = []
|
||||
self._fkeys = []
|
||||
for line in self.cursor.fetchall():
|
||||
for line in cursor.fetchall():
|
||||
conname, contype = line
|
||||
if contype not in ('PRIMARY KEY', 'FOREIGN KEY'):
|
||||
self._constraints.append(conname)
|
||||
@@ -127,37 +135,40 @@ class TableHandler(TableHandlerInterface):
|
||||
self._fkeys.append(conname)
|
||||
|
||||
# Fetch indexes defined for the table
|
||||
self.cursor.execute('SHOW INDEXES FROM `%s`' % self.table_name)
|
||||
self._indexes = list(set(x[2] for x in self.cursor.fetchall()
|
||||
cursor.execute('SHOW INDEXES FROM `%s`' % self.table_name)
|
||||
self._indexes = list(set(x[2] for x in cursor.fetchall()
|
||||
if x[2] != 'PRIMARY'))
|
||||
|
||||
# Keep track of which module created each field
|
||||
self._field2module = {}
|
||||
if self.object_name is not None:
|
||||
self.cursor.execute('SELECT f.name, f.module '
|
||||
cursor.execute('SELECT f.name, f.module '
|
||||
'FROM ir_model_field f '
|
||||
'JOIN ir_model m on (f.model=m.id) '
|
||||
'WHERE m.model = %s',
|
||||
(self.object_name,))
|
||||
for line in self.cursor.fetchall():
|
||||
for line in cursor.fetchall():
|
||||
self._field2module[line[0]] = line[1]
|
||||
|
||||
def alter_size(self, column_name, column_type):
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'MODIFY COLUMN `%s` %s'
|
||||
% (self.table_name, column_name,
|
||||
self._column_definition(column_name)))
|
||||
self._update_definitions()
|
||||
|
||||
def alter_type(self, column_name, column_type):
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'MODIFY COLUMN `%s` %s'
|
||||
% (self.table_name, column_name,
|
||||
self._column_definition(column_name, typname=column_type)))
|
||||
self._update_definitions()
|
||||
|
||||
def db_default(self, column_name, value):
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'MODIFY COLUMN `%s` %s'
|
||||
% (self.table_name, column_name,
|
||||
self._column_definition(column_name, default=value)))
|
||||
@@ -209,19 +220,20 @@ class TableHandler(TableHandlerInterface):
|
||||
field_size)
|
||||
return
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
column_type = column_type[1]
|
||||
self.cursor.execute('ALTER TABLE `%s` ADD COLUMN `%s` %s' %
|
||||
cursor.execute('ALTER TABLE `%s` ADD COLUMN `%s` %s' %
|
||||
(self.table_name, column_name, column_type))
|
||||
|
||||
if column_format:
|
||||
# check if table is non-empty:
|
||||
self.cursor.execute('SELECT 1 FROM `%s` limit 1' % self.table_name)
|
||||
if self.cursor.rowcount:
|
||||
cursor.execute('SELECT 1 FROM `%s` limit 1' % self.table_name)
|
||||
if cursor.rowcount:
|
||||
# Populate column with default values:
|
||||
default = None
|
||||
if default_fun is not None:
|
||||
default = default_fun()
|
||||
self.cursor.execute('UPDATE `' + self.table_name + '` '
|
||||
cursor.execute('UPDATE `' + self.table_name + '` '
|
||||
'SET `' + column_name + '` = %s',
|
||||
(column_format(default),))
|
||||
|
||||
@@ -233,17 +245,20 @@ class TableHandler(TableHandlerInterface):
|
||||
conname = '%s_%s_fkey' % (self.table_name, column_name)
|
||||
if conname in self._fkeys:
|
||||
self.drop_fk(column_name)
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'ADD CONSTRAINT `%s` FOREIGN KEY (`%s`) '
|
||||
'REFERENCES `%s` (id) ON DELETE %s'
|
||||
% (self.table_name, conname, column_name, reference, on_delete))
|
||||
% (self.table_name, conname, column_name, reference,
|
||||
on_delete))
|
||||
self._update_definitions()
|
||||
|
||||
def drop_fk(self, column_name, table=None):
|
||||
conname = '%s_%s_fkey' % (self.table_name, column_name)
|
||||
if conname not in self._fkeys:
|
||||
return
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'DROP FOREIGN KEY `%s`' % (self.table_name, conname))
|
||||
self._update_definitions()
|
||||
|
||||
@@ -260,123 +275,135 @@ class TableHandler(TableHandlerInterface):
|
||||
if self._columns[k]['typname'] in ('text', 'blob'):
|
||||
return
|
||||
|
||||
if action == 'add':
|
||||
if index_name in self._indexes:
|
||||
return
|
||||
self.cursor.execute('CREATE INDEX `' + index_name + '` '
|
||||
'ON `' + self.table_name + '` '
|
||||
'( ' + ','.join(['`' + x + '`' for x in column_name]) + ')')
|
||||
self._update_definitions()
|
||||
elif action == 'remove':
|
||||
if len(column_name) == 1:
|
||||
if (self._field2module.get(column_name[0], self.module_name)
|
||||
!= self.module_name):
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
if action == 'add':
|
||||
if index_name in self._indexes:
|
||||
return
|
||||
|
||||
if index_name in self._indexes:
|
||||
self.cursor.execute('DROP INDEX `%s` ON `%s`'
|
||||
% (index_name, self.table_name))
|
||||
cursor.execute('CREATE INDEX `' + index_name + '` '
|
||||
'ON `' + self.table_name
|
||||
+ '` ( '
|
||||
+ ','.join(['`' + x + '`' for x in column_name])
|
||||
+ ')')
|
||||
self._update_definitions()
|
||||
else:
|
||||
raise Exception('Index action not supported!')
|
||||
elif action == 'remove':
|
||||
if len(column_name) == 1:
|
||||
if (self._field2module.get(column_name[0],
|
||||
self.module_name) != self.module_name):
|
||||
return
|
||||
|
||||
if index_name in self._indexes:
|
||||
cursor.execute('DROP INDEX `%s` ON `%s`'
|
||||
% (index_name, self.table_name))
|
||||
self._update_definitions()
|
||||
else:
|
||||
raise Exception('Index action not supported!')
|
||||
|
||||
def not_null_action(self, column_name, action='add'):
|
||||
if not self.column_exist(column_name):
|
||||
return
|
||||
|
||||
if action == 'add':
|
||||
if not self._columns[column_name]['nullable']:
|
||||
return
|
||||
self.cursor.execute('SELECT id FROM `%s` '
|
||||
'WHERE `%s` IS NULL'
|
||||
% (self.table_name, column_name))
|
||||
if not self.cursor.rowcount:
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
if action == 'add':
|
||||
if not self._columns[column_name]['nullable']:
|
||||
return
|
||||
cursor.execute('SELECT id FROM `%s` '
|
||||
'WHERE `%s` IS NULL'
|
||||
% (self.table_name, column_name))
|
||||
if not cursor.rowcount:
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'MODIFY COLUMN `%s` %s'
|
||||
% (self.table_name, column_name,
|
||||
self._column_definition(column_name,
|
||||
nullable=False)))
|
||||
self._update_definitions()
|
||||
else:
|
||||
logger.warning(
|
||||
'Unable to set column %s '
|
||||
'of table %s not null !\n'
|
||||
'Try to re-run: '
|
||||
'trytond.py --update=module\n'
|
||||
'If it doesn\'t work, update records '
|
||||
'and execute manually:\n'
|
||||
'ALTER TABLE `%s` MODIFY COLUMN `%s` %s',
|
||||
column_name, self.table_name, self.table_name,
|
||||
column_name, self._column_definition(column_name,
|
||||
nullable=False))
|
||||
elif action == 'remove':
|
||||
if self._columns[column_name]['nullable']:
|
||||
return
|
||||
if (self._field2module.get(column_name, self.module_name)
|
||||
!= self.module_name):
|
||||
return
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'MODIFY COLUMN `%s` %s'
|
||||
% (self.table_name, column_name,
|
||||
self._column_definition(column_name, nullable=False)))
|
||||
self._column_definition(column_name, nullable=True)))
|
||||
self._update_definitions()
|
||||
else:
|
||||
logger.warning(
|
||||
'Unable to set column %s '
|
||||
'of table %s not null !\n'
|
||||
'Try to re-run: '
|
||||
'trytond.py --update=module\n'
|
||||
'If it doesn\'t work, update records '
|
||||
'and execute manually:\n'
|
||||
'ALTER TABLE `%s` MODIFY COLUMN `%s` %s',
|
||||
column_name, self.table_name, self.table_name,
|
||||
column_name, self._column_definition(column_name,
|
||||
nullable=False))
|
||||
elif action == 'remove':
|
||||
if self._columns[column_name]['nullable']:
|
||||
return
|
||||
if (self._field2module.get(column_name, self.module_name)
|
||||
!= self.module_name):
|
||||
return
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
'MODIFY COLUMN `%s` %s'
|
||||
% (self.table_name, column_name,
|
||||
self._column_definition(column_name, nullable=True)))
|
||||
self._update_definitions()
|
||||
else:
|
||||
raise Exception('Not null action not supported!')
|
||||
raise Exception('Not null action not supported!')
|
||||
|
||||
def add_constraint(self, ident, constraint, exception=False):
|
||||
ident = self.table_name + "_" + ident
|
||||
if ident in self._constraints:
|
||||
# This constrain already exists
|
||||
return
|
||||
try:
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
'ADD CONSTRAINT `%s` %s'
|
||||
% (self.table_name, ident, constraint), constraint.params)
|
||||
except Exception:
|
||||
if exception:
|
||||
raise
|
||||
logger.warning(
|
||||
'unable to add \'%s\' constraint on table %s !\n'
|
||||
'If you want to have it, you should update the records '
|
||||
'and execute manually:\n'
|
||||
'ALTER table `%s` ADD CONSTRAINT `%s` %s',
|
||||
constraint, self.table_name, self.table_name, ident,
|
||||
constraint, exc_info=True)
|
||||
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'ADD CONSTRAINT `%s` %s'
|
||||
% (self.table_name, ident, constraint), constraint.params)
|
||||
except Exception:
|
||||
if exception:
|
||||
raise
|
||||
logger.warning(
|
||||
'unable to add \'%s\' constraint on table %s !\n'
|
||||
'If you want to have it, you should update the records '
|
||||
'and execute manually:\n'
|
||||
'ALTER table `%s` ADD CONSTRAINT `%s` %s',
|
||||
constraint, self.table_name, self.table_name, ident,
|
||||
constraint, exc_info=True)
|
||||
self._update_definitions()
|
||||
|
||||
def drop_constraint(self, ident, exception=False, table=None):
|
||||
ident = (table or self.table_name) + "_" + ident
|
||||
if ident not in self._constraints:
|
||||
return
|
||||
try:
|
||||
self.cursor.execute('ALTER TABLE `%s` '
|
||||
'DROP CONSTRAINT `%s`'
|
||||
% (self.table_name, ident))
|
||||
except Exception:
|
||||
if exception:
|
||||
raise
|
||||
logger.warning(
|
||||
'unable to drop \'%s\' constraint on table %s!',
|
||||
ident, self.table_name)
|
||||
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('ALTER TABLE `%s` '
|
||||
'DROP CONSTRAINT `%s`'
|
||||
% (self.table_name, ident))
|
||||
except Exception:
|
||||
if exception:
|
||||
raise
|
||||
logger.warning(
|
||||
'unable to drop \'%s\' constraint on table %s!',
|
||||
ident, self.table_name)
|
||||
self._update_definitions()
|
||||
|
||||
def drop_column(self, column_name, exception=False):
|
||||
if not self.column_exist(column_name):
|
||||
return
|
||||
try:
|
||||
self.cursor.execute(
|
||||
'ALTER TABLE `%s` DROP COLUMN `%s`' %
|
||||
(self.table_name, column_name))
|
||||
|
||||
except Exception:
|
||||
if exception:
|
||||
raise
|
||||
logger.warning(
|
||||
'unable to drop \'%s\' column on table %s!',
|
||||
column_name, self.table_name)
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute(
|
||||
'ALTER TABLE `%s` DROP COLUMN `%s`' %
|
||||
(self.table_name, column_name))
|
||||
|
||||
except Exception:
|
||||
if exception:
|
||||
raise
|
||||
logger.warning(
|
||||
'unable to drop \'%s\' column on table %s!',
|
||||
column_name, self.table_name)
|
||||
self._update_definitions()
|
||||
|
||||
@staticmethod
|
||||
def drop_table(cursor, model, table, cascade=False):
|
||||
def drop_table(model, table, cascade=False):
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('DELETE from ir_model_data where '
|
||||
'model = %s', model)
|
||||
|
||||
|
||||
@@ -26,11 +26,10 @@ from psycopg2 import OperationalError as DatabaseOperationalError
|
||||
|
||||
from sql import Flavor
|
||||
|
||||
from trytond.backend.database import DatabaseInterface, CursorInterface
|
||||
from trytond.backend.database import DatabaseInterface
|
||||
from trytond.config import config, parse_uri
|
||||
|
||||
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError',
|
||||
'Cursor']
|
||||
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -60,24 +59,26 @@ class Database(DatabaseInterface):
|
||||
_version_cache = {}
|
||||
flavor = Flavor(ilike=True)
|
||||
|
||||
def __new__(cls, database_name='template1'):
|
||||
if database_name in cls._databases:
|
||||
return cls._databases[database_name]
|
||||
return DatabaseInterface.__new__(cls, database_name=database_name)
|
||||
def __new__(cls, name='template1'):
|
||||
if name in cls._databases:
|
||||
return cls._databases[name]
|
||||
return DatabaseInterface.__new__(cls, name=name)
|
||||
|
||||
def __init__(self, database_name='template1'):
|
||||
super(Database, self).__init__(database_name=database_name)
|
||||
self._databases.setdefault(database_name, self)
|
||||
def __init__(self, name='template1'):
|
||||
super(Database, self).__init__(name=name)
|
||||
self._databases.setdefault(name, self)
|
||||
self._search_path = None
|
||||
self._current_user = None
|
||||
|
||||
def connect(self):
|
||||
if self._connpool is not None:
|
||||
return self
|
||||
logger.info('connect to "%s"', self.database_name)
|
||||
logger.info('connect to "%s"', self.name)
|
||||
uri = parse_uri(config.get('database', 'uri'))
|
||||
assert uri.scheme == 'postgresql'
|
||||
host = uri.hostname and "host=%s" % uri.hostname or ''
|
||||
port = uri.port and "port=%s" % uri.port or ''
|
||||
name = "dbname=%s" % self.database_name
|
||||
name = "dbname=%s" % self.name
|
||||
user = uri.username and "user=%s" % uri.username or ''
|
||||
password = ("password=%s" % urllib.unquote_plus(uri.password)
|
||||
if uri.password else '')
|
||||
@@ -87,7 +88,7 @@ class Database(DatabaseInterface):
|
||||
self._connpool = ThreadedConnectionPool(minconn, maxconn, dsn)
|
||||
return self
|
||||
|
||||
def cursor(self, autocommit=False, readonly=False):
|
||||
def get_connection(self, autocommit=False, readonly=False):
|
||||
if self._connpool is None:
|
||||
self.connect()
|
||||
conn = self._connpool.getconn()
|
||||
@@ -95,10 +96,13 @@ class Database(DatabaseInterface):
|
||||
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
else:
|
||||
conn.set_isolation_level(ISOLATION_LEVEL_REPEATABLE_READ)
|
||||
cursor = Cursor(self._connpool, conn, self)
|
||||
if readonly:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SET TRANSACTION READ ONLY')
|
||||
return cursor
|
||||
return conn
|
||||
|
||||
def put_connection(self, connection, close=False):
|
||||
self._connpool.putconn(connection, close=close)
|
||||
|
||||
def close(self):
|
||||
if self._connpool is None:
|
||||
@@ -107,23 +111,26 @@ class Database(DatabaseInterface):
|
||||
self._connpool = None
|
||||
|
||||
@classmethod
|
||||
def create(cls, cursor, database_name):
|
||||
def create(cls, connection, database_name):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('CREATE DATABASE "' + database_name + '" '
|
||||
'TEMPLATE template0 ENCODING \'unicode\'')
|
||||
connection.commit()
|
||||
cls._list_cache = None
|
||||
|
||||
@classmethod
|
||||
def drop(cls, cursor, database_name):
|
||||
def drop(self, connection, database_name):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('DROP DATABASE "' + database_name + '"')
|
||||
cls._list_cache = None
|
||||
Database._list_cache = None
|
||||
|
||||
def get_version(self, cursor):
|
||||
if self.database_name not in self._version_cache:
|
||||
def get_version(self, connection):
|
||||
if self.name not in self._version_cache:
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT version()')
|
||||
version, = cursor.fetchone()
|
||||
self._version_cache[self.database_name] = tuple(map(int,
|
||||
self._version_cache[self.name] = tuple(map(int,
|
||||
RE_VERSION.search(version).groups()))
|
||||
return self._version_cache[self.database_name]
|
||||
return self._version_cache[self.name]
|
||||
|
||||
@staticmethod
|
||||
def dump(database_name):
|
||||
@@ -158,10 +165,8 @@ class Database(DatabaseInterface):
|
||||
from trytond.tools import exec_command_pipe
|
||||
|
||||
database = Database().connect()
|
||||
cursor = database.cursor(autocommit=True)
|
||||
database.create(cursor, database_name)
|
||||
cursor.commit()
|
||||
cursor.close()
|
||||
connection = database.get_connection(autocommit=True)
|
||||
database.create(connection, database_name)
|
||||
database.close()
|
||||
|
||||
cmd = ['pg_restore', '--no-owner']
|
||||
@@ -195,8 +200,8 @@ class Database(DatabaseInterface):
|
||||
raise Exception('Couldn\'t restore database')
|
||||
|
||||
database = Database(database_name).connect()
|
||||
cursor = database.cursor()
|
||||
if not cursor.test():
|
||||
cursor = database.get_connection().cursor()
|
||||
if not database.test():
|
||||
cursor.close()
|
||||
database.close()
|
||||
raise Exception('Couldn\'t restore database!')
|
||||
@@ -205,35 +210,38 @@ class Database(DatabaseInterface):
|
||||
Database._list_cache = None
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def list(cursor):
|
||||
def list(self):
|
||||
now = time.time()
|
||||
timeout = config.getint('session', 'timeout')
|
||||
res = Database._list_cache
|
||||
if res and abs(Database._list_cache_timestamp - now) < timeout:
|
||||
return res
|
||||
|
||||
connection = self.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT datname FROM pg_database '
|
||||
'WHERE datistemplate = false ORDER BY datname')
|
||||
res = []
|
||||
for db_name, in cursor.fetchall():
|
||||
for db_name, in cursor:
|
||||
try:
|
||||
database = Database(db_name).connect()
|
||||
except Exception:
|
||||
continue
|
||||
cursor2 = database.cursor()
|
||||
if cursor2.test():
|
||||
if database.test():
|
||||
res.append(db_name)
|
||||
cursor2.close(close=True)
|
||||
else:
|
||||
cursor2.close(close=True)
|
||||
database.close()
|
||||
self.put_connection(connection)
|
||||
|
||||
Database._list_cache = res
|
||||
Database._list_cache_timestamp = now
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def init(cursor):
|
||||
def init(self):
|
||||
from trytond.modules import get_module_info
|
||||
|
||||
connection = self.get_connection()
|
||||
cursor = connection.cursor()
|
||||
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
|
||||
with open(sql_file) as fp:
|
||||
for line in fp.read().split(';'):
|
||||
@@ -257,108 +265,72 @@ class Database(DatabaseInterface):
|
||||
'VALUES (%s, now(), %s, %s)',
|
||||
(0, module_id, dependency))
|
||||
|
||||
|
||||
class Cursor(CursorInterface):
|
||||
|
||||
def __init__(self, connpool, conn, database):
|
||||
super(Cursor, self).__init__()
|
||||
self._connpool = connpool
|
||||
self._conn = conn
|
||||
self._database = database
|
||||
self._current_user = None
|
||||
self._search_path = None
|
||||
self.cursor = conn.cursor()
|
||||
self.commit()
|
||||
self.sql_from_log = {}
|
||||
self.sql_into_log = {}
|
||||
self.count = {
|
||||
'from': 0,
|
||||
'into': 0,
|
||||
}
|
||||
|
||||
@property
|
||||
def database_name(self):
|
||||
return self._database.database_name
|
||||
|
||||
# TODO to remove
|
||||
@property
|
||||
def dbname(self):
|
||||
return self.database_name
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.cursor, name)
|
||||
|
||||
def execute(self, sql, params=None):
|
||||
if params:
|
||||
return self.cursor.execute(sql, params)
|
||||
else:
|
||||
return self.cursor.execute(sql)
|
||||
|
||||
def close(self, close=False):
|
||||
self.cursor.close()
|
||||
self.rollback()
|
||||
self._connpool.putconn(self._conn, close=close)
|
||||
|
||||
def commit(self):
|
||||
super(Cursor, self).commit()
|
||||
self._conn.commit()
|
||||
|
||||
def rollback(self):
|
||||
super(Cursor, self).rollback()
|
||||
self._conn.rollback()
|
||||
connection.commit()
|
||||
self.put_connection(connection)
|
||||
|
||||
def test(self):
|
||||
self.cursor.execute('SELECT 1 FROM information_schema.tables '
|
||||
connection = self.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT 1 FROM information_schema.tables '
|
||||
'WHERE table_name IN %s',
|
||||
(('ir_model', 'ir_model_field', 'ir_ui_view', 'ir_ui_menu',
|
||||
'res_user', 'res_group', 'ir_module',
|
||||
'ir_module_dependency', 'ir_translation', 'ir_lang'),))
|
||||
return len(self.cursor.fetchall()) != 0
|
||||
'ir_module_dependency', 'ir_translation',
|
||||
'ir_lang'),))
|
||||
is_tryton_database = len(cursor.fetchall()) != 0
|
||||
self.put_connection(connection)
|
||||
return is_tryton_database
|
||||
|
||||
def nextid(self, table):
|
||||
self.cursor.execute("SELECT NEXTVAL('" + table + "_id_seq')")
|
||||
return self.cursor.fetchone()[0]
|
||||
def nextid(self, connection, table):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT NEXTVAL('" + table + "_id_seq')")
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def setnextid(self, table, value):
|
||||
self.cursor.execute("SELECT SETVAL('" + table + "_id_seq', %d)"
|
||||
% value)
|
||||
def setnextid(self, connection, table, value):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT SETVAL('" + table + "_id_seq', %d)" % value)
|
||||
|
||||
def currid(self, table):
|
||||
self.cursor.execute('SELECT last_value FROM "' + table + '_id_seq"')
|
||||
return self.cursor.fetchone()[0]
|
||||
def currid(self, connection, table):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT last_value FROM "' + table + '_id_seq"')
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def lock(self, table):
|
||||
self.cursor.execute('LOCK "%s" IN EXCLUSIVE MODE NOWAIT' % table)
|
||||
def lock(self, connection, table):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('LOCK "%s" IN EXCLUSIVE MODE NOWAIT' % table)
|
||||
|
||||
def has_constraint(self):
|
||||
return True
|
||||
|
||||
def limit_clause(self, select, limit=None, offset=None):
|
||||
if limit is not None:
|
||||
select += ' LIMIT %d' % limit
|
||||
if offset is not None and offset != 0:
|
||||
select += ' OFFSET %d' % offset
|
||||
return select
|
||||
|
||||
def has_returning(self):
|
||||
# RETURNING clause is available since PostgreSQL 8.2
|
||||
return self._database.get_version(self) >= (8, 2)
|
||||
|
||||
def has_multirow_insert(self):
|
||||
return True
|
||||
|
||||
def get_table_schema(self, connection, table_name):
|
||||
cursor = connection.cursor()
|
||||
for schema in self.search_path:
|
||||
cursor.execute('SELECT 1 '
|
||||
'FROM information_schema.tables '
|
||||
'WHERE table_name = %s AND table_schema = %s',
|
||||
(table_name, schema))
|
||||
if cursor.rowcount:
|
||||
return schema
|
||||
|
||||
@property
|
||||
def current_user(self):
|
||||
if self._current_user is None:
|
||||
self.execute('SELECT current_user')
|
||||
self._current_user, = self.fetchone()
|
||||
connection = self.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT current_user')
|
||||
self._current_user = cursor.fetchone()[0]
|
||||
return self._current_user
|
||||
|
||||
@property
|
||||
def search_path(self):
|
||||
if self._search_path is None:
|
||||
self.execute('SHOW search_path')
|
||||
path, = self.fetchone()
|
||||
connection = self.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SHOW search_path')
|
||||
path, = cursor.fetchone()
|
||||
special_values = {
|
||||
'user': self.current_user,
|
||||
}
|
||||
|
||||
+133
-120
@@ -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.transaction import Transaction
|
||||
from trytond.backend.table import TableHandlerInterface
|
||||
import logging
|
||||
|
||||
@@ -11,8 +11,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class TableHandler(TableHandlerInterface):
|
||||
|
||||
def __init__(self, cursor, model, module_name=None, history=False):
|
||||
super(TableHandler, self).__init__(cursor, model,
|
||||
def __init__(self, model, module_name=None, history=False):
|
||||
super(TableHandler, self).__init__(model,
|
||||
module_name=module_name, history=history)
|
||||
self._columns = {}
|
||||
self._constraints = []
|
||||
@@ -20,93 +20,91 @@ class TableHandler(TableHandlerInterface):
|
||||
self._indexes = []
|
||||
self._field2module = {}
|
||||
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
# Create sequence if necessary
|
||||
if not self.sequence_exist(self.cursor, self.sequence_name):
|
||||
self.cursor.execute('CREATE SEQUENCE "%s"' % self.sequence_name)
|
||||
if not self.sequence_exist(self.sequence_name):
|
||||
cursor.execute('CREATE SEQUENCE "%s"' % self.sequence_name)
|
||||
|
||||
# Create new table if necessary
|
||||
if not self.table_exist(self.cursor, self.table_name):
|
||||
self.cursor.execute('CREATE TABLE "%s" ()' % self.table_name)
|
||||
self.table_schema = self.get_table_schema(self.cursor, self.table_name)
|
||||
if not self.table_exist(self.table_name):
|
||||
cursor.execute('CREATE TABLE "%s" ()' % self.table_name)
|
||||
self.table_schema = transaction.database.get_table_schema(
|
||||
transaction.connection, self.table_name)
|
||||
|
||||
self.cursor.execute('SELECT tableowner = current_user FROM pg_tables '
|
||||
cursor.execute('SELECT tableowner = current_user FROM pg_tables '
|
||||
'WHERE tablename = %s AND schemaname = %s',
|
||||
(self.table_name, self.table_schema))
|
||||
self.is_owner, = self.cursor.fetchone()
|
||||
self.is_owner, = cursor.fetchone()
|
||||
|
||||
if model.__doc__ and self.is_owner:
|
||||
self.cursor.execute('COMMENT ON TABLE "%s" IS \'%s\'' %
|
||||
cursor.execute('COMMENT ON TABLE "%s" IS \'%s\'' %
|
||||
(self.table_name, model.__doc__.replace("'", "''")))
|
||||
|
||||
self._update_definitions()
|
||||
if 'id' not in self._columns:
|
||||
if not self.history:
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ADD COLUMN id INTEGER '
|
||||
'DEFAULT nextval(\'"%s"\') NOT NULL'
|
||||
% (self.table_name, self.sequence_name))
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ADD PRIMARY KEY(id)' % self.table_name)
|
||||
else:
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ADD COLUMN id INTEGER' % self.table_name)
|
||||
self._update_definitions()
|
||||
if self.history and '__id' not in self._columns:
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ADD COLUMN __id INTEGER '
|
||||
'DEFAULT nextval(\'"%s"\') NOT NULL' %
|
||||
(self.table_name, self.sequence_name))
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ADD PRIMARY KEY(__id)' % self.table_name)
|
||||
else:
|
||||
default = "nextval('%s'::regclass)" % self.sequence_name
|
||||
if self.history:
|
||||
if self._columns['__id']['default'] != default:
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ALTER __id SET DEFAULT %s'
|
||||
% (self.table_name, default))
|
||||
if self._columns['id']['default'] != default:
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ALTER id SET DEFAULT %s'
|
||||
% (self.table_name, default))
|
||||
self._update_definitions()
|
||||
|
||||
@classmethod
|
||||
def get_table_schema(cls, cursor, table_name):
|
||||
for schema in cursor.search_path:
|
||||
cursor.execute('SELECT 1 '
|
||||
'FROM information_schema.tables '
|
||||
'WHERE table_name = %s AND table_schema = %s',
|
||||
(table_name, schema))
|
||||
if cursor.rowcount:
|
||||
return schema
|
||||
|
||||
@classmethod
|
||||
def table_exist(cls, cursor, table_name):
|
||||
return bool(cls.get_table_schema(cursor, table_name))
|
||||
@staticmethod
|
||||
def table_exist(table_name):
|
||||
transaction = Transaction()
|
||||
return bool(transaction.database.get_table_schema(
|
||||
transaction.connection, table_name))
|
||||
|
||||
@staticmethod
|
||||
def table_rename(cursor, old_name, new_name):
|
||||
def table_rename(old_name, new_name):
|
||||
cursor = Transaction().connection.cursor()
|
||||
# Rename table
|
||||
if (TableHandler.table_exist(cursor, old_name)
|
||||
and not TableHandler.table_exist(cursor, new_name)):
|
||||
if (TableHandler.table_exist(old_name)
|
||||
and not TableHandler.table_exist(new_name)):
|
||||
cursor.execute('ALTER TABLE "%s" RENAME TO "%s"'
|
||||
% (old_name, new_name))
|
||||
# Rename sequence
|
||||
old_sequence = old_name + '_id_seq'
|
||||
new_sequence = new_name + '_id_seq'
|
||||
TableHandler.sequence_rename(cursor, old_sequence, new_sequence)
|
||||
TableHandler.sequence_rename(old_sequence, new_sequence)
|
||||
# Rename history table
|
||||
old_history = old_name + "__history"
|
||||
new_history = new_name + "__history"
|
||||
if (TableHandler.table_exist(cursor, old_history)
|
||||
and not TableHandler.table_exist(cursor, new_history)):
|
||||
if (TableHandler.table_exist(old_history)
|
||||
and not TableHandler.table_exist(new_history)):
|
||||
cursor.execute('ALTER TABLE "%s" RENAME TO "%s"'
|
||||
% (old_history, new_history))
|
||||
|
||||
@classmethod
|
||||
def sequence_schema(cls, cursor, sequence_name):
|
||||
for schema in cursor.search_path:
|
||||
def sequence_schema(cls, sequence_name):
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
for schema in transaction.database.search_path:
|
||||
cursor.execute('SELECT 1 '
|
||||
'FROM information_schema.sequences '
|
||||
'WHERE sequence_name = %s AND sequence_schema = %s',
|
||||
@@ -115,13 +113,14 @@ class TableHandler(TableHandlerInterface):
|
||||
return schema
|
||||
|
||||
@classmethod
|
||||
def sequence_exist(cls, cursor, sequence_name):
|
||||
return bool(cls.sequence_schema(cursor, sequence_name))
|
||||
def sequence_exist(cls, sequence_name):
|
||||
return bool(cls.sequence_schema(sequence_name))
|
||||
|
||||
@staticmethod
|
||||
def sequence_rename(cursor, old_name, new_name):
|
||||
if (TableHandler.sequence_exist(cursor, old_name)
|
||||
and not TableHandler.sequence_exist(cursor, new_name)):
|
||||
def sequence_rename(old_name, new_name):
|
||||
cursor = Transaction().connection.cursor()
|
||||
if (TableHandler.sequence_exist(old_name)
|
||||
and not TableHandler.sequence_exist(new_name)):
|
||||
cursor.execute('ALTER TABLE "%s" RENAME TO "%s"'
|
||||
% (old_name, new_name))
|
||||
|
||||
@@ -129,9 +128,10 @@ class TableHandler(TableHandlerInterface):
|
||||
return column_name in self._columns
|
||||
|
||||
def column_rename(self, old_name, new_name, exception=False):
|
||||
cursor = Transaction().connection.cursor()
|
||||
if (self.column_exist(old_name)
|
||||
and not self.column_exist(new_name)):
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'RENAME COLUMN "%s" TO "%s"'
|
||||
% (self.table_name, old_name, new_name))
|
||||
self._update_definitions()
|
||||
@@ -142,15 +142,16 @@ class TableHandler(TableHandlerInterface):
|
||||
self.table_name, new_name))
|
||||
|
||||
def _update_definitions(self):
|
||||
cursor = Transaction().connection.cursor()
|
||||
self._columns = {}
|
||||
# Fetch columns definitions from the table
|
||||
self.cursor.execute('SELECT '
|
||||
cursor.execute('SELECT '
|
||||
'column_name, udt_name, is_nullable, character_maximum_length, '
|
||||
'column_default '
|
||||
'FROM information_schema.columns '
|
||||
'WHERE table_name = %s AND table_schema = %s',
|
||||
(self.table_name, self.table_schema))
|
||||
for column, typname, nullable, size, default in self.cursor.fetchall():
|
||||
for column, typname, nullable, size, default in cursor.fetchall():
|
||||
self._columns[column] = {
|
||||
'typname': typname,
|
||||
'notnull': True if nullable == 'NO' else False,
|
||||
@@ -159,60 +160,61 @@ class TableHandler(TableHandlerInterface):
|
||||
}
|
||||
|
||||
# fetch constraints for the table
|
||||
self.cursor.execute('SELECT constraint_name '
|
||||
cursor.execute('SELECT constraint_name '
|
||||
'FROM information_schema.table_constraints '
|
||||
'WHERE table_name = %s AND table_schema = %s',
|
||||
(self.table_name, self.table_schema))
|
||||
self._constraints = [c for c, in self.cursor.fetchall()]
|
||||
self._constraints = [c for c, in cursor.fetchall()]
|
||||
|
||||
self.cursor.execute('SELECT k.column_name, r.delete_rule '
|
||||
cursor.execute('SELECT k.column_name, r.delete_rule '
|
||||
'FROM information_schema.key_column_usage AS k '
|
||||
'JOIN information_schema.referential_constraints AS r '
|
||||
'ON r.constraint_schema = k.constraint_schema '
|
||||
'AND r.constraint_name = k.constraint_name '
|
||||
'WHERE k.table_name = %s AND k.table_schema = %s',
|
||||
(self.table_name, self.table_schema))
|
||||
self._fk_deltypes = dict(self.cursor.fetchall())
|
||||
self._fk_deltypes = dict(cursor.fetchall())
|
||||
|
||||
# Fetch indexes defined for the table
|
||||
self.cursor.execute("SELECT cl2.relname "
|
||||
cursor.execute("SELECT cl2.relname "
|
||||
"FROM pg_index ind "
|
||||
"JOIN pg_class cl on (cl.oid = ind.indrelid) "
|
||||
"JOIN pg_namespace n ON (cl.relnamespace = n.oid) "
|
||||
"JOIN pg_class cl2 on (cl2.oid = ind.indexrelid) "
|
||||
"WHERE cl.relname = %s AND n.nspname = %s",
|
||||
(self.table_name, self.table_schema))
|
||||
self._indexes = [l[0] for l in self.cursor.fetchall()]
|
||||
self._indexes = [l[0] for l in cursor.fetchall()]
|
||||
|
||||
# Keep track of which module created each field
|
||||
self._field2module = {}
|
||||
if self.object_name is not None:
|
||||
self.cursor.execute('SELECT f.name, f.module '
|
||||
cursor.execute('SELECT f.name, f.module '
|
||||
'FROM ir_model_field f '
|
||||
'JOIN ir_model m on (f.model=m.id) '
|
||||
'WHERE m.model = %s',
|
||||
(self.object_name,))
|
||||
for line in self.cursor.fetchall():
|
||||
for line in cursor:
|
||||
self._field2module[line[0]] = line[1]
|
||||
|
||||
def alter_size(self, column_name, column_type):
|
||||
|
||||
self.cursor.execute("ALTER TABLE \"%s\" "
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute("ALTER TABLE \"%s\" "
|
||||
"RENAME COLUMN \"%s\" TO _temp_change_size"
|
||||
% (self.table_name, column_name))
|
||||
self.cursor.execute("ALTER TABLE \"%s\" "
|
||||
cursor.execute("ALTER TABLE \"%s\" "
|
||||
"ADD COLUMN \"%s\" %s"
|
||||
% (self.table_name, column_name, column_type))
|
||||
self.cursor.execute("UPDATE \"%s\" "
|
||||
cursor.execute("UPDATE \"%s\" "
|
||||
"SET \"%s\" = _temp_change_size::%s"
|
||||
% (self.table_name, column_name, column_type))
|
||||
self.cursor.execute("ALTER TABLE \"%s\" "
|
||||
cursor.execute("ALTER TABLE \"%s\" "
|
||||
"DROP COLUMN _temp_change_size"
|
||||
% (self.table_name,))
|
||||
self._update_definitions()
|
||||
|
||||
def alter_type(self, column_name, column_type):
|
||||
self.cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
'ALTER "' + column_name + '" TYPE ' + column_type)
|
||||
self._update_definitions()
|
||||
|
||||
@@ -222,21 +224,24 @@ class TableHandler(TableHandlerInterface):
|
||||
else:
|
||||
test = value
|
||||
if self._columns[column_name]['default'] != test:
|
||||
self.cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
'ALTER COLUMN "' + column_name + '" SET DEFAULT %s',
|
||||
(value,))
|
||||
|
||||
def add_raw_column(self, column_name, column_type, column_format,
|
||||
default_fun=None, field_size=None, migrate=True, string=''):
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
def comment():
|
||||
if self.is_owner:
|
||||
self.cursor.execute('COMMENT ON COLUMN "%s"."%s" IS \'%s\'' %
|
||||
cursor.execute('COMMENT ON COLUMN "%s"."%s" IS \'%s\'' %
|
||||
(self.table_name, column_name, string.replace("'", "''")))
|
||||
if self.column_exist(column_name):
|
||||
if (column_name in ('create_date', 'write_date')
|
||||
and column_type[1].lower() != 'timestamp(6)'):
|
||||
# Migrate dates from timestamp(0) to timestamp
|
||||
self.cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
'ALTER COLUMN "' + column_name + '" TYPE timestamp')
|
||||
comment()
|
||||
if not migrate:
|
||||
@@ -279,19 +284,19 @@ class TableHandler(TableHandlerInterface):
|
||||
return
|
||||
|
||||
column_type = column_type[1]
|
||||
self.cursor.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s'
|
||||
cursor.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s'
|
||||
% (self.table_name, column_name, column_type))
|
||||
comment()
|
||||
|
||||
if column_format:
|
||||
# check if table is non-empty:
|
||||
self.cursor.execute('SELECT 1 FROM "%s" limit 1' % self.table_name)
|
||||
if self.cursor.rowcount:
|
||||
cursor.execute('SELECT 1 FROM "%s" limit 1' % self.table_name)
|
||||
if cursor.rowcount:
|
||||
# Populate column with default values:
|
||||
default = None
|
||||
if default_fun is not None:
|
||||
default = default_fun()
|
||||
self.cursor.execute('UPDATE "' + self.table_name + '" '
|
||||
cursor.execute('UPDATE "' + self.table_name + '" '
|
||||
'SET "' + column_name + '" = %s',
|
||||
(column_format(default),))
|
||||
|
||||
@@ -303,20 +308,21 @@ class TableHandler(TableHandlerInterface):
|
||||
else:
|
||||
on_delete = 'SET NULL'
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
name = self.table_name + '_' + column_name + '_fkey'
|
||||
self.cursor.execute('SELECT 1 '
|
||||
cursor.execute('SELECT 1 '
|
||||
'FROM information_schema.key_column_usage '
|
||||
'WHERE table_name = %s AND table_schema = %s '
|
||||
'AND constraint_name = %s',
|
||||
(self.table_name, self.table_schema, name))
|
||||
add = False
|
||||
if not self.cursor.rowcount:
|
||||
if not cursor.rowcount:
|
||||
add = True
|
||||
elif self._fk_deltypes.get(column_name) != on_delete:
|
||||
self.drop_fk(column_name)
|
||||
add = True
|
||||
if add:
|
||||
self.cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
'ADD CONSTRAINT "' + name + '" '
|
||||
'FOREIGN KEY ("' + column_name + '") '
|
||||
'REFERENCES "' + reference + '" '
|
||||
@@ -336,69 +342,73 @@ class TableHandler(TableHandlerInterface):
|
||||
else:
|
||||
test_index_name = index_name
|
||||
|
||||
if action == 'add':
|
||||
if test_index_name in self._indexes:
|
||||
return
|
||||
self.cursor.execute('CREATE INDEX "' + index_name + '" '
|
||||
'ON "' + self.table_name + '" ( '
|
||||
+ ','.join(['"' + x + '"' for x in column_name]) + ')')
|
||||
self._update_definitions()
|
||||
elif action == 'remove':
|
||||
if len(column_name) == 1:
|
||||
if (self._field2module.get(column_name[0], self.module_name)
|
||||
!= self.module_name):
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
if action == 'add':
|
||||
if test_index_name in self._indexes:
|
||||
return
|
||||
|
||||
if test_index_name in self._indexes:
|
||||
self.cursor.execute('DROP INDEX "%s" ' % (index_name,))
|
||||
cursor.execute('CREATE INDEX "' + index_name + '" '
|
||||
'ON "' + self.table_name + '" ( '
|
||||
+ ','.join(['"' + x + '"' for x in column_name]) + ')')
|
||||
self._update_definitions()
|
||||
else:
|
||||
raise Exception('Index action not supported!')
|
||||
elif action == 'remove':
|
||||
if len(column_name) == 1:
|
||||
if (self._field2module.get(column_name[0],
|
||||
self.module_name) != self.module_name):
|
||||
return
|
||||
|
||||
if test_index_name in self._indexes:
|
||||
cursor.execute('DROP INDEX "%s" ' % (index_name,))
|
||||
self._update_definitions()
|
||||
else:
|
||||
raise Exception('Index action not supported!')
|
||||
|
||||
def not_null_action(self, column_name, action='add'):
|
||||
if not self.column_exist(column_name):
|
||||
return
|
||||
|
||||
if action == 'add':
|
||||
if self._columns[column_name]['notnull']:
|
||||
return
|
||||
self.cursor.execute('SELECT id FROM "%s" '
|
||||
'WHERE "%s" IS NULL'
|
||||
% (self.table_name, column_name))
|
||||
if not self.cursor.rowcount:
|
||||
self.cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
'ALTER COLUMN "' + column_name + '" SET NOT NULL')
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
if action == 'add':
|
||||
if self._columns[column_name]['notnull']:
|
||||
return
|
||||
cursor.execute('SELECT id FROM "%s" '
|
||||
'WHERE "%s" IS NULL'
|
||||
% (self.table_name, column_name))
|
||||
if not cursor.rowcount:
|
||||
cursor.execute('ALTER TABLE "' + self.table_name + '" '
|
||||
'ALTER COLUMN "' + column_name + '" SET NOT NULL')
|
||||
self._update_definitions()
|
||||
else:
|
||||
logger.warning(
|
||||
'Unable to set column %s '
|
||||
'of table %s not null !\n'
|
||||
'Try to re-run: '
|
||||
'trytond.py --update=module\n'
|
||||
'If it doesn\'t work, update records '
|
||||
'and execute manually:\n'
|
||||
'ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL',
|
||||
column_name, self.table_name, self.table_name,
|
||||
column_name)
|
||||
elif action == 'remove':
|
||||
if not self._columns[column_name]['notnull']:
|
||||
return
|
||||
if (self._field2module.get(column_name, self.module_name)
|
||||
!= self.module_name):
|
||||
return
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ALTER COLUMN "%s" DROP NOT NULL'
|
||||
% (self.table_name, column_name))
|
||||
self._update_definitions()
|
||||
else:
|
||||
logger.warning(
|
||||
'Unable to set column %s '
|
||||
'of table %s not null !\n'
|
||||
'Try to re-run: '
|
||||
'trytond.py --update=module\n'
|
||||
'If it doesn\'t work, update records '
|
||||
'and execute manually:\n'
|
||||
'ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL',
|
||||
column_name, self.table_name, self.table_name, column_name)
|
||||
elif action == 'remove':
|
||||
if not self._columns[column_name]['notnull']:
|
||||
return
|
||||
if (self._field2module.get(column_name, self.module_name)
|
||||
!= self.module_name):
|
||||
return
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
'ALTER COLUMN "%s" DROP NOT NULL'
|
||||
% (self.table_name, column_name))
|
||||
self._update_definitions()
|
||||
else:
|
||||
raise Exception('Not null action not supported!')
|
||||
raise Exception('Not null action not supported!')
|
||||
|
||||
def add_constraint(self, ident, constraint, exception=False):
|
||||
ident = self.table_name + "_" + ident
|
||||
if ident in self._constraints:
|
||||
# This constrain already exist
|
||||
return
|
||||
cursor = Transaction().connection.cursor()
|
||||
try:
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'ADD CONSTRAINT "%s" %s'
|
||||
% (self.table_name, ident, constraint), constraint.params)
|
||||
except Exception:
|
||||
@@ -417,8 +427,9 @@ class TableHandler(TableHandlerInterface):
|
||||
ident = (table or self.table_name) + "_" + ident
|
||||
if ident not in self._constraints:
|
||||
return
|
||||
cursor = Transaction().connection.cursor()
|
||||
try:
|
||||
self.cursor.execute('ALTER TABLE "%s" '
|
||||
cursor.execute('ALTER TABLE "%s" '
|
||||
'DROP CONSTRAINT "%s"'
|
||||
% (self.table_name, ident))
|
||||
except Exception:
|
||||
@@ -432,8 +443,9 @@ class TableHandler(TableHandlerInterface):
|
||||
def drop_column(self, column_name, exception=False):
|
||||
if not self.column_exist(column_name):
|
||||
return
|
||||
cursor = Transaction().connection.cursor()
|
||||
try:
|
||||
self.cursor.execute(
|
||||
cursor.execute(
|
||||
'ALTER TABLE "%s" DROP COLUMN "%s"' %
|
||||
(self.table_name, column_name))
|
||||
|
||||
@@ -447,6 +459,7 @@ class TableHandler(TableHandlerInterface):
|
||||
|
||||
@staticmethod
|
||||
def drop_table(cursor, model, table, cascade=False):
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('DELETE FROM ir_model_data '
|
||||
'WHERE model = \'%s\'' % model)
|
||||
|
||||
|
||||
@@ -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.backend.database import DatabaseInterface, CursorInterface
|
||||
from trytond.backend.database import DatabaseInterface
|
||||
from trytond.config import config
|
||||
import os
|
||||
from decimal import Decimal
|
||||
@@ -24,8 +24,7 @@ from sql import Flavor, Table
|
||||
from sql.functions import (Function, Extract, Position, Substring,
|
||||
Overlay, CharLength, CurrentTimestamp)
|
||||
|
||||
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError',
|
||||
'Cursor']
|
||||
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
|
||||
|
||||
|
||||
class SQLiteExtract(Function):
|
||||
@@ -178,35 +177,52 @@ MAPPING = {
|
||||
}
|
||||
|
||||
|
||||
class SQLiteCursor(sqlite.Cursor):
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
pass
|
||||
|
||||
|
||||
class SQLiteConnection(sqlite.Connection):
|
||||
|
||||
def cursor(self):
|
||||
return super(SQLiteConnection, self).cursor(SQLiteCursor)
|
||||
|
||||
|
||||
class Database(DatabaseInterface):
|
||||
|
||||
_local = threading.local()
|
||||
_conn = None
|
||||
flavor = Flavor(paramstyle='qmark', function_mapping=MAPPING)
|
||||
IN_MAX = 200
|
||||
|
||||
def __new__(cls, database_name=':memory:'):
|
||||
if (database_name == ':memory:'
|
||||
def __new__(cls, name=':memory:'):
|
||||
if (name == ':memory:'
|
||||
and getattr(cls._local, 'memory_database', None)):
|
||||
return cls._local.memory_database
|
||||
return DatabaseInterface.__new__(cls, database_name=database_name)
|
||||
return DatabaseInterface.__new__(cls, name=name)
|
||||
|
||||
def __init__(self, database_name=':memory:'):
|
||||
super(Database, self).__init__(database_name=database_name)
|
||||
if database_name == ':memory:':
|
||||
def __init__(self, name=':memory:'):
|
||||
super(Database, self).__init__(name=name)
|
||||
if name == ':memory:':
|
||||
Database._local.memory_database = self
|
||||
|
||||
def connect(self):
|
||||
if self.database_name == ':memory:':
|
||||
if self.name == ':memory:':
|
||||
path = ':memory:'
|
||||
else:
|
||||
db_filename = self.database_name + '.sqlite'
|
||||
db_filename = self.name + '.sqlite'
|
||||
path = os.path.join(config.get('database', 'path'), db_filename)
|
||||
if not os.path.isfile(path):
|
||||
raise IOError('Database "%s" doesn\'t exist!' % db_filename)
|
||||
if self._conn is not None:
|
||||
return self
|
||||
self._conn = sqlite.connect(path,
|
||||
detect_types=sqlite.PARSE_DECLTYPES | sqlite.PARSE_COLNAMES)
|
||||
detect_types=sqlite.PARSE_DECLTYPES | sqlite.PARSE_COLNAMES,
|
||||
factory=SQLiteConnection)
|
||||
self._conn.create_function('extract', 2, SQLiteExtract.extract)
|
||||
self._conn.create_function('date_trunc', 2, date_trunc)
|
||||
self._conn.create_function('split_part', 3, split_part)
|
||||
@@ -222,24 +238,27 @@ class Database(DatabaseInterface):
|
||||
self._conn.execute('PRAGMA foreign_keys = ON')
|
||||
return self
|
||||
|
||||
def cursor(self, autocommit=False, readonly=False):
|
||||
def get_connection(self, autocommit=False, readonly=False):
|
||||
if self._conn is None:
|
||||
self.connect()
|
||||
if autocommit:
|
||||
self._conn.isolation_level = None
|
||||
else:
|
||||
self._conn.isolation_level = 'IMMEDIATE'
|
||||
return Cursor(self._conn, self.database_name)
|
||||
return self._conn
|
||||
|
||||
def put_connection(self, connection=None, close=False):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
if self.database_name == ':memory:':
|
||||
if self.name == ':memory:':
|
||||
return
|
||||
if self._conn is None:
|
||||
return
|
||||
self._conn = None
|
||||
|
||||
@staticmethod
|
||||
def create(cursor, database_name):
|
||||
@classmethod
|
||||
def create(cls, connection, database_name):
|
||||
if database_name == ':memory:':
|
||||
path = ':memory:'
|
||||
else:
|
||||
@@ -251,10 +270,9 @@ class Database(DatabaseInterface):
|
||||
cursor = conn.cursor()
|
||||
cursor.close()
|
||||
|
||||
@classmethod
|
||||
def drop(cls, cursor, database_name):
|
||||
def drop(self, connection, database_name):
|
||||
if database_name == ':memory:':
|
||||
cls._local.memory_database._conn = None
|
||||
self._local.memory_database._conn = None
|
||||
return
|
||||
if os.sep in database_name:
|
||||
return
|
||||
@@ -286,8 +304,7 @@ class Database(DatabaseInterface):
|
||||
with open(path, 'wb') as file_p:
|
||||
file_p.write(data)
|
||||
|
||||
@staticmethod
|
||||
def list(cursor):
|
||||
def list(self):
|
||||
res = []
|
||||
listdir = [':memory:']
|
||||
try:
|
||||
@@ -304,77 +321,45 @@ class Database(DatabaseInterface):
|
||||
database = Database(db_name)
|
||||
except Exception:
|
||||
continue
|
||||
cursor2 = database.cursor()
|
||||
if cursor2.test():
|
||||
if database.test():
|
||||
res.append(db_name)
|
||||
cursor2.close()
|
||||
database.close()
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def init(cursor):
|
||||
def init(self):
|
||||
from trytond.modules import get_module_info
|
||||
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
|
||||
with open(sql_file) as fp:
|
||||
for line in fp.read().split(';'):
|
||||
if (len(line) > 0) and (not line.isspace()):
|
||||
cursor.execute(line)
|
||||
with self._conn as conn:
|
||||
cursor = conn.cursor()
|
||||
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
|
||||
with open(sql_file) as fp:
|
||||
for line in fp.read().split(';'):
|
||||
if (len(line) > 0) and (not line.isspace()):
|
||||
cursor.execute(line)
|
||||
|
||||
ir_module = Table('ir_module')
|
||||
ir_module_dependency = Table('ir_module_dependency')
|
||||
for module in ('ir', 'res'):
|
||||
state = 'uninstalled'
|
||||
if module in ('ir', 'res'):
|
||||
state = 'to install'
|
||||
info = get_module_info(module)
|
||||
insert = ir_module.insert(
|
||||
[ir_module.create_uid, ir_module.create_date, ir_module.name,
|
||||
ir_module.state],
|
||||
[[0, CurrentTimestamp(), module, state]])
|
||||
cursor.execute(*insert)
|
||||
cursor.execute('SELECT last_insert_rowid()')
|
||||
module_id, = cursor.fetchone()
|
||||
for dependency in info.get('depends', []):
|
||||
insert = ir_module_dependency.insert(
|
||||
[ir_module_dependency.create_uid,
|
||||
ir_module_dependency.create_date,
|
||||
ir_module_dependency.module, ir_module_dependency.name
|
||||
],
|
||||
[[0, CurrentTimestamp(), module_id, dependency]])
|
||||
ir_module = Table('ir_module')
|
||||
ir_module_dependency = Table('ir_module_dependency')
|
||||
for module in ('ir', 'res'):
|
||||
state = 'uninstalled'
|
||||
if module in ('ir', 'res'):
|
||||
state = 'to install'
|
||||
info = get_module_info(module)
|
||||
insert = ir_module.insert(
|
||||
[ir_module.create_uid, ir_module.create_date,
|
||||
ir_module.name, ir_module.state],
|
||||
[[0, CurrentTimestamp(), module, state]])
|
||||
cursor.execute(*insert)
|
||||
|
||||
|
||||
class Cursor(CursorInterface):
|
||||
IN_MAX = 200
|
||||
|
||||
def __init__(self, conn, database_name):
|
||||
super(Cursor, self).__init__()
|
||||
self._conn = conn
|
||||
self.database_name = database_name
|
||||
self.dbname = self.database_name # XXX to remove
|
||||
self.cursor = conn.cursor()
|
||||
|
||||
def __getattr__(self, name):
|
||||
if _FIX_ROWCOUNT and name == 'rowcount':
|
||||
return -1
|
||||
return getattr(self.cursor, name)
|
||||
|
||||
def execute(self, sql, params=None):
|
||||
if params:
|
||||
return self.cursor.execute(sql, params)
|
||||
else:
|
||||
return self.cursor.execute(sql)
|
||||
|
||||
def close(self, close=False):
|
||||
self.cursor.close()
|
||||
self.rollback()
|
||||
|
||||
def commit(self):
|
||||
super(Cursor, self).commit()
|
||||
self._conn.commit()
|
||||
|
||||
def rollback(self):
|
||||
super(Cursor, self).rollback()
|
||||
self._conn.rollback()
|
||||
cursor.execute('SELECT last_insert_rowid()')
|
||||
module_id, = cursor.fetchone()
|
||||
for dependency in info.get('depends', []):
|
||||
insert = ir_module_dependency.insert(
|
||||
[ir_module_dependency.create_uid,
|
||||
ir_module_dependency.create_date,
|
||||
ir_module_dependency.module,
|
||||
ir_module_dependency.name,
|
||||
],
|
||||
[[0, CurrentTimestamp(), module_id, dependency]])
|
||||
cursor.execute(*insert)
|
||||
conn.commit()
|
||||
|
||||
def test(self):
|
||||
sqlite_master = Table('sqlite_master')
|
||||
@@ -392,17 +377,19 @@ class Cursor(CursorInterface):
|
||||
'ir_translation',
|
||||
'ir_lang',
|
||||
])
|
||||
try:
|
||||
self.cursor.execute(*select)
|
||||
except Exception:
|
||||
return False
|
||||
return len(self.cursor.fetchall()) != 0
|
||||
with self._conn as conn:
|
||||
cursor = conn.cursor()
|
||||
try:
|
||||
cursor.execute(*select)
|
||||
except Exception:
|
||||
return False
|
||||
return len(cursor.fetchall()) != 0
|
||||
|
||||
def lastid(self):
|
||||
self.cursor.execute('SELECT last_insert_rowid()')
|
||||
return self.cursor.fetchone()[0]
|
||||
def lastid(self, cursor):
|
||||
# This call is not thread safe
|
||||
return cursor.lastrowid
|
||||
|
||||
def lock(self, table):
|
||||
def lock(self, connection, table):
|
||||
pass
|
||||
|
||||
def has_constraint(self):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# 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.transaction import Transaction
|
||||
from trytond.backend.table import TableHandlerInterface
|
||||
import logging
|
||||
import re
|
||||
@@ -12,8 +13,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TableHandler(TableHandlerInterface):
|
||||
def __init__(self, cursor, model, module_name=None, history=False):
|
||||
super(TableHandler, self).__init__(cursor, model,
|
||||
def __init__(self, model, module_name=None, history=False):
|
||||
super(TableHandler, self).__init__(model,
|
||||
module_name=module_name, history=history)
|
||||
self._columns = {}
|
||||
self._constraints = []
|
||||
@@ -22,21 +23,23 @@ class TableHandler(TableHandlerInterface):
|
||||
self._field2module = {}
|
||||
self._model = model
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
# Create new table if necessary
|
||||
if not self.table_exist(self.cursor, self.table_name):
|
||||
if not self.table_exist(self.table_name):
|
||||
if not self.history:
|
||||
self.cursor.execute('CREATE TABLE "%s" '
|
||||
cursor.execute('CREATE TABLE "%s" '
|
||||
'(id INTEGER PRIMARY KEY AUTOINCREMENT)'
|
||||
% self.table_name)
|
||||
else:
|
||||
self.cursor.execute('CREATE TABLE "%s" '
|
||||
cursor.execute('CREATE TABLE "%s" '
|
||||
'(__id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||
'id INTEGER)' % self.table_name)
|
||||
|
||||
self._update_definitions()
|
||||
|
||||
@staticmethod
|
||||
def table_exist(cursor, table_name):
|
||||
def table_exist(table_name):
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute("SELECT sql FROM sqlite_master "
|
||||
"WHERE type = 'table' AND name = ?",
|
||||
(table_name,))
|
||||
@@ -69,38 +72,38 @@ class TableHandler(TableHandlerInterface):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def table_rename(cursor, old_name, new_name):
|
||||
if (TableHandler.table_exist(cursor, old_name)
|
||||
and not TableHandler.table_exist(cursor, new_name)):
|
||||
def table_rename(old_name, new_name):
|
||||
cursor = Transaction().connection.cursor()
|
||||
if (TableHandler.table_exist(old_name)
|
||||
and not TableHandler.table_exist(new_name)):
|
||||
cursor.execute('ALTER TABLE "%s" RENAME TO "%s"'
|
||||
% (old_name, new_name))
|
||||
# Rename history table
|
||||
old_history = old_name + "__history"
|
||||
new_history = new_name + "__history"
|
||||
if (TableHandler.table_exist(cursor, old_history)
|
||||
and not TableHandler.table_exist(cursor, new_history)):
|
||||
if (TableHandler.table_exist(old_history)
|
||||
and not TableHandler.table_exist(new_history)):
|
||||
cursor.execute('ALTER TABLE "%s" RENAME TO "%s"'
|
||||
% (old_history, new_history))
|
||||
|
||||
@staticmethod
|
||||
def sequence_exist(cursor, sequence_name):
|
||||
def sequence_exist(sequence_name):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def sequence_rename(cursor, old_name, new_name):
|
||||
def sequence_rename(old_name, new_name):
|
||||
pass
|
||||
|
||||
def column_exist(self, column_name):
|
||||
return column_name in self._columns
|
||||
|
||||
def column_rename(self, old_name, new_name, exception=False):
|
||||
cursor = Transaction().connection.cursor()
|
||||
if self.column_exist(old_name) and \
|
||||
not self.column_exist(new_name):
|
||||
temp_table = '_temp_%s' % self.table_name
|
||||
TableHandler.table_rename(self.cursor, self.table_name,
|
||||
temp_table)
|
||||
new_table = TableHandler(self.cursor, self._model,
|
||||
history=self.history)
|
||||
TableHandler.table_rename(self.table_name, temp_table)
|
||||
new_table = TableHandler(self._model, history=self.history)
|
||||
for column, (notnull, hasdef, size, typname) \
|
||||
in self._columns.iteritems():
|
||||
if column == old_name:
|
||||
@@ -110,12 +113,12 @@ class TableHandler(TableHandlerInterface):
|
||||
new_columns = new_table._columns.keys()
|
||||
old_columns = [x if x != old_name else new_name
|
||||
for x in new_columns]
|
||||
self.cursor.execute(('INSERT INTO "%s" (' +
|
||||
cursor.execute(('INSERT INTO "%s" (' +
|
||||
','.join('"%s"' % x for x in new_columns) +
|
||||
') SELECT ' +
|
||||
','.join('"%s"' % x for x in old_columns) + ' ' +
|
||||
'FROM "%s"') % (self.table_name, temp_table))
|
||||
self.cursor.execute('DROP TABLE "%s"' % temp_table)
|
||||
cursor.execute('DROP TABLE "%s"' % temp_table)
|
||||
self._update_definitions()
|
||||
elif exception and self.column_exist(new_name):
|
||||
raise Exception('Unable to rename column %s.%s to %s.%s: '
|
||||
@@ -124,11 +127,11 @@ class TableHandler(TableHandlerInterface):
|
||||
self.table_name, new_name))
|
||||
|
||||
def _update_definitions(self):
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
# Fetch columns definitions from the table
|
||||
self.cursor.execute('PRAGMA table_info("' + self.table_name + '")')
|
||||
cursor.execute('PRAGMA table_info("' + self.table_name + '")')
|
||||
self._columns = {}
|
||||
for _, column, type_, notnull, hasdef, _ in self.cursor.fetchall():
|
||||
for _, column, type_, notnull, hasdef, _ in cursor.fetchall():
|
||||
column = re.sub(r'^\"|\"$', '', column)
|
||||
match = re.match(r'(\w+)(\((.*?)\))?', type_)
|
||||
if match:
|
||||
@@ -146,20 +149,20 @@ class TableHandler(TableHandlerInterface):
|
||||
|
||||
# Fetch indexes defined for the table
|
||||
try:
|
||||
self.cursor.execute('PRAGMA index_list("' + self.table_name + '")')
|
||||
cursor.execute('PRAGMA index_list("' + self.table_name + '")')
|
||||
except IndexError: # There is sometimes IndexError
|
||||
self.cursor.execute('PRAGMA index_list("' + self.table_name + '")')
|
||||
self._indexes = [l[1] for l in self.cursor.fetchall()]
|
||||
cursor.execute('PRAGMA index_list("' + self.table_name + '")')
|
||||
self._indexes = [l[1] for l in cursor.fetchall()]
|
||||
|
||||
# Keep track of which module created each field
|
||||
self._field2module = {}
|
||||
if self.object_name is not None:
|
||||
self.cursor.execute('SELECT f.name, f.module '
|
||||
cursor.execute('SELECT f.name, f.module '
|
||||
'FROM ir_model_field f '
|
||||
'JOIN ir_model m on (f.model=m.id) '
|
||||
'WHERE m.model = ?',
|
||||
(self.object_name,))
|
||||
for line in self.cursor.fetchall():
|
||||
for line in cursor.fetchall():
|
||||
self._field2module[line[0]] = line[1]
|
||||
|
||||
def alter_size(self, column_name, column_type):
|
||||
@@ -213,20 +216,21 @@ class TableHandler(TableHandlerInterface):
|
||||
field_size)
|
||||
return
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
column_type = column_type[1]
|
||||
default = ''
|
||||
self.cursor.execute(('ALTER TABLE "%s" ADD COLUMN "%s" %s' + default) %
|
||||
cursor.execute(('ALTER TABLE "%s" ADD COLUMN "%s" %s' + default) %
|
||||
(self.table_name, column_name, column_type))
|
||||
|
||||
if column_format:
|
||||
# check if table is non-empty:
|
||||
self.cursor.execute('SELECT 1 FROM "%s" limit 1' % self.table_name)
|
||||
if self.cursor.fetchone():
|
||||
cursor.execute('SELECT 1 FROM "%s" limit 1' % self.table_name)
|
||||
if cursor.fetchone():
|
||||
# Populate column with default values:
|
||||
default = None
|
||||
if default_fun is not None:
|
||||
default = default_fun()
|
||||
self.cursor.execute('UPDATE "' + self.table_name + '" '
|
||||
cursor.execute('UPDATE "' + self.table_name + '" '
|
||||
'SET "' + column_name + '" = ?',
|
||||
(column_format(default),))
|
||||
|
||||
@@ -243,22 +247,23 @@ class TableHandler(TableHandlerInterface):
|
||||
column_name = [column_name]
|
||||
index_name = self.table_name + "_" + '_'.join(column_name) + "_index"
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
if action == 'add':
|
||||
if index_name in self._indexes:
|
||||
return
|
||||
self.cursor.execute('CREATE INDEX "' + index_name + '" '
|
||||
cursor.execute('CREATE INDEX "' + index_name + '" '
|
||||
'ON "' + self.table_name + '" ( ' +
|
||||
','.join('"' + x + '"' for x in column_name) +
|
||||
')')
|
||||
self._update_definitions()
|
||||
elif action == 'remove':
|
||||
if len(column_name) == 1:
|
||||
if self._field2module.get(column_name[0], self.module_name) \
|
||||
!= self.module_name:
|
||||
if self._field2module.get(column_name[0],
|
||||
self.module_name) != self.module_name:
|
||||
return
|
||||
|
||||
if index_name in self._indexes:
|
||||
self.cursor.execute('DROP INDEX "%s" ' % (index_name,))
|
||||
cursor.execute('DROP INDEX "%s" ' % (index_name,))
|
||||
self._update_definitions()
|
||||
else:
|
||||
raise Exception('Index action not supported!')
|
||||
@@ -284,7 +289,8 @@ class TableHandler(TableHandlerInterface):
|
||||
warnings.warn('Unable to drop column with SQLite backend')
|
||||
|
||||
@staticmethod
|
||||
def drop_table(cursor, model, table, cascade=False):
|
||||
def drop_table(model, table, cascade=False):
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('DELETE from ir_model_data where '
|
||||
'model = \'%s\'' % model)
|
||||
|
||||
|
||||
@@ -7,15 +7,13 @@ class TableHandlerInterface(object):
|
||||
Define generic interface to handle database table
|
||||
'''
|
||||
|
||||
def __init__(self, cursor, model, module_name=None, history=False):
|
||||
def __init__(self, model, module_name=None, history=False):
|
||||
'''
|
||||
:param cursor: the database cursor
|
||||
:param model: the Model linked to the table
|
||||
:param module_name: the module name
|
||||
:param history: a boolean to define if it is a history table
|
||||
'''
|
||||
super(TableHandlerInterface, self).__init__()
|
||||
self.cursor = cursor
|
||||
if history:
|
||||
self.table_name = model._table + '__history'
|
||||
else:
|
||||
@@ -29,44 +27,40 @@ class TableHandlerInterface(object):
|
||||
self.history = history
|
||||
|
||||
@staticmethod
|
||||
def table_exist(cursor, table_name):
|
||||
def table_exist(table_name):
|
||||
'''
|
||||
Table exist
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param table_name: the table name
|
||||
:return: a boolean
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def table_rename(cursor, old_name, new_name):
|
||||
def table_rename(old_name, new_name):
|
||||
'''
|
||||
Rename table
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param old_name: the old table name
|
||||
:param new_name: the new table name
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def sequence_exist(cursor, sequence_name):
|
||||
def sequence_exist(sequence_name):
|
||||
'''
|
||||
Sequence exist
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param sequence_name: the sequence name
|
||||
:return: a boolean
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def sequence_rename(cursor, old_name, new_name):
|
||||
def sequence_rename(old_name, new_name):
|
||||
'''
|
||||
Rename sequence
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param old_name: the old sequence name
|
||||
:param new_name: the new sequence name
|
||||
'''
|
||||
@@ -206,7 +200,7 @@ class TableHandlerInterface(object):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def drop_table(cursor, model, table, cascade=False):
|
||||
def drop_table(model, table, cascade=False):
|
||||
'''
|
||||
Remove a table and clean ir_model_data from the given model.
|
||||
|
||||
|
||||
+26
-29
@@ -43,11 +43,10 @@ class Cache(object):
|
||||
return key
|
||||
|
||||
def get(self, key, default=None):
|
||||
cursor = Transaction().cursor
|
||||
dbname = Transaction().database.name
|
||||
key = self._key(key)
|
||||
with self._lock:
|
||||
cache = self._cache.setdefault(cursor.dbname,
|
||||
LRUDict(self.size_limit))
|
||||
cache = self._cache.setdefault(dbname, LRUDict(self.size_limit))
|
||||
try:
|
||||
result = cache[key] = cache.pop(key)
|
||||
return result
|
||||
@@ -55,11 +54,10 @@ class Cache(object):
|
||||
return default
|
||||
|
||||
def set(self, key, value):
|
||||
cursor = Transaction().cursor
|
||||
dbname = Transaction().database.name
|
||||
key = self._key(key)
|
||||
with self._lock:
|
||||
cache = self._cache.setdefault(cursor.dbname,
|
||||
LRUDict(self.size_limit))
|
||||
cache = self._cache.setdefault(dbname, LRUDict(self.size_limit))
|
||||
try:
|
||||
cache[key] = value
|
||||
except TypeError:
|
||||
@@ -67,15 +65,15 @@ class Cache(object):
|
||||
return value
|
||||
|
||||
def clear(self):
|
||||
cursor = Transaction().cursor
|
||||
Cache.reset(cursor.dbname, self._name)
|
||||
dbname = Transaction().database.name
|
||||
Cache.reset(dbname, self._name)
|
||||
with self._lock:
|
||||
self._cache[cursor.dbname] = LRUDict(self.size_limit)
|
||||
self._cache[dbname] = LRUDict(self.size_limit)
|
||||
|
||||
@staticmethod
|
||||
def clean(dbname):
|
||||
with Transaction().new_cursor():
|
||||
cursor = Transaction().cursor
|
||||
with Transaction().new_transaction() as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
table = Table('ir_cache')
|
||||
cursor.execute(*table.select(table.timestamp, table.name))
|
||||
timestamps = {}
|
||||
@@ -97,25 +95,24 @@ class Cache(object):
|
||||
|
||||
@staticmethod
|
||||
def resets(dbname):
|
||||
with Transaction().new_cursor():
|
||||
cursor = Transaction().cursor
|
||||
table = Table('ir_cache')
|
||||
with Cache._resets_lock:
|
||||
Cache._resets.setdefault(dbname, set())
|
||||
for name in Cache._resets[dbname]:
|
||||
cursor.execute(*table.select(table.name,
|
||||
table = Table('ir_cache')
|
||||
with Transaction().new_transaction() as transaction,\
|
||||
transaction.connection.cursor() as cursor,\
|
||||
Cache._resets_lock:
|
||||
Cache._resets.setdefault(dbname, set())
|
||||
for name in Cache._resets[dbname]:
|
||||
cursor.execute(*table.select(table.name,
|
||||
where=table.name == name))
|
||||
if cursor.fetchone():
|
||||
# It would be better to insert only
|
||||
cursor.execute(*table.update([table.timestamp],
|
||||
[CurrentTimestamp()],
|
||||
where=table.name == name))
|
||||
if cursor.fetchone():
|
||||
# It would be better to insert only
|
||||
cursor.execute(*table.update([table.timestamp],
|
||||
[CurrentTimestamp()],
|
||||
where=table.name == name))
|
||||
else:
|
||||
cursor.execute(*table.insert(
|
||||
[table.timestamp, table.name],
|
||||
[[CurrentTimestamp(), name]]))
|
||||
Cache._resets[dbname].clear()
|
||||
cursor.commit()
|
||||
else:
|
||||
cursor.execute(*table.insert(
|
||||
[table.timestamp, table.name],
|
||||
[[CurrentTimestamp(), name]]))
|
||||
Cache._resets[dbname].clear()
|
||||
|
||||
@classmethod
|
||||
def drop(cls, dbname):
|
||||
|
||||
+7
-7
@@ -43,7 +43,7 @@ class MenuitemTagHandler:
|
||||
self.xml_id = None
|
||||
|
||||
def startElement(self, name, attributes):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
values = {}
|
||||
|
||||
@@ -354,7 +354,7 @@ class Fs2bdAccessor:
|
||||
models = Model.browse(ids)
|
||||
for model in models:
|
||||
if model.id in self.browserecord[module][model_name]:
|
||||
for cache in Transaction().cursor.cache.values():
|
||||
for cache in Transaction().cache.values():
|
||||
for cache in (cache, cache.get('_language_cache',
|
||||
{}).values()):
|
||||
if (model_name in cache
|
||||
@@ -783,7 +783,7 @@ def post_import(pool, module, to_delete):
|
||||
"""
|
||||
Remove the records that are given in to_delete.
|
||||
"""
|
||||
cursor = Transaction().cursor
|
||||
transaction = Transaction()
|
||||
mdata_delete = []
|
||||
ModelData = pool.get("ir.model.data")
|
||||
|
||||
@@ -810,9 +810,9 @@ def post_import(pool, module, to_delete):
|
||||
logger.warning(
|
||||
'Could not delete id %d of model %s because model no '
|
||||
'longer exists.', db_id, model)
|
||||
cursor.commit()
|
||||
transaction.commit()
|
||||
except Exception:
|
||||
cursor.rollback()
|
||||
transaction.rollback()
|
||||
logger.error(
|
||||
'Could not delete id: %d of model %s\n'
|
||||
'There should be some relation '
|
||||
@@ -826,7 +826,7 @@ def post_import(pool, module, to_delete):
|
||||
'active': False,
|
||||
})
|
||||
except Exception:
|
||||
cursor.rollback()
|
||||
transaction.rollback()
|
||||
logger.error(
|
||||
'Could not inactivate id: %d of model %s\n',
|
||||
db_id, model, exc_info=True)
|
||||
@@ -834,6 +834,6 @@ def post_import(pool, module, to_delete):
|
||||
# Clean model_data:
|
||||
if mdata_delete:
|
||||
ModelData.delete(mdata_delete)
|
||||
cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
return True
|
||||
|
||||
+17
-12
@@ -129,7 +129,7 @@ class ActionKeyword(ModelSQL, ModelView):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(ActionKeyword, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(Transaction().cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.index_action(['keyword', 'model'], 'add')
|
||||
|
||||
def get_groups(self, name):
|
||||
@@ -311,9 +311,12 @@ class ActionMixin(ModelSQL):
|
||||
for field in later:
|
||||
del values[field]
|
||||
action_values['type'] = cls.default_type()
|
||||
cursor = Transaction().cursor
|
||||
if cursor.nextid(cls._table):
|
||||
cursor.setnextid(cls._table, cursor.currid(Action._table))
|
||||
transaction = Transaction()
|
||||
database = transaction.database
|
||||
cursor = transaction.connection.cursor()
|
||||
if database.nextid(transaction.connection, cls._table):
|
||||
database.setnextid(transaction.connection, cls._table,
|
||||
database.currid(transaction.connection, Action._table))
|
||||
if 'action' not in values:
|
||||
action, = Action.create([action_values])
|
||||
values['action'] = action.id
|
||||
@@ -323,7 +326,8 @@ class ActionMixin(ModelSQL):
|
||||
cursor.execute(*ir_action.update(
|
||||
[ir_action.id], [action.id],
|
||||
where=ir_action.id == record.id))
|
||||
cursor.update_auto_increment(cls._table, action.id)
|
||||
transaction.database.update_auto_increment(
|
||||
transaction.connection, cls._table, action.id)
|
||||
record = cls(action.id)
|
||||
new_records.append(record)
|
||||
cls.write([record], later)
|
||||
@@ -481,8 +485,9 @@ class ActionReport(ActionMixin, ModelSQL, ModelView):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(ActionReport, cls).__register__(module_name)
|
||||
|
||||
cursor = Transaction().cursor
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
table = TableHandler(cls, module_name)
|
||||
action_report = cls.__table__()
|
||||
|
||||
# Migration from 1.0 report_name_uniq has been removed
|
||||
@@ -503,7 +508,7 @@ class ActionReport(ActionMixin, ModelSQL, ModelView):
|
||||
cls.write(cls.browse(ids), {'extension': 'odt'})
|
||||
|
||||
table.drop_column("output_format")
|
||||
TableHandler.dropTable(cursor, 'ir.action.report.outputformat',
|
||||
TableHandler.dropTable('ir.action.report.outputformat',
|
||||
'ir_action_report_outputformat')
|
||||
|
||||
# Migrate from 2.0 remove required on extension
|
||||
@@ -517,7 +522,7 @@ class ActionReport(ActionMixin, ModelSQL, ModelView):
|
||||
# report_content_custom to remove base64 encoding
|
||||
if (table.column_exist('report_content_data')
|
||||
and table.column_exist('report_content_custom')):
|
||||
limit = cursor.IN_MAX
|
||||
limit = transaction.database.IN_MAX
|
||||
cursor.execute(*action_report.select(
|
||||
Count(action_report.id)))
|
||||
report_count, = cursor.fetchone()
|
||||
@@ -705,12 +710,12 @@ class ActionActWindow(ActionMixin, ModelSQL, ModelView):
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
TableHandler = backend.get('TableHandler')
|
||||
act_window = cls.__table__()
|
||||
super(ActionActWindow, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.0: new search_value format
|
||||
cursor.execute(*act_window.update(
|
||||
@@ -903,7 +908,7 @@ class ActionActWindowView(ModelSQL, ModelView):
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(ActionActWindowView, cls).__register__(module_name)
|
||||
table = TableHandler(Transaction().cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 1.0 remove multi
|
||||
table.drop_column('multi')
|
||||
|
||||
@@ -58,11 +58,11 @@ class Attachment(ResourceMixin, ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
super(Attachment, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
attachment = cls.__table__()
|
||||
|
||||
# Migration from 1.4 res_model and res_id merged into resource
|
||||
@@ -86,7 +86,7 @@ class Attachment(ResourceMixin, ModelSQL, ModelView):
|
||||
return 0
|
||||
|
||||
def get_data(self, name):
|
||||
db_name = Transaction().cursor.dbname
|
||||
db_name = Transaction().database.name
|
||||
format_ = Transaction().context.pop('%s.%s'
|
||||
% (self.__name__, name), '')
|
||||
value = None
|
||||
@@ -116,9 +116,10 @@ class Attachment(ResourceMixin, ModelSQL, ModelView):
|
||||
def set_data(cls, attachments, name, value):
|
||||
if value is None:
|
||||
return
|
||||
cursor = Transaction().cursor
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
table = cls.__table__()
|
||||
db_name = cursor.dbname
|
||||
db_name = transaction.database.name
|
||||
directory = os.path.join(config.get('database', 'path'), db_name)
|
||||
if not os.path.isdir(directory):
|
||||
os.makedirs(directory, 0770)
|
||||
|
||||
+6
-6
@@ -79,11 +79,11 @@ class Cron(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
cron = cls.__table__()
|
||||
|
||||
# Migration from 2.0: rename numbercall, doall and nextcall
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.column_rename('numbercall', 'number_calls')
|
||||
table.column_rename('doall', 'repeat_missed')
|
||||
table.column_rename('nextcall', 'next_call')
|
||||
@@ -186,7 +186,7 @@ class Cron(ModelSQL, ModelView):
|
||||
now = datetime.datetime.now()
|
||||
with Transaction().start(db_name, 0) as transaction:
|
||||
Cache.clean(db_name)
|
||||
transaction.cursor.lock(cls._table)
|
||||
transaction.database.lock(transaction.connection, cls._table)
|
||||
crons = cls.search([
|
||||
('number_calls', '!=', 0),
|
||||
('next_call', '<=', datetime.datetime.now()),
|
||||
@@ -202,7 +202,7 @@ class Cron(ModelSQL, ModelView):
|
||||
try:
|
||||
cron.run_once()
|
||||
except Exception:
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
cron.send_error_message()
|
||||
next_call += cls.get_delta(cron)
|
||||
if number_calls > 0:
|
||||
@@ -214,8 +214,8 @@ class Cron(ModelSQL, ModelView):
|
||||
if not number_calls:
|
||||
cron.active = False
|
||||
cron.save()
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
except Exception:
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
logger.error('Running cron %s', cron.id, exc_info=True)
|
||||
Cache.resets(db_name)
|
||||
|
||||
+1
-2
@@ -16,9 +16,8 @@ from ..tools import datetime_strftime
|
||||
from ..transaction import Transaction
|
||||
from ..pool import Pool
|
||||
from .time_locale import TIME_LOCALE
|
||||
from ..backend.database import CursorInterface
|
||||
|
||||
CursorInterface.cache_keys.add('translate_name')
|
||||
Transaction.cache_keys.add('translate_name')
|
||||
|
||||
__all__ = [
|
||||
'Lang',
|
||||
|
||||
+11
-13
@@ -22,7 +22,7 @@ from ..pyson import Bool, Eval
|
||||
from ..rpc import RPC
|
||||
from .. import backend
|
||||
from ..protocols.jsonrpc import JSONDecoder, JSONEncoder
|
||||
from ..tools import is_instance_method
|
||||
from ..tools import is_instance_method, cursor_dict
|
||||
try:
|
||||
from ..tools.StringMatcher import StringMatcher
|
||||
except ImportError:
|
||||
@@ -84,7 +84,7 @@ class Model(ModelSQL, ModelView):
|
||||
def register(cls, model, module_name):
|
||||
pool = Pool()
|
||||
Property = pool.get('ir.property')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
ir_model = cls.__table__()
|
||||
cursor.execute(*ir_model.select(ir_model.id,
|
||||
@@ -265,7 +265,7 @@ class ModelField(ModelSQL, ModelView):
|
||||
def register(cls, model, module_name, model_id):
|
||||
pool = Pool()
|
||||
Model = pool.get('ir.model')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
ir_model_field = cls.__table__()
|
||||
ir_model = Model.__table__()
|
||||
@@ -280,7 +280,7 @@ class ModelField(ModelSQL, ModelView):
|
||||
ir_model_field.module.as_('module'),
|
||||
ir_model_field.help.as_('help'),
|
||||
where=ir_model.model == model.__name__))
|
||||
model_fields = dict((f['name'], f) for f in cursor.dictfetchall())
|
||||
model_fields = {f['name']: f for f in cursor_dict(cursor)}
|
||||
|
||||
for field_name, field in model._fields.iteritems():
|
||||
if hasattr(field, 'model_name'):
|
||||
@@ -374,7 +374,7 @@ class ModelField(ModelSQL, ModelView):
|
||||
else:
|
||||
model_ids.add(rec['model'])
|
||||
model_ids = list(model_ids)
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
model = Model.__table__()
|
||||
cursor.execute(*model.select(model.id, model.model,
|
||||
where=model.id.in_(model_ids)))
|
||||
@@ -449,11 +449,10 @@ class ModelAccess(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
|
||||
super(ModelAccess, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.6 (model, group) no more unique
|
||||
table.drop_constraint('model_group_uniq')
|
||||
@@ -488,7 +487,7 @@ class ModelAccess(ModelSQL, ModelView):
|
||||
pool = Pool()
|
||||
Model = pool.get('ir.model')
|
||||
UserGroup = pool.get('res.user-res.group')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
user = Transaction().user
|
||||
model_access = cls.__table__()
|
||||
ir_model = Model.__table__()
|
||||
@@ -628,11 +627,10 @@ class ModelFieldAccess(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
|
||||
super(ModelFieldAccess, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.6 (field, group) no more unique
|
||||
table.drop_constraint('field_group_uniq')
|
||||
@@ -669,7 +667,6 @@ class ModelFieldAccess(ModelSQL, ModelView):
|
||||
Model = pool.get('ir.model')
|
||||
ModelField = pool.get('ir.model.field')
|
||||
UserGroup = pool.get('res.user-res.group')
|
||||
cursor = Transaction().cursor
|
||||
user = Transaction().user
|
||||
field_access = cls.__table__()
|
||||
ir_model = Model.__table__()
|
||||
@@ -687,6 +684,7 @@ class ModelFieldAccess(ModelSQL, ModelView):
|
||||
|
||||
default = {}
|
||||
accesses = dict((m, default) for m in models)
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute(*field_access.join(model_field,
|
||||
condition=field_access.field == model_field.id
|
||||
).join(ir_model,
|
||||
@@ -856,12 +854,12 @@ class ModelData(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
model_data = cls.__table__()
|
||||
|
||||
super(ModelData, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.6: remove inherit
|
||||
if table.column_exist('inherit'):
|
||||
|
||||
+12
-15
@@ -97,12 +97,11 @@ class Module(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
|
||||
# Migration from 3.6: remove double module
|
||||
old_table = 'ir_module_module'
|
||||
if TableHandler.table_exist(cursor, old_table):
|
||||
TableHandler.table_rename(cursor, old_table, cls._table)
|
||||
if TableHandler.table_exist(old_table):
|
||||
TableHandler.table_rename(old_table, cls._table)
|
||||
|
||||
super(Module, cls).__register__(module_name)
|
||||
|
||||
@@ -241,7 +240,7 @@ class Module(ModelSQL, ModelView):
|
||||
Dependency = pool.get('ir.module.dependency')
|
||||
module_table = Module.__table__()
|
||||
dep_table = Dependency.__table__()
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
for module in modules:
|
||||
cursor.execute(*dep_table.join(module_table,
|
||||
condition=(dep_table.module == module_table.id)
|
||||
@@ -344,12 +343,11 @@ class ModuleDependency(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
|
||||
# Migration from 3.6: remove double module
|
||||
old_table = 'ir_module_module_dependency'
|
||||
if TableHandler.table_exist(cursor, old_table):
|
||||
TableHandler.table_rename(cursor, old_table, cls._table)
|
||||
if TableHandler.table_exist(old_table):
|
||||
TableHandler.table_rename(old_table, cls._table)
|
||||
|
||||
super(ModuleDependency, cls).__register__(module_name)
|
||||
|
||||
@@ -385,22 +383,22 @@ class ModuleConfigWizardItem(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
model_data = ModelData.__table__()
|
||||
|
||||
# Migration from 3.6: remove double module
|
||||
old_table = 'ir_module_module_config_wizard_item'
|
||||
if TableHandler.table_exist(cursor, old_table):
|
||||
TableHandler.table_rename(cursor, old_table, cls._table)
|
||||
if TableHandler.table_exist(old_table):
|
||||
TableHandler.table_rename(old_table, cls._table)
|
||||
cursor.execute(*model_data.update(
|
||||
columns=[model_data.model],
|
||||
values=[cls.__name__],
|
||||
where=(model_data.model ==
|
||||
'ir.module.module.config_wizard.item')))
|
||||
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migrate from 2.2 remove name
|
||||
table.drop_column('name')
|
||||
@@ -533,8 +531,8 @@ class ModuleInstallUpgrade(Wizard):
|
||||
|
||||
@classmethod
|
||||
def check_access(cls):
|
||||
# Use new cursor to prevent lock when installing modules
|
||||
with Transaction().new_cursor():
|
||||
# Use new transaction to prevent lock when installing modules
|
||||
with Transaction().new_transaction():
|
||||
super(ModuleInstallUpgrade, cls).check_access()
|
||||
|
||||
@staticmethod
|
||||
@@ -559,7 +557,7 @@ class ModuleInstallUpgrade(Wizard):
|
||||
pool = Pool()
|
||||
Module = pool.get('ir.module')
|
||||
Lang = pool.get('ir.lang')
|
||||
with Transaction().new_cursor() as transaction:
|
||||
with Transaction().new_transaction():
|
||||
modules = Module.search([
|
||||
('state', 'in', ['to upgrade', 'to remove', 'to install']),
|
||||
])
|
||||
@@ -568,7 +566,6 @@ class ModuleInstallUpgrade(Wizard):
|
||||
('translatable', '=', True),
|
||||
])
|
||||
lang = [x.code for x in langs]
|
||||
transaction.cursor.commit()
|
||||
if update:
|
||||
pool.init(update=update, lang=lang)
|
||||
return 'done'
|
||||
|
||||
@@ -34,7 +34,7 @@ class Property(ModelSQL, ModelView):
|
||||
models = cls._models_get_cache.get(None)
|
||||
if models:
|
||||
return models
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
model = Model.__table__()
|
||||
cursor.execute(*model.select(model.model, model.name,
|
||||
order_by=model.name.asc))
|
||||
|
||||
+2
-2
@@ -113,7 +113,7 @@ class Rule(ModelSQL, ModelView):
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(Rule, cls).__register__(module_name)
|
||||
table = TableHandler(Transaction().cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.6: replace field, operator and operand by domain
|
||||
table.not_null_action('field', action='remove')
|
||||
@@ -181,7 +181,7 @@ class Rule(ModelSQL, ModelView):
|
||||
RuleGroup_Group = pool.get('ir.rule.group-res.group')
|
||||
User_Group = pool.get('res.user-res.group')
|
||||
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
rule_table = cls.__table__()
|
||||
rule_group = RuleGroup.__table__()
|
||||
rule_group_user = RuleGroup_User.__table__()
|
||||
|
||||
+10
-10
@@ -103,8 +103,7 @@ class Sequence(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.0 rename number_next into number_next_internal
|
||||
table.column_rename('number_next', 'number_next_internal')
|
||||
@@ -117,7 +116,7 @@ class Sequence(ModelSQL, ModelView):
|
||||
for sequence in sequences:
|
||||
if sequence.type != 'incremental':
|
||||
continue
|
||||
if not TableHandler.sequence_exist(cursor,
|
||||
if not TableHandler.sequence_exist(
|
||||
sequence._sql_sequence_name):
|
||||
sequence.create_sql_sequence(sequence.number_next_internal)
|
||||
|
||||
@@ -158,7 +157,7 @@ class Sequence(ModelSQL, ModelView):
|
||||
return Transaction().context.get('code')
|
||||
|
||||
def get_number_next(self, name):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
sql_name = self._sql_sequence_name
|
||||
if sql_sequence and not self._strict:
|
||||
cursor.execute('SELECT '
|
||||
@@ -258,7 +257,7 @@ class Sequence(ModelSQL, ModelView):
|
||||
|
||||
def create_sql_sequence(self, number_next=None):
|
||||
'Create the SQL sequence'
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
param = Flavor.get().param
|
||||
if self.type != 'incremental':
|
||||
return
|
||||
@@ -271,9 +270,9 @@ class Sequence(ModelSQL, ModelView):
|
||||
def update_sql_sequence(self, number_next=None):
|
||||
'Update the SQL sequence'
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
param = Flavor.get().param
|
||||
exist = TableHandler.sequence_exist(cursor, self._sql_sequence_name)
|
||||
exist = TableHandler.sequence_exist(self._sql_sequence_name)
|
||||
if self.type != 'incremental':
|
||||
if exist:
|
||||
self.delete_sql_sequence()
|
||||
@@ -289,7 +288,7 @@ class Sequence(ModelSQL, ModelView):
|
||||
|
||||
def delete_sql_sequence(self):
|
||||
'Delete the SQL sequence'
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
if self.type != 'incremental':
|
||||
return
|
||||
cursor.execute('DROP SEQUENCE "%s"'
|
||||
@@ -319,7 +318,7 @@ class Sequence(ModelSQL, ModelView):
|
||||
def _get_sequence(cls, sequence):
|
||||
if sequence.type == 'incremental':
|
||||
if sql_sequence and not cls._strict:
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute('SELECT nextval(\'"%s"\')'
|
||||
% sequence._sql_sequence_name)
|
||||
number_next, = cursor.fetchone()
|
||||
@@ -382,5 +381,6 @@ class SequenceStrict(Sequence):
|
||||
|
||||
@classmethod
|
||||
def get_id(cls, clause):
|
||||
Transaction().cursor.lock(cls._table)
|
||||
transaction = Transaction()
|
||||
transaction.database.lock(transaction.connection, cls._table)
|
||||
return super(SequenceStrict, cls).get_id(clause)
|
||||
|
||||
@@ -10,7 +10,6 @@ import datetime
|
||||
from trytond.model import ModelSQL, fields
|
||||
from trytond.config import config
|
||||
from .. import backend
|
||||
from ..transaction import Transaction
|
||||
|
||||
__all__ = [
|
||||
'Session', 'SessionWizard',
|
||||
@@ -34,7 +33,7 @@ class Session(ModelSQL):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(Session, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(Transaction().cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.index_action('create_uid', 'add')
|
||||
|
||||
@staticmethod
|
||||
|
||||
+34
-35
@@ -19,7 +19,7 @@ from sql.aggregate import Max
|
||||
from ..model import ModelView, ModelSQL, fields, Unique
|
||||
from ..wizard import Wizard, StateView, StateTransition, StateAction, \
|
||||
Button
|
||||
from ..tools import file_open, reduce_ids, grouped_slice
|
||||
from ..tools import file_open, reduce_ids, grouped_slice, cursor_dict
|
||||
from .. import backend
|
||||
from ..pyson import PYSONEncoder, Eval
|
||||
from ..transaction import Transaction
|
||||
@@ -93,9 +93,10 @@ class Translation(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
ir_translation = cls.__table__()
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
# Migration from 1.8: new field src_md5
|
||||
src_md5_exist = table.column_exist('src_md5')
|
||||
if not src_md5_exist:
|
||||
@@ -111,7 +112,7 @@ class Translation(ModelSQL, ModelView):
|
||||
# Migration from 1.8: fill new field src_md5
|
||||
if not src_md5_exist:
|
||||
offset = 0
|
||||
limit = cursor.IN_MAX
|
||||
limit = transaction.database.IN_MAX
|
||||
translations = True
|
||||
while translations:
|
||||
translations = cls.search([], offset=offset, limit=limit)
|
||||
@@ -121,7 +122,7 @@ class Translation(ModelSQL, ModelView):
|
||||
cls.write([translation], {
|
||||
'src_md5': src_md5,
|
||||
})
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.not_null_action('src_md5', action='add')
|
||||
|
||||
# Migration from 2.2 and 2.8
|
||||
@@ -129,12 +130,12 @@ class Translation(ModelSQL, ModelView):
|
||||
[-1], where=(ir_translation.res_id == Null)
|
||||
| (ir_translation.res_id == 0)))
|
||||
|
||||
table = TableHandler(Transaction().cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.index_action(['lang', 'type', 'name'], 'add')
|
||||
|
||||
@classmethod
|
||||
def register_model(cls, model, module_name):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
ir_translation = cls.__table__()
|
||||
|
||||
if not model.__doc__:
|
||||
@@ -173,7 +174,7 @@ class Translation(ModelSQL, ModelView):
|
||||
|
||||
@classmethod
|
||||
def register_fields(cls, model, module_name):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
ir_translation = cls.__table__()
|
||||
|
||||
# Prefetch field translations
|
||||
@@ -189,7 +190,7 @@ class Translation(ModelSQL, ModelView):
|
||||
& ir_translation.type.in_(
|
||||
('field', 'help', 'selection'))
|
||||
& ir_translation.name.in_(names))))
|
||||
for trans in cursor.dictfetchall():
|
||||
for trans in cursor_dict(cursor):
|
||||
if trans['type'] == 'field':
|
||||
trans_fields[trans['name']] = trans
|
||||
elif trans['type'] == 'help':
|
||||
@@ -260,7 +261,7 @@ class Translation(ModelSQL, ModelView):
|
||||
|
||||
@classmethod
|
||||
def register_error_messages(cls, model, module_name):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
ir_translation = cls.__table__()
|
||||
|
||||
cursor.execute(*ir_translation.select(
|
||||
@@ -268,7 +269,7 @@ class Translation(ModelSQL, ModelView):
|
||||
where=((ir_translation.lang == 'en_US')
|
||||
& (ir_translation.type == 'error')
|
||||
& (ir_translation.name == model.__name__))))
|
||||
trans_error = dict((t['src'], t) for t in cursor.dictfetchall())
|
||||
trans_error = {t['src']: t for t in cursor_dict(cursor)}
|
||||
|
||||
errors = model._get_error_messages()
|
||||
for error in set(errors):
|
||||
@@ -285,7 +286,7 @@ class Translation(ModelSQL, ModelView):
|
||||
|
||||
@classmethod
|
||||
def register_wizard(cls, wizard, module_name):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
ir_translation = cls.__table__()
|
||||
|
||||
# Prefetch button translations
|
||||
@@ -294,7 +295,7 @@ class Translation(ModelSQL, ModelView):
|
||||
where=((ir_translation.lang == 'en_US')
|
||||
& (ir_translation.type == 'wizard_button')
|
||||
& (ir_translation.name.like(wizard.__name__ + ',%')))))
|
||||
trans_buttons = dict((t['name'], t) for t in cursor.dictfetchall())
|
||||
trans_buttons = {t['name']: t for t in cursor_dict(cursor)}
|
||||
|
||||
def update_insert_button(state_name, button):
|
||||
trans_name = '%s,%s,%s' % (
|
||||
@@ -430,12 +431,13 @@ class Translation(ModelSQL, ModelView):
|
||||
else:
|
||||
to_fetch = ids
|
||||
if to_fetch:
|
||||
cursor = Transaction().cursor
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
table = cls.__table__()
|
||||
fuzzy_sql = table.fuzzy == False
|
||||
if Transaction().context.get('fuzzy_translation', False):
|
||||
fuzzy_sql = None
|
||||
in_max = cursor.IN_MAX // 7
|
||||
in_max = transaction.database.IN_MAX // 7
|
||||
for sub_to_fetch in grouped_slice(to_fetch, in_max):
|
||||
red_sql = reduce_ids(table.res_id, sub_to_fetch)
|
||||
where = And(((table.lang == lang),
|
||||
@@ -469,8 +471,8 @@ class Translation(ModelSQL, ModelView):
|
||||
ModelFields = pool.get('ir.model.field')
|
||||
Model = pool.get('ir.model')
|
||||
Config = pool.get('ir.configuration')
|
||||
cursor = Transaction().cursor
|
||||
in_max = cursor.IN_MAX
|
||||
transaction = Transaction()
|
||||
in_max = transaction.database.IN_MAX
|
||||
|
||||
if len(ids) > in_max:
|
||||
for i in range(0, len(ids), in_max):
|
||||
@@ -611,7 +613,7 @@ class Translation(ModelSQL, ModelView):
|
||||
if trans != -1:
|
||||
return trans
|
||||
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = cls.__table__()
|
||||
where = ((table.lang == lang)
|
||||
& (table.type == ttype)
|
||||
@@ -640,9 +642,10 @@ class Translation(ModelSQL, ModelView):
|
||||
'''
|
||||
res = {}
|
||||
clause = []
|
||||
cursor = Transaction().cursor
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
table = cls.__table__()
|
||||
if len(args) > cursor.IN_MAX:
|
||||
if len(args) > transaction.database.IN_MAX:
|
||||
for sub_args in grouped_slice(args):
|
||||
res.update(cls.get_sources(list(sub_args)))
|
||||
return res
|
||||
@@ -670,7 +673,7 @@ class Translation(ModelSQL, ModelView):
|
||||
where &= table.src == source
|
||||
clause.append(where)
|
||||
if clause:
|
||||
in_max = cursor.IN_MAX // 7
|
||||
in_max = transaction.database.IN_MAX // 7
|
||||
for sub_clause in grouped_slice(clause, in_max):
|
||||
cursor.execute(*table.select(
|
||||
table.lang, table.type, table.name, table.src,
|
||||
@@ -696,7 +699,7 @@ class Translation(ModelSQL, ModelView):
|
||||
ModelView._fields_view_get_cache.clear()
|
||||
vlist = [x.copy() for x in vlist]
|
||||
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = cls.__table__()
|
||||
for vals in vlist:
|
||||
if not vals.get('module'):
|
||||
@@ -1017,7 +1020,7 @@ class TranslationSet(Wizard):
|
||||
if not reports:
|
||||
return
|
||||
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
translation = Translation.__table__()
|
||||
for report in reports:
|
||||
cursor.execute(*translation.select(
|
||||
@@ -1026,9 +1029,7 @@ class TranslationSet(Wizard):
|
||||
& (translation.type == 'odt')
|
||||
& (translation.name == report.report_name)
|
||||
& (translation.module == report.module or '')))
|
||||
trans_reports = {}
|
||||
for trans in cursor.dictfetchall():
|
||||
trans_reports[trans['src']] = trans
|
||||
trans_reports = {t['src']: t for t in cursor_dict(cursor)}
|
||||
|
||||
strings = []
|
||||
|
||||
@@ -1119,7 +1120,7 @@ class TranslationSet(Wizard):
|
||||
|
||||
if not views:
|
||||
return
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
translation = Translation.__table__()
|
||||
for view in views:
|
||||
cursor.execute(*translation.select(
|
||||
@@ -1128,9 +1129,7 @@ class TranslationSet(Wizard):
|
||||
& (translation.type == 'view')
|
||||
& (translation.name == view.model)
|
||||
& (translation.module == view.module)))
|
||||
trans_views = {}
|
||||
for trans in cursor.dictfetchall():
|
||||
trans_views[trans['src']] = trans
|
||||
trans_views = {t['src']: t for t in cursor_dict(cursor)}
|
||||
|
||||
xml = (view.arch or '').strip()
|
||||
if not xml:
|
||||
@@ -1465,7 +1464,7 @@ class TranslationUpdate(Wizard):
|
||||
def do_update(self, action):
|
||||
pool = Pool()
|
||||
Translation = pool.get('ir.translation')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
translation = Translation.__table__()
|
||||
lang = self.start.language.code
|
||||
columns = [translation.name.as_('name'),
|
||||
@@ -1478,7 +1477,7 @@ class TranslationUpdate(Wizard):
|
||||
where=(translation.lang == lang)
|
||||
& translation.type.in_(self._source_types))))
|
||||
to_create = []
|
||||
for row in cursor.dictfetchall():
|
||||
for row in cursor_dict(cursor):
|
||||
to_create.append({
|
||||
'name': row['name'],
|
||||
'res_id': row['res_id'],
|
||||
@@ -1499,7 +1498,7 @@ class TranslationUpdate(Wizard):
|
||||
where=(translation.lang == lang)
|
||||
& translation.type.in_(self._ressource_types))))
|
||||
to_create = []
|
||||
for row in cursor.dictfetchall():
|
||||
for row in cursor_dict(cursor):
|
||||
to_create.append({
|
||||
'name': row['name'],
|
||||
'res_id': row['res_id'],
|
||||
@@ -1518,7 +1517,7 @@ class TranslationUpdate(Wizard):
|
||||
- translation.select(*columns,
|
||||
where=(translation.lang == lang)
|
||||
& translation.type.in_(self._updatable_types))))
|
||||
for row in cursor.dictfetchall():
|
||||
for row in cursor_dict(cursor):
|
||||
cursor.execute(*translation.update(
|
||||
[translation.fuzzy, translation.src],
|
||||
[True, row['src']],
|
||||
@@ -1541,7 +1540,7 @@ class TranslationUpdate(Wizard):
|
||||
& (translation.value != Null),
|
||||
group_by=translation.src))
|
||||
|
||||
for row in cursor.dictfetchall():
|
||||
for row in cursor_dict(cursor):
|
||||
cursor.execute(*translation.update(
|
||||
[translation.fuzzy, translation.value],
|
||||
[True, row['value']],
|
||||
|
||||
@@ -74,8 +74,8 @@ class Trigger(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = TableHandler(cls, module_name)
|
||||
sql_table = cls.__table__()
|
||||
|
||||
super(Trigger, cls).__register__(module_name)
|
||||
@@ -187,7 +187,7 @@ class Trigger(ModelSQL, ModelView):
|
||||
TriggerLog = pool.get('ir.trigger.log')
|
||||
Model = pool.get(trigger.model.model)
|
||||
ActionModel = pool.get(trigger.action_model.model)
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
trigger_log = TriggerLog.__table__()
|
||||
ids = map(int, records)
|
||||
|
||||
@@ -218,7 +218,7 @@ class Trigger(ModelSQL, ModelView):
|
||||
red_sql = reduce_ids(trigger_log.record_id, sub_ids)
|
||||
cursor.execute(*trigger_log.select(
|
||||
trigger_log.record_id, Max(trigger_log.create_date),
|
||||
where=red_sql & (trigger_log.trigger == trigger.id),
|
||||
where=(red_sql & (trigger_log.trigger == trigger.id)),
|
||||
group_by=trigger_log.record_id))
|
||||
delay = dict(cursor.fetchall())
|
||||
for record_id in sub_ids:
|
||||
@@ -236,8 +236,8 @@ class Trigger(ModelSQL, ModelView):
|
||||
microseconds = int(timepart_full[1])
|
||||
else:
|
||||
microseconds = 0
|
||||
delay[record_id] = datetime.datetime(year, month, day,
|
||||
hours, minutes, seconds, microseconds)
|
||||
delay[record_id] = datetime.datetime(year, month,
|
||||
day, hours, minutes, seconds, microseconds)
|
||||
if (datetime.datetime.now() - delay[record_id]
|
||||
>= trigger.minimum_time_delay):
|
||||
new_ids.append(record_id)
|
||||
@@ -307,5 +307,5 @@ class TriggerLog(ModelSQL):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(TriggerLog, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(Transaction().cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.index_action(['trigger', 'record_id'], 'add')
|
||||
|
||||
@@ -225,9 +225,9 @@ class UIMenu(ModelSQL, ModelView):
|
||||
pool = Pool()
|
||||
ActionKeyword = pool.get('ir.action.keyword')
|
||||
action_keywords = []
|
||||
cursor = Transaction().cursor
|
||||
for i in range(0, len(menus), cursor.IN_MAX):
|
||||
sub_menus = menus[i:i + cursor.IN_MAX]
|
||||
transaction = Transaction()
|
||||
for i in range(0, len(menus), transaction.database.IN_MAX):
|
||||
sub_menus = menus[i:i + transaction.database.IN_MAX]
|
||||
action_keywords += ActionKeyword.search([
|
||||
('keyword', '=', 'tree_open'),
|
||||
('model', 'in', [str(menu) for menu in sub_menus]),
|
||||
|
||||
@@ -85,8 +85,7 @@ class View(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.4 arch moved into data
|
||||
if table.column_exist('arch'):
|
||||
@@ -95,7 +94,7 @@ class View(ModelSQL, ModelView):
|
||||
super(View, cls).__register__(module_name)
|
||||
|
||||
# New instance to refresh definition
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 1.0 arch no more required
|
||||
table.not_null_action('arch', action='remove')
|
||||
@@ -336,16 +335,14 @@ class ViewTreeState(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.8: table name changed
|
||||
table.table_rename(cursor, 'ir_ui_view_tree_expanded_state',
|
||||
cls._table)
|
||||
table.table_rename('ir_ui_view_tree_expanded_state', cls._table)
|
||||
|
||||
super(ViewTreeState, cls).__register__(module_name)
|
||||
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.index_action(['model', 'domain', 'user', 'child_name'], 'add')
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -97,7 +97,7 @@ class Many2One(Field):
|
||||
return SQLType('INTEGER', 'INTEGER')
|
||||
|
||||
def convert_domain_mptt(self, domain, tables):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table, _ = tables[None]
|
||||
name, operator, ids = domain
|
||||
red_sql = reduce_ids(table.id, ids)
|
||||
|
||||
@@ -72,7 +72,7 @@ class Property(Function):
|
||||
Property = pool.get('ir.property')
|
||||
IrModel = pool.get('ir.model')
|
||||
Field = pool.get('ir.model.field')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
name, operator, value = domain
|
||||
|
||||
@@ -135,7 +135,7 @@ class Property(Function):
|
||||
return [('id', 'in', [x[0] for x in props])]
|
||||
|
||||
else:
|
||||
other_ids = [x[0] for x in cursor.fetchall()]
|
||||
other_ids = [x[0] for x in fetchall]
|
||||
|
||||
res_ids = Model.search(['OR',
|
||||
('id', 'in', [x[0] for x in props]),
|
||||
|
||||
+43
-42
@@ -13,7 +13,7 @@ from sql.aggregate import Count, Max
|
||||
from trytond.model import ModelStorage, ModelView
|
||||
from trytond.model import fields
|
||||
from trytond import backend
|
||||
from trytond.tools import reduce_ids, grouped_slice
|
||||
from trytond.tools import reduce_ids, grouped_slice, cursor_dict
|
||||
from trytond.const import OPERATORS
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
@@ -127,7 +127,7 @@ class ModelSQL(ModelStorage):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
sql_table = cls.__table__()
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(ModelSQL, cls).__register__(module_name)
|
||||
|
||||
@@ -137,10 +137,9 @@ class ModelSQL(ModelStorage):
|
||||
pool = Pool()
|
||||
|
||||
# create/update table in the database
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
if cls._history:
|
||||
history_table = TableHandler(
|
||||
cursor, cls, module_name, history=True)
|
||||
history_table = TableHandler(cls, module_name, history=True)
|
||||
history_table.index_action('id', action='add')
|
||||
|
||||
for field_name, field in cls._fields.iteritems():
|
||||
@@ -241,9 +240,8 @@ class ModelSQL(ModelStorage):
|
||||
def _update_history_table(cls):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
if cls._history:
|
||||
table = TableHandler(Transaction().cursor, cls)
|
||||
history_table = TableHandler(Transaction().cursor, cls,
|
||||
history=True)
|
||||
table = TableHandler(cls)
|
||||
history_table = TableHandler(cls, history=True)
|
||||
for column_name in table._columns:
|
||||
string = ''
|
||||
if column_name in cls._fields:
|
||||
@@ -309,7 +307,7 @@ class ModelSQL(ModelStorage):
|
||||
pool = Pool()
|
||||
ModelAccess = pool.get('ir.model.access')
|
||||
User = pool.get('res.user')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
ModelAccess.check(cls.__name__, 'read')
|
||||
|
||||
@@ -339,7 +337,7 @@ class ModelSQL(ModelStorage):
|
||||
@classmethod
|
||||
def __insert_history(cls, ids, deleted=False):
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
cursor = transaction.connection.cursor()
|
||||
if not cls._history:
|
||||
return
|
||||
user = transaction.user
|
||||
@@ -366,7 +364,7 @@ class ModelSQL(ModelStorage):
|
||||
cursor.execute(*history.insert(hcolumns,
|
||||
table.select(*columns, where=where)))
|
||||
else:
|
||||
if cursor.has_multirow_insert():
|
||||
if transaction.database.has_multirow_insert():
|
||||
cursor.execute(*history.insert(hcolumns,
|
||||
[[id_, CurrentTimestamp(), user]
|
||||
for id_ in sub_ids]))
|
||||
@@ -380,7 +378,7 @@ class ModelSQL(ModelStorage):
|
||||
if not cls._history:
|
||||
return
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
cursor = transaction.connection.cursor()
|
||||
table = cls.__table__()
|
||||
history = cls.__table_history__()
|
||||
columns = []
|
||||
@@ -449,7 +447,7 @@ class ModelSQL(ModelStorage):
|
||||
@classmethod
|
||||
def __check_timestamp(cls, ids):
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
cursor = transaction.connection.cursor()
|
||||
table = cls.__table__()
|
||||
if not transaction.timestamp:
|
||||
return
|
||||
@@ -476,7 +474,7 @@ class ModelSQL(ModelStorage):
|
||||
def create(cls, vlist):
|
||||
DatabaseIntegrityError = backend.get('DatabaseIntegrityError')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
cursor = transaction.connection.cursor()
|
||||
pool = Pool()
|
||||
Translation = pool.get('ir.translation')
|
||||
Rule = pool.get('ir.rule')
|
||||
@@ -521,12 +519,13 @@ class ModelSQL(ModelStorage):
|
||||
insert_values.append(field.sql_format(value))
|
||||
|
||||
try:
|
||||
if cursor.has_returning():
|
||||
if transaction.database.has_returning():
|
||||
cursor.execute(*table.insert(insert_columns,
|
||||
[insert_values], [table.id]))
|
||||
id_new, = cursor.fetchone()
|
||||
else:
|
||||
id_new = cursor.nextid(cls._table)
|
||||
id_new = transaction.database.nextid(
|
||||
transaction.connection, cls._table)
|
||||
if id_new:
|
||||
insert_columns.append(table.id)
|
||||
insert_values.append(id_new)
|
||||
@@ -535,10 +534,10 @@ class ModelSQL(ModelStorage):
|
||||
else:
|
||||
cursor.execute(*table.insert(insert_columns,
|
||||
[insert_values]))
|
||||
id_new = cursor.lastid()
|
||||
id_new = transaction.database.lastid(cursor)
|
||||
new_ids.append(id_new)
|
||||
except DatabaseIntegrityError, exception:
|
||||
with Transaction().new_cursor(), \
|
||||
with Transaction().new_transaction(), \
|
||||
Transaction().set_context(_check_access=False):
|
||||
cls.__raise_integrity_error(exception, values)
|
||||
raise
|
||||
@@ -608,7 +607,8 @@ class ModelSQL(ModelStorage):
|
||||
mode='read'):
|
||||
fields_names.append(field_name)
|
||||
super(ModelSQL, cls).read(ids, fields_names=fields_names)
|
||||
cursor = Transaction().cursor
|
||||
transaction = Transaction()
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
if not ids:
|
||||
return []
|
||||
@@ -634,12 +634,12 @@ class ModelSQL(ModelStorage):
|
||||
table = cls.__table__()
|
||||
table_query = cls.table_query()
|
||||
|
||||
in_max = cursor.IN_MAX
|
||||
in_max = transaction.database.IN_MAX
|
||||
history_order = None
|
||||
history_clause = None
|
||||
history_limit = None
|
||||
if (cls._history
|
||||
and Transaction().context.get('_datetime')
|
||||
and transaction.context.get('_datetime')
|
||||
and not table_query):
|
||||
in_max = 1
|
||||
table = cls.__table_history__()
|
||||
@@ -678,8 +678,8 @@ class ModelSQL(ModelStorage):
|
||||
where &= dom_exp
|
||||
cursor.execute(*from_.select(*columns, where=where,
|
||||
order_by=history_order, limit=history_limit))
|
||||
dictfetchall = cursor.dictfetchall()
|
||||
if not len(dictfetchall) == len({}.fromkeys(sub_ids)):
|
||||
fetchall = list(cursor_dict(cursor))
|
||||
if not len(fetchall) == len({}.fromkeys(sub_ids)):
|
||||
if domain:
|
||||
where = red_sql
|
||||
if history_clause:
|
||||
@@ -693,7 +693,7 @@ class ModelSQL(ModelStorage):
|
||||
if rowcount == len({}.fromkeys(sub_ids)):
|
||||
cls.raise_user_error('access_error', cls.__name__)
|
||||
cls.raise_user_error('read_error', cls.__name__)
|
||||
result.extend(dictfetchall)
|
||||
result.extend(fetchall)
|
||||
else:
|
||||
result = [{'id': x} for x in ids]
|
||||
|
||||
@@ -835,7 +835,7 @@ class ModelSQL(ModelStorage):
|
||||
def write(cls, records, values, *args):
|
||||
DatabaseIntegrityError = backend.get('DatabaseIntegrityError')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
cursor = transaction.connection.cursor()
|
||||
pool = Pool()
|
||||
Translation = pool.get('ir.translation')
|
||||
Config = pool.get('ir.configuration')
|
||||
@@ -912,8 +912,8 @@ class ModelSQL(ModelStorage):
|
||||
cursor.execute(*table.update(columns, update_values,
|
||||
where=red_sql))
|
||||
except DatabaseIntegrityError, exception:
|
||||
with Transaction().new_cursor(), \
|
||||
Transaction().set_context(_check_access=False):
|
||||
with Transaction().new_transaction() as transaction, \
|
||||
transaction.set_context(_check_access=False):
|
||||
cls.__raise_integrity_error(exception, values,
|
||||
values.keys())
|
||||
raise
|
||||
@@ -945,7 +945,7 @@ class ModelSQL(ModelStorage):
|
||||
def delete(cls, records):
|
||||
DatabaseIntegrityError = backend.get('DatabaseIntegrityError')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
cursor = transaction.connection.cursor()
|
||||
pool = Pool()
|
||||
Translation = pool.get('ir.translation')
|
||||
Rule = pool.get('ir.rule')
|
||||
@@ -1070,7 +1070,7 @@ class ModelSQL(ModelStorage):
|
||||
try:
|
||||
cursor.execute(*table.delete(where=red_sql))
|
||||
except DatabaseIntegrityError, exception:
|
||||
with Transaction().new_cursor():
|
||||
with Transaction().new_transaction():
|
||||
cls.__raise_integrity_error(exception, {})
|
||||
raise
|
||||
|
||||
@@ -1086,7 +1086,7 @@ class ModelSQL(ModelStorage):
|
||||
pool = Pool()
|
||||
Rule = pool.get('ir.rule')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
cursor = transaction.connection.cursor()
|
||||
|
||||
# Get domain clauses
|
||||
tables, expression = cls.search_domain(domain)
|
||||
@@ -1146,8 +1146,8 @@ class ModelSQL(ModelStorage):
|
||||
return select
|
||||
cursor.execute(*select)
|
||||
|
||||
rows = cursor.dictfetchmany(cursor.IN_MAX)
|
||||
cache = cursor.get_cache()
|
||||
rows = list(cursor_dict(cursor, transaction.database.IN_MAX))
|
||||
cache = transaction.get_cache()
|
||||
if cls.__name__ not in cache:
|
||||
cache[cls.__name__] = LRUDict(cache_size())
|
||||
delete_records = transaction.delete_records.setdefault(cls.__name__,
|
||||
@@ -1195,7 +1195,7 @@ class ModelSQL(ModelStorage):
|
||||
# Can not cache the history value if we are not sure to have fetch all
|
||||
# the rows for each records
|
||||
if (not (cls._history and transaction.context.get('_datetime'))
|
||||
or len(rows) < cursor.IN_MAX):
|
||||
or len(rows) < transaction.database.IN_MAX):
|
||||
rows = list(filter_history(rows))
|
||||
keys = None
|
||||
for data in islice(rows, 0, cache.size_limit):
|
||||
@@ -1215,9 +1215,9 @@ class ModelSQL(ModelStorage):
|
||||
del data[k]
|
||||
cache[cls.__name__].setdefault(data['id'], {}).update(data)
|
||||
|
||||
if len(rows) >= cursor.IN_MAX:
|
||||
if len(rows) >= transaction.database.IN_MAX:
|
||||
if (cls._history
|
||||
and Transaction().context.get('_datetime')
|
||||
and transaction.context.get('_datetime')
|
||||
and not query):
|
||||
columns = columns[:3]
|
||||
else:
|
||||
@@ -1225,7 +1225,7 @@ class ModelSQL(ModelStorage):
|
||||
cursor.execute(*table.select(*columns,
|
||||
where=expression, order_by=order_by,
|
||||
limit=limit, offset=offset))
|
||||
rows = filter_history(cursor.dictfetchall())
|
||||
rows = filter_history(list(cursor_dict(cursor)))
|
||||
|
||||
return cls.browse([x['id'] for x in rows])
|
||||
|
||||
@@ -1278,7 +1278,7 @@ class ModelSQL(ModelStorage):
|
||||
|
||||
@classmethod
|
||||
def _update_mptt(cls, field_names, list_ids, values=None):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
count = None
|
||||
for field_name, ids in zip(field_names, list_ids):
|
||||
field = cls._fields[field_name]
|
||||
@@ -1319,7 +1319,7 @@ class ModelSQL(ModelStorage):
|
||||
'''
|
||||
Rebuild left, right value for the tree.
|
||||
'''
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = cls.__table__()
|
||||
right = left + 1
|
||||
|
||||
@@ -1347,7 +1347,7 @@ class ModelSQL(ModelStorage):
|
||||
- the value (right - left - 1) / 2 will not give
|
||||
the number of children node
|
||||
'''
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = cls.__table__()
|
||||
left = Column(table, left)
|
||||
right = Column(table, right)
|
||||
@@ -1401,8 +1401,9 @@ class ModelSQL(ModelStorage):
|
||||
@classmethod
|
||||
def validate(cls, records):
|
||||
super(ModelSQL, cls).validate(records)
|
||||
cursor = Transaction().cursor
|
||||
if cursor.has_constraint():
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
if transaction.database.has_constraint():
|
||||
return
|
||||
# Works only for a single transaction
|
||||
ids = map(int, records)
|
||||
@@ -1411,7 +1412,7 @@ class ModelSQL(ModelStorage):
|
||||
if isinstance(sql, Unique):
|
||||
columns = [Column(table, c.name) for c in sql.columns]
|
||||
columns.insert(0, table.id)
|
||||
in_max = cursor.IN_MAX // (len(columns) + 1)
|
||||
in_max = transaction.database.IN_MAX // (len(columns) + 1)
|
||||
for sub_ids in grouped_slice(ids, in_max):
|
||||
red_sql = reduce_ids(table.id, sub_ids)
|
||||
|
||||
|
||||
@@ -166,8 +166,8 @@ class ModelStorage(Model):
|
||||
if local_cache:
|
||||
local_cache.clear()
|
||||
|
||||
# Clean cursor cache
|
||||
for cache in Transaction().cursor.cache.itervalues():
|
||||
# Clean transaction cache
|
||||
for cache in Transaction().cache.itervalues():
|
||||
if cls.__name__ in cache:
|
||||
for record in all_records:
|
||||
if record.id in cache[cls.__name__]:
|
||||
@@ -220,8 +220,8 @@ class ModelStorage(Model):
|
||||
# Increase transaction counter
|
||||
Transaction().counter += 1
|
||||
|
||||
# Clean cursor cache
|
||||
for cache in Transaction().cursor.cache.values():
|
||||
# Clean transaction cache
|
||||
for cache in Transaction().cache.values():
|
||||
for cache in (cache, cache.get('_language_cache', {}).values()):
|
||||
if cls.__name__ in cache:
|
||||
for record in records:
|
||||
@@ -1155,7 +1155,7 @@ class ModelStorage(Model):
|
||||
def __init__(self, id=None, **kwargs):
|
||||
_ids = kwargs.pop('_ids', None)
|
||||
_local_cache = kwargs.pop('_local_cache', None)
|
||||
self._cursor = Transaction().cursor
|
||||
self._transaction = Transaction()
|
||||
self._user = Transaction().user
|
||||
self._context = Transaction().context
|
||||
if id is not None:
|
||||
@@ -1166,7 +1166,7 @@ class ModelStorage(Model):
|
||||
else:
|
||||
self._ids = [id]
|
||||
|
||||
self._cursor_cache = self._cursor.get_cache()
|
||||
self._transaction_cache = self._transaction.get_cache()
|
||||
|
||||
if _local_cache is not None:
|
||||
self._local_cache = _local_cache
|
||||
@@ -1178,7 +1178,7 @@ class ModelStorage(Model):
|
||||
|
||||
@property
|
||||
def _cache(self):
|
||||
cache = self._cursor_cache
|
||||
cache = self._transaction_cache
|
||||
if self.__name__ not in cache:
|
||||
cache[self.__name__] = LRUDict(cache_size())
|
||||
return cache[self.__name__]
|
||||
@@ -1275,7 +1275,8 @@ class ModelStorage(Model):
|
||||
index = self._ids.index(self.id)
|
||||
ids = chain(islice(self._ids, index, None),
|
||||
islice(self._ids, 0, max(index - 1, 0)))
|
||||
ids = islice(unique(ifilter(filter_, ids)), self._cursor.IN_MAX)
|
||||
ids = islice(unique(ifilter(filter_, ids)),
|
||||
self._transaction.database.IN_MAX)
|
||||
|
||||
def instantiate(field, value, data):
|
||||
if field._type in ('many2one', 'one2one', 'reference'):
|
||||
@@ -1320,14 +1321,14 @@ class ModelStorage(Model):
|
||||
model2ids = {}
|
||||
model2cache = {}
|
||||
# Read the data
|
||||
with Transaction().set_cursor(self._cursor), \
|
||||
with Transaction().set_current_transaction(self._transaction), \
|
||||
Transaction().set_user(self._user), \
|
||||
Transaction().set_context(self._context):
|
||||
if self.id in self._cache and name in self._cache[self.id]:
|
||||
# Use values from cache
|
||||
ids = islice(chain(islice(self._ids, index, None),
|
||||
islice(self._ids, 0, max(index - 1, 0))),
|
||||
self._cursor.IN_MAX)
|
||||
self._transaction.database.IN_MAX)
|
||||
ffields = {name: ffields[name]}
|
||||
read_data = [{'id': i, name: self._cache[i][name]}
|
||||
for i in ids
|
||||
@@ -1424,11 +1425,11 @@ class ModelStorage(Model):
|
||||
save_values = {}
|
||||
to_create = []
|
||||
to_write = []
|
||||
cursor = records[0]._cursor
|
||||
transaction = records[0]._transaction
|
||||
user = records[0]._user
|
||||
context = records[0]._context
|
||||
for record in records:
|
||||
assert cursor == record._cursor
|
||||
assert transaction == record._transaction
|
||||
assert user == record._user
|
||||
assert context == record._context
|
||||
save_values[record] = record._save_values
|
||||
@@ -1440,7 +1441,7 @@ class ModelStorage(Model):
|
||||
to_write.append(record)
|
||||
transaction = Transaction()
|
||||
try:
|
||||
with transaction.set_cursor(cursor), \
|
||||
with transaction.set_current_transaction(transaction), \
|
||||
transaction.set_user(user), \
|
||||
transaction.set_context(context):
|
||||
if to_create:
|
||||
|
||||
+120
-125
@@ -205,92 +205,92 @@ def load_module_graph(graph, pool, update=None, lang=None):
|
||||
update = []
|
||||
modules_todo = []
|
||||
models_to_update_history = set()
|
||||
cursor = Transaction().cursor
|
||||
|
||||
modules = [x.name for x in graph]
|
||||
cursor.execute(*ir_module.select(ir_module.name, ir_module.state,
|
||||
where=ir_module.name.in_(modules)))
|
||||
module2state = dict(cursor.fetchall())
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
modules = [x.name for x in graph]
|
||||
cursor.execute(*ir_module.select(ir_module.name, ir_module.state,
|
||||
where=ir_module.name.in_(modules)))
|
||||
module2state = dict(cursor.fetchall())
|
||||
|
||||
for package in graph:
|
||||
module = package.name
|
||||
if module not in MODULES:
|
||||
continue
|
||||
logger.info(module)
|
||||
classes = pool.setup(module)
|
||||
package_state = module2state.get(module, 'uninstalled')
|
||||
if (is_module_to_install(module, update)
|
||||
or package_state in ('to install', 'to upgrade')):
|
||||
if package_state not in ('to install', 'to upgrade'):
|
||||
if package_state == 'installed':
|
||||
package_state = 'to upgrade'
|
||||
elif package_state != 'to remove':
|
||||
package_state = 'to install'
|
||||
for child in package.childs:
|
||||
module2state[child.name] = package_state
|
||||
for type in classes.keys():
|
||||
for cls in classes[type]:
|
||||
logger.info('%s:register %s', module, cls.__name__)
|
||||
cls.__register__(module)
|
||||
for model in classes['model']:
|
||||
if hasattr(model, '_history'):
|
||||
models_to_update_history.add(model.__name__)
|
||||
|
||||
# Instanciate a new parser for the package:
|
||||
tryton_parser = convert.TrytondXmlHandler(pool=pool, module=module,
|
||||
module_state=package_state)
|
||||
|
||||
for filename in package.info.get('xml', []):
|
||||
filename = filename.replace('/', os.sep)
|
||||
logger.info('%s:loading %s', module, filename)
|
||||
# Feed the parser with xml content:
|
||||
with tools.file_open(OPJ(module, filename), 'rb') as fp:
|
||||
tryton_parser.parse_xmlstream(fp)
|
||||
|
||||
modules_todo.append((module, list(tryton_parser.to_delete)))
|
||||
|
||||
localedir = '%s/%s' % (package.info['directory'], 'locale')
|
||||
for filename in itertools.chain(
|
||||
iglob('%s/*.po' % localedir),
|
||||
iglob('%s/override/*.po' % localedir)):
|
||||
filename = filename.replace('/', os.sep)
|
||||
lang2 = os.path.splitext(os.path.basename(filename))[0]
|
||||
if lang2 not in lang:
|
||||
continue
|
||||
logger.info('%s:loading %s', module,
|
||||
filename[len(package.info['directory']) + 1:])
|
||||
Translation = pool.get('ir.translation')
|
||||
Translation.translation_import(lang2, module, filename)
|
||||
|
||||
if package_state == 'to remove':
|
||||
for package in graph:
|
||||
module = package.name
|
||||
if module not in MODULES:
|
||||
continue
|
||||
cursor.execute(*ir_module.select(ir_module.id,
|
||||
where=(ir_module.name == package.name)))
|
||||
try:
|
||||
module_id, = cursor.fetchone()
|
||||
cursor.execute(*ir_module.update([ir_module.state],
|
||||
['installed'], where=(ir_module.id == module_id)))
|
||||
except TypeError:
|
||||
cursor.execute(*ir_module.insert(
|
||||
[ir_module.create_uid, ir_module.create_date,
|
||||
ir_module.name, ir_module.state],
|
||||
[[0, CurrentTimestamp(), package.name, 'installed']]))
|
||||
module2state[package.name] = 'installed'
|
||||
logger.info(module)
|
||||
classes = pool.setup(module)
|
||||
package_state = module2state.get(module, 'uninstalled')
|
||||
if (is_module_to_install(module, update)
|
||||
or package_state in ('to install', 'to upgrade')):
|
||||
if package_state not in ('to install', 'to upgrade'):
|
||||
if package_state == 'installed':
|
||||
package_state = 'to upgrade'
|
||||
elif package_state != 'to remove':
|
||||
package_state = 'to install'
|
||||
for child in package.childs:
|
||||
module2state[child.name] = package_state
|
||||
for type in classes.keys():
|
||||
for cls in classes[type]:
|
||||
logger.info('%s:register %s', module, cls.__name__)
|
||||
cls.__register__(module)
|
||||
for model in classes['model']:
|
||||
if hasattr(model, '_history'):
|
||||
models_to_update_history.add(model.__name__)
|
||||
|
||||
cursor.commit()
|
||||
# Instanciate a new parser for the package:
|
||||
tryton_parser = convert.TrytondXmlHandler(pool=pool,
|
||||
module=module, module_state=package_state)
|
||||
|
||||
for model_name in models_to_update_history:
|
||||
model = pool.get(model_name)
|
||||
if model._history:
|
||||
logger.info('history:update %s', model.__name__)
|
||||
model._update_history_table()
|
||||
for filename in package.info.get('xml', []):
|
||||
filename = filename.replace('/', os.sep)
|
||||
logger.info('%s:loading %s', module, filename)
|
||||
# Feed the parser with xml content:
|
||||
with tools.file_open(OPJ(module, filename), 'rb') as fp:
|
||||
tryton_parser.parse_xmlstream(fp)
|
||||
|
||||
# Vacuum :
|
||||
while modules_todo:
|
||||
(module, to_delete) = modules_todo.pop()
|
||||
convert.post_import(pool, module, to_delete)
|
||||
modules_todo.append((module, list(tryton_parser.to_delete)))
|
||||
|
||||
cursor.commit()
|
||||
localedir = '%s/%s' % (package.info['directory'], 'locale')
|
||||
for filename in itertools.chain(
|
||||
iglob('%s/*.po' % localedir),
|
||||
iglob('%s/override/*.po' % localedir)):
|
||||
filename = filename.replace('/', os.sep)
|
||||
lang2 = os.path.splitext(os.path.basename(filename))[0]
|
||||
if lang2 not in lang:
|
||||
continue
|
||||
logger.info('%s:loading %s', module,
|
||||
filename[len(package.info['directory']) + 1:])
|
||||
Translation = pool.get('ir.translation')
|
||||
Translation.translation_import(lang2, module, filename)
|
||||
|
||||
if package_state == 'to remove':
|
||||
continue
|
||||
cursor.execute(*ir_module.select(ir_module.id,
|
||||
where=(ir_module.name == package.name)))
|
||||
try:
|
||||
module_id, = cursor.fetchone()
|
||||
cursor.execute(*ir_module.update([ir_module.state],
|
||||
['installed'], where=(ir_module.id == module_id)))
|
||||
except TypeError:
|
||||
cursor.execute(*ir_module.insert(
|
||||
[ir_module.create_uid, ir_module.create_date,
|
||||
ir_module.name, ir_module.state],
|
||||
[[0, CurrentTimestamp(), package.name,
|
||||
'installed'],
|
||||
]))
|
||||
module2state[package.name] = 'installed'
|
||||
|
||||
Transaction().connection.commit()
|
||||
|
||||
for model_name in models_to_update_history:
|
||||
model = pool.get(model_name)
|
||||
if model._history:
|
||||
logger.info('history:update %s', model.__name__)
|
||||
model._update_history_table()
|
||||
|
||||
# Vacuum :
|
||||
while modules_todo:
|
||||
(module, to_delete) = modules_todo.pop()
|
||||
convert.post_import(pool, module, to_delete)
|
||||
|
||||
|
||||
def get_module_list():
|
||||
@@ -367,63 +367,58 @@ def load_modules(database_name, pool, update=None, lang=None):
|
||||
def _load_modules():
|
||||
global res
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
|
||||
# Migration from 3.6: remove double module
|
||||
old_table = 'ir_module_module'
|
||||
new_table = 'ir_module'
|
||||
if TableHandler.table_exist(cursor, old_table):
|
||||
TableHandler.table_rename(cursor, old_table, new_table)
|
||||
if update:
|
||||
cursor.execute(*ir_module.select(ir_module.name,
|
||||
where=ir_module.state.in_(('installed', 'to install',
|
||||
'to upgrade', 'to remove'))))
|
||||
else:
|
||||
cursor.execute(*ir_module.select(ir_module.name,
|
||||
where=ir_module.state.in_(('installed', 'to upgrade',
|
||||
'to remove'))))
|
||||
module_list = [name for (name,) in cursor.fetchall()]
|
||||
if update:
|
||||
module_list += update
|
||||
graph = create_graph(module_list)[0]
|
||||
with Transaction().connection.cursor() as cursor:
|
||||
# Migration from 3.6: remove double module
|
||||
old_table = 'ir_module_module'
|
||||
new_table = 'ir_module'
|
||||
if TableHandler.table_exist(old_table):
|
||||
TableHandler.table_rename(old_table, new_table)
|
||||
if update:
|
||||
cursor.execute(*ir_module.select(ir_module.name,
|
||||
where=ir_module.state.in_(('installed', 'to install',
|
||||
'to upgrade', 'to remove'))))
|
||||
else:
|
||||
cursor.execute(*ir_module.select(ir_module.name,
|
||||
where=ir_module.state.in_(('installed', 'to upgrade',
|
||||
'to remove'))))
|
||||
module_list = [name for (name,) in cursor.fetchall()]
|
||||
if update:
|
||||
module_list += update
|
||||
graph = create_graph(module_list)[0]
|
||||
|
||||
try:
|
||||
load_module_graph(graph, pool, update, lang)
|
||||
except Exception:
|
||||
cursor.rollback()
|
||||
raise
|
||||
|
||||
if update:
|
||||
cursor.execute(*ir_module.select(ir_module.name,
|
||||
where=(ir_module.state == 'to remove')))
|
||||
fetchall = cursor.fetchall()
|
||||
if fetchall:
|
||||
for (mod_name,) in fetchall:
|
||||
# TODO check if ressource not updated by the user
|
||||
cursor.execute(*ir_model_data.select(ir_model_data.model,
|
||||
ir_model_data.db_id,
|
||||
where=(ir_model_data.module == mod_name),
|
||||
order_by=ir_model_data.id.desc))
|
||||
for rmod, rid in cursor.fetchall():
|
||||
Model = pool.get(rmod)
|
||||
Model.delete([Model(rid)])
|
||||
cursor.commit()
|
||||
cursor.execute(*ir_module.update([ir_module.state],
|
||||
['uninstalled'],
|
||||
if update:
|
||||
cursor.execute(*ir_module.select(ir_module.name,
|
||||
where=(ir_module.state == 'to remove')))
|
||||
cursor.commit()
|
||||
res = False
|
||||
fetchall = cursor.fetchall()
|
||||
if fetchall:
|
||||
for (mod_name,) in fetchall:
|
||||
# TODO check if ressource not updated by the user
|
||||
cursor.execute(*ir_model_data.select(
|
||||
ir_model_data.model, ir_model_data.db_id,
|
||||
where=(ir_model_data.module == mod_name),
|
||||
order_by=ir_model_data.id.desc))
|
||||
for rmod, rid in cursor.fetchall():
|
||||
Model = pool.get(rmod)
|
||||
Model.delete([Model(rid)])
|
||||
Transaction().connection.commit()
|
||||
cursor.execute(*ir_module.update([ir_module.state],
|
||||
['uninstalled'],
|
||||
where=(ir_module.state == 'to remove')))
|
||||
Transaction().connection.commit()
|
||||
res = False
|
||||
|
||||
Module = pool.get('ir.module')
|
||||
Module.update_list()
|
||||
cursor.commit()
|
||||
Module = pool.get('ir.module')
|
||||
Module.update_list()
|
||||
Cache.resets(database_name)
|
||||
|
||||
if not Transaction().cursor:
|
||||
if not Transaction().connection:
|
||||
with Transaction().start(database_name, 0):
|
||||
_load_modules()
|
||||
else:
|
||||
with Transaction().new_cursor(), \
|
||||
with Transaction().new_transaction(), \
|
||||
Transaction().set_user(0), \
|
||||
Transaction().reset_context():
|
||||
_load_modules()
|
||||
|
||||
+2
-2
@@ -55,7 +55,7 @@ class Pool(object):
|
||||
|
||||
def __new__(cls, database_name=None):
|
||||
if database_name is None:
|
||||
database_name = Transaction().cursor.database_name
|
||||
database_name = Transaction().database.name
|
||||
result = cls._instances.get(database_name)
|
||||
if result:
|
||||
return result
|
||||
@@ -69,7 +69,7 @@ class Pool(object):
|
||||
|
||||
def __init__(self, database_name=None):
|
||||
if database_name is None:
|
||||
database_name = Transaction().cursor.database_name
|
||||
database_name = Transaction().database.name
|
||||
self.database_name = database_name
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -34,7 +34,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
|
||||
if method == 'login':
|
||||
try:
|
||||
database = Database(database_name).connect()
|
||||
cursor = database.cursor()
|
||||
cursor = database.get_connection().cursor()
|
||||
cursor.close()
|
||||
except Exception:
|
||||
return False
|
||||
@@ -87,7 +87,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
|
||||
if not config.getboolean('database', 'list'):
|
||||
raise Exception('AccessDenied')
|
||||
with Transaction().start(None, 0, close=True) as transaction:
|
||||
return transaction.database.list(transaction.cursor)
|
||||
return transaction.database.list()
|
||||
elif method == 'create':
|
||||
return create(*args, **kwargs)
|
||||
elif method == 'restore':
|
||||
@@ -168,33 +168,25 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
|
||||
else:
|
||||
result = [rpc.result(meth(i, *c_args, **c_kwargs))
|
||||
for i in inst]
|
||||
if not rpc.readonly:
|
||||
transaction.cursor.commit()
|
||||
except DatabaseOperationalError:
|
||||
transaction.cursor.rollback()
|
||||
if count and not rpc.readonly:
|
||||
transaction.rollback()
|
||||
continue
|
||||
raise
|
||||
except (NotLogged, ConcurrencyException, UserError, UserWarning):
|
||||
logger.debug(log_message, *log_args, exc_info=True)
|
||||
transaction.cursor.rollback()
|
||||
raise
|
||||
except Exception:
|
||||
logger.error(log_message, *log_args, exc_info=True)
|
||||
transaction.cursor.rollback()
|
||||
raise
|
||||
Cache.resets(database_name)
|
||||
with Transaction().start(database_name, 0) as transaction:
|
||||
pool = Pool(database_name)
|
||||
Session = pool.get('ir.session')
|
||||
try:
|
||||
try:
|
||||
with Transaction().start(database_name, 0) as transaction:
|
||||
pool = Pool(database_name)
|
||||
Session = pool.get('ir.session')
|
||||
Session.reset(session)
|
||||
except DatabaseOperationalError:
|
||||
logger.debug('Reset session failed', exc_info=True)
|
||||
# Silently fail when reseting session
|
||||
transaction.cursor.rollback()
|
||||
else:
|
||||
transaction.cursor.commit()
|
||||
except DatabaseOperationalError:
|
||||
logger.debug('Reset session failed', exc_info=True)
|
||||
logger.debug('Result: %s', result)
|
||||
return result
|
||||
|
||||
@@ -216,14 +208,13 @@ def create(database_name, password, lang, admin_password):
|
||||
try:
|
||||
with Transaction().start(None, 0, close=True, autocommit=True) \
|
||||
as transaction:
|
||||
transaction.database.create(transaction.cursor, database_name)
|
||||
transaction.cursor.commit()
|
||||
transaction.database.create(transaction.connection, database_name)
|
||||
|
||||
with Transaction().start(database_name, 0) as transaction:
|
||||
Database.init(transaction.cursor)
|
||||
transaction.cursor.execute(*ir_configuration.insert(
|
||||
with Transaction().start(database_name, 0) as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
Database(database_name).init()
|
||||
cursor.execute(*ir_configuration.insert(
|
||||
[ir_configuration.language], [[lang]]))
|
||||
transaction.cursor.commit()
|
||||
|
||||
pool = Pool(database_name)
|
||||
pool.init(update=['res', 'ir'], lang=[lang])
|
||||
@@ -244,7 +235,6 @@ def create(database_name, password, lang, admin_password):
|
||||
Module = pool.get('ir.module')
|
||||
if Module:
|
||||
Module.update_list()
|
||||
transaction.cursor.commit()
|
||||
res = True
|
||||
except Exception:
|
||||
logger.error('CREATE DB: %s failed', database_name, exc_info=True)
|
||||
@@ -257,16 +247,15 @@ def create(database_name, password, lang, admin_password):
|
||||
def drop(database_name, password):
|
||||
Database = backend.get('Database')
|
||||
security.check_super(password)
|
||||
Database(database_name).close()
|
||||
database = Database(database_name)
|
||||
database.close()
|
||||
# Sleep to let connections close
|
||||
time.sleep(1)
|
||||
|
||||
with Transaction().start(None, 0, close=True, autocommit=True) \
|
||||
as transaction:
|
||||
cursor = transaction.cursor
|
||||
try:
|
||||
Database.drop(cursor, database_name)
|
||||
cursor.commit()
|
||||
database.drop(transaction.connection, database_name)
|
||||
except Exception:
|
||||
logger.error('DROP DB: %s failed', database_name, exc_info=True)
|
||||
raise
|
||||
@@ -296,9 +285,7 @@ def restore(database_name, password, data, update=False):
|
||||
Database = backend.get('Database')
|
||||
security.check_super(password)
|
||||
try:
|
||||
database = Database(database_name).connect()
|
||||
cursor = database.cursor()
|
||||
cursor.close(close=True)
|
||||
Database(database_name).connect()
|
||||
existing = True
|
||||
except Exception:
|
||||
existing = False
|
||||
@@ -307,8 +294,8 @@ def restore(database_name, password, data, update=False):
|
||||
Database.restore(database_name, data)
|
||||
logger.info('RESTORE DB: %s', database_name)
|
||||
if update:
|
||||
with Transaction().start(database_name, 0) as transaction:
|
||||
cursor = transaction.cursor
|
||||
with Transaction().start(database_name, 0) as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
cursor.execute(*ir_lang.select(ir_lang.code,
|
||||
where=ir_lang.translatable))
|
||||
lang = [x[0] for x in cursor.fetchall()]
|
||||
|
||||
+15
-22
@@ -2,7 +2,6 @@
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from ..model import ModelSQL, fields
|
||||
from .. import backend
|
||||
from ..transaction import Transaction
|
||||
from ..pool import Pool, PoolMeta
|
||||
|
||||
__all__ = [
|
||||
@@ -24,13 +23,12 @@ class UIMenuGroup(ModelSQL):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
# Migration from 1.0 table name change
|
||||
TableHandler.table_rename(cursor, 'ir_ui_menu_group_rel', cls._table)
|
||||
TableHandler.sequence_rename(cursor, 'ir_ui_menu_group_rel_id_seq',
|
||||
TableHandler.table_rename('ir_ui_menu_group_rel', cls._table)
|
||||
TableHandler.sequence_rename('ir_ui_menu_group_rel_id_seq',
|
||||
cls._table + '_id_seq')
|
||||
# Migration from 2.0 menu_id and gid renamed into menu group
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.column_rename('menu_id', 'menu')
|
||||
table.column_rename('gid', 'group')
|
||||
super(UIMenuGroup, cls).__register__(module_name)
|
||||
@@ -66,13 +64,12 @@ class ActionGroup(ModelSQL):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
# Migration from 1.0 table name change
|
||||
TableHandler.table_rename(cursor, 'ir_action_group_rel', cls._table)
|
||||
TableHandler.sequence_rename(cursor, 'ir_action_group_rel_id_seq',
|
||||
TableHandler.table_rename('ir_action_group_rel', cls._table)
|
||||
TableHandler.sequence_rename('ir_action_group_rel_id_seq',
|
||||
cls._table + '_id_seq')
|
||||
# Migration from 2.0 action_id and gid renamed into action and group
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.column_rename('action_id', 'action')
|
||||
table.column_rename('gid', 'group')
|
||||
super(ActionGroup, cls).__register__(module_name)
|
||||
@@ -121,13 +118,11 @@ class ModelFieldGroup(ModelSQL):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
# Migration from 1.0 table name change
|
||||
TableHandler.table_rename(cursor, 'ir_model_field_group_rel',
|
||||
cls._table)
|
||||
TableHandler.sequence_rename(cursor, 'ir_model_field_group_rel_id_seq',
|
||||
TableHandler.table_rename('ir_model_field_group_rel', cls._table)
|
||||
TableHandler.sequence_rename('ir_model_field_group_rel_id_seq',
|
||||
cls._table + '_id_seq')
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
# Migration from 2.6: field_id and group_id renamed to field and group
|
||||
table.column_rename('field_id', 'field')
|
||||
table.column_rename('group_id', 'group')
|
||||
@@ -181,14 +176,13 @@ class RuleGroupGroup(ModelSQL):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
# Migration from 1.0 table name change
|
||||
TableHandler.table_rename(cursor, 'group_rule_group_rel', cls._table)
|
||||
TableHandler.sequence_rename(cursor, 'group_rule_group_rel_id_seq',
|
||||
TableHandler.table_rename('group_rule_group_rel', cls._table)
|
||||
TableHandler.sequence_rename('group_rule_group_rel_id_seq',
|
||||
cls._table + '_id_seq')
|
||||
# Migration from 2.0 rule_group_id and group_id renamed into rule_group
|
||||
# and group
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.column_rename('rule_group_id', 'rule_group')
|
||||
table.column_rename('group_id', 'group')
|
||||
super(RuleGroupGroup, cls).__register__(module_name)
|
||||
@@ -205,14 +199,13 @@ class RuleGroupUser(ModelSQL):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
# Migration from 1.0 table name change
|
||||
TableHandler.table_rename(cursor, 'user_rule_group_rel', cls._table)
|
||||
TableHandler.sequence_rename(cursor, 'user_rule_group_rel_id_seq',
|
||||
TableHandler.table_rename('user_rule_group_rel', cls._table)
|
||||
TableHandler.sequence_rename('user_rule_group_rel_id_seq',
|
||||
cls._table + '_id_seq')
|
||||
# Migration from 2.0 rule_group_id and user_id renamed into rule_group
|
||||
# and user
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.column_rename('rule_group_id', 'rule_group')
|
||||
table.column_rename('user_id', 'user')
|
||||
super(RuleGroupUser, cls).__register__(module_name)
|
||||
|
||||
+10
-11
@@ -112,9 +112,9 @@ class User(ModelSQL, ModelView):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
super(User, cls).__register__(module_name)
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 1.6
|
||||
|
||||
@@ -243,7 +243,7 @@ class User(ModelSQL, ModelView):
|
||||
args.extend((users, cls._convert_vals(values)))
|
||||
super(User, cls).write(*args)
|
||||
# Clean cursor cache as it could be filled by domain_get
|
||||
for cache in Transaction().cursor.cache.itervalues():
|
||||
for cache in Transaction().cache.itervalues():
|
||||
if cls.__name__ in cache:
|
||||
for user in all_users:
|
||||
if user.id in cache[cls.__name__]:
|
||||
@@ -450,7 +450,7 @@ class User(ModelSQL, ModelView):
|
||||
result = cls._get_login_cache.get(login)
|
||||
if result:
|
||||
return result
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = cls.__table__()
|
||||
cursor.execute(*table.select(table.id, table.password_hash,
|
||||
where=(table.login == login) & (table.active == True)))
|
||||
@@ -543,7 +543,7 @@ class LoginAttempt(ModelSQL):
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
super(LoginAttempt, cls).__register__(module_name)
|
||||
table = TableHandler(Transaction().cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
|
||||
# Migration from 2.8: remove user
|
||||
table.drop_column('user')
|
||||
@@ -562,13 +562,13 @@ class LoginAttempt(ModelSQL):
|
||||
|
||||
@classmethod
|
||||
def remove(cls, login):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = cls.__table__()
|
||||
cursor.execute(*table.delete(where=table.login == login))
|
||||
|
||||
@classmethod
|
||||
def count(cls, login):
|
||||
cursor = Transaction().cursor
|
||||
cursor = Transaction().connection.cursor()
|
||||
table = cls.__table__()
|
||||
cursor.execute(*table.select(Count(Literal(1)),
|
||||
where=(table.login == login)
|
||||
@@ -618,13 +618,12 @@ class UserGroup(ModelSQL):
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
TableHandler = backend.get('TableHandler')
|
||||
cursor = Transaction().cursor
|
||||
# Migration from 1.0 table name change
|
||||
TableHandler.table_rename(cursor, 'res_group_user_rel', cls._table)
|
||||
TableHandler.sequence_rename(cursor, 'res_group_user_rel_id_seq',
|
||||
TableHandler.table_rename('res_group_user_rel', cls._table)
|
||||
TableHandler.sequence_rename('res_group_user_rel_id_seq',
|
||||
cls._table + '_id_seq')
|
||||
# Migration from 2.0 uid and gid rename into user and group
|
||||
table = TableHandler(cursor, cls, module_name)
|
||||
table = TableHandler(cls, module_name)
|
||||
table.column_rename('uid', 'user')
|
||||
table.column_rename('gid', 'group')
|
||||
super(UserGroup, cls).__register__(module_name)
|
||||
|
||||
+4
-7
@@ -20,24 +20,22 @@ def _get_pool(dbname):
|
||||
|
||||
|
||||
def login(dbname, loginname, password, cache=True):
|
||||
with Transaction().start(dbname, 0) as transaction:
|
||||
with Transaction().start(dbname, 0):
|
||||
pool = _get_pool(dbname)
|
||||
User = pool.get('res.user')
|
||||
user_id = User.get_login(loginname, password)
|
||||
transaction.cursor.commit()
|
||||
if user_id:
|
||||
if not cache:
|
||||
return user_id
|
||||
with Transaction().start(dbname, user_id) as transaction:
|
||||
with Transaction().start(dbname, user_id):
|
||||
Session = pool.get('ir.session')
|
||||
session, = Session.create([{}])
|
||||
transaction.cursor.commit()
|
||||
return user_id, session.key
|
||||
return False
|
||||
|
||||
|
||||
def logout(dbname, user, session):
|
||||
with Transaction().start(dbname, 0) as transaction:
|
||||
with Transaction().start(dbname, 0):
|
||||
pool = _get_pool(dbname)
|
||||
Session = pool.get('ir.session')
|
||||
sessions = Session.search([
|
||||
@@ -48,7 +46,6 @@ def logout(dbname, user, session):
|
||||
session, = sessions
|
||||
name = session.create_uid.login
|
||||
Session.delete(sessions)
|
||||
transaction.cursor.commit()
|
||||
return name
|
||||
|
||||
|
||||
@@ -73,4 +70,4 @@ def check(dbname, user, session):
|
||||
else:
|
||||
return user
|
||||
finally:
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
+13
-10
@@ -78,18 +78,20 @@ class TrytonServer(object):
|
||||
if not self.options.update:
|
||||
self.start_servers()
|
||||
|
||||
Database = backend.get('Database')
|
||||
for db_name in self.options.database_names:
|
||||
init[db_name] = False
|
||||
try:
|
||||
with Transaction().start(db_name, 0) as transaction:
|
||||
cursor = transaction.cursor
|
||||
with Transaction().start(db_name, 0) as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
database = Database(db_name)
|
||||
database.connect()
|
||||
if self.options.update:
|
||||
if not cursor.test():
|
||||
if not database.test():
|
||||
self.logger.info("init db")
|
||||
backend.get('Database').init(cursor)
|
||||
database.init(cursor)
|
||||
init[db_name] = True
|
||||
cursor.commit()
|
||||
elif not cursor.test():
|
||||
elif not database.test():
|
||||
raise Exception("'%s' is not a Tryton database!" %
|
||||
db_name)
|
||||
except Exception:
|
||||
@@ -98,9 +100,11 @@ class TrytonServer(object):
|
||||
|
||||
for db_name in self.options.database_names:
|
||||
if self.options.update:
|
||||
with Transaction().start(db_name, 0) as transaction:
|
||||
cursor = transaction.cursor
|
||||
if not cursor.test():
|
||||
with Transaction().start(db_name, 0) as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
database = Database(db_name)
|
||||
database.connect()
|
||||
if not database.test():
|
||||
raise Exception("'%s' is not a Tryton database!"
|
||||
% db_name)
|
||||
lang = Table('ir_lang')
|
||||
@@ -145,7 +149,6 @@ class TrytonServer(object):
|
||||
User.write([admin], {
|
||||
'password': password,
|
||||
})
|
||||
transaction.cursor.commit()
|
||||
|
||||
if self.options.update:
|
||||
self.logger.info('Update/Init succeed!')
|
||||
|
||||
@@ -384,7 +384,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# With field access
|
||||
@@ -400,7 +400,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# One access disallowed for any group
|
||||
@@ -413,7 +413,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
self.assertRaises(UserError, TestAccess.read, [test.id])
|
||||
self.assertRaises(UserError, getattr, test, 'field1')
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# Two access rules with one group allowed
|
||||
@@ -429,7 +429,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# Two access rules with both allowed
|
||||
@@ -441,7 +441,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# Two access rules with any group allowed
|
||||
@@ -453,7 +453,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# Two access rules with both disallowed
|
||||
@@ -466,7 +466,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
self.assertRaises(UserError, TestAccess.read, [test.id])
|
||||
self.assertRaises(UserError, getattr, test, 'field1')
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# One access disallowed for one group
|
||||
@@ -477,7 +477,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
self.assertRaises(UserError, TestAccess.read, [test.id])
|
||||
self.assertRaises(UserError, getattr, test, 'field1')
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# One access allowed for one group
|
||||
@@ -489,7 +489,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# One access allowed for one other group
|
||||
@@ -502,7 +502,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# One access disallowed for one other group
|
||||
@@ -514,7 +514,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# Two access rules on both fields allowed
|
||||
@@ -536,7 +536,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
TestAccess.read([test.id])
|
||||
test.field1
|
||||
test.field2
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# Two access rules on both fields one allowed and one disallowed
|
||||
@@ -549,7 +549,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
self.assertRaises(UserError, TestAccess.read, [test.id])
|
||||
test.field1
|
||||
self.assertRaises(UserError, getattr, test, 'field2')
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
# Two access rules on both fields disallowed
|
||||
@@ -563,7 +563,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
|
||||
self.assertRaises(UserError, TestAccess.read, [test.id])
|
||||
self.assertRaises(UserError, getattr, test, 'field1')
|
||||
self.assertRaises(UserError, getattr, test, 'field2')
|
||||
transaction.cursor.cache.clear()
|
||||
transaction.cache.clear()
|
||||
test = TestAccess(test.id)
|
||||
|
||||
@with_transaction(context=_context)
|
||||
|
||||
@@ -300,7 +300,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
# We should catch UserError but mysql does not raise an
|
||||
# IntegrityError but an OperationalError
|
||||
self.assertRaises(Exception, IntegerRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
integer5, = IntegerRequired.create([{
|
||||
'integer': 0,
|
||||
@@ -308,7 +308,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assert_(integer5)
|
||||
self.assertEqual(integer5.integer, 0)
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@with_transaction()
|
||||
def test_integer_with_domain(self):
|
||||
@@ -496,7 +496,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
})
|
||||
|
||||
self.assertRaises(UserError, FloatRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
float5, = FloatRequired.create([{
|
||||
'float': 0.0,
|
||||
@@ -742,7 +742,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
})
|
||||
|
||||
self.assertRaises(UserError, NumericRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
numeric5, = NumericRequired.create([{
|
||||
'numeric': Decimal(0),
|
||||
@@ -1044,12 +1044,12 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assertEqual(char2.char, 'Test')
|
||||
|
||||
self.assertRaises(UserError, CharRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, CharRequired.create, [{
|
||||
'char': '',
|
||||
}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
char5, = CharRequired.create([{
|
||||
'char': 'Test',
|
||||
@@ -1068,7 +1068,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assertRaises(Exception, CharSize.write, [char6], {
|
||||
'char': 'foobar',
|
||||
})
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
char7, = Char.create([{
|
||||
'char': u'é',
|
||||
@@ -1299,7 +1299,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assertEqual(text2.text, 'Test')
|
||||
|
||||
self.assertRaises(UserError, TextRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
text5, = TextRequired.create([{
|
||||
'text': 'Test',
|
||||
@@ -1573,7 +1573,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assertEqual(date5.date, datetime.date(2009, 1, 1))
|
||||
|
||||
self.assertRaises(UserError, DateRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
date6, = DateRequired.create([{
|
||||
'date': today,
|
||||
@@ -1829,7 +1829,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
datetime.datetime(2009, 1, 1, 12, 0, 0))
|
||||
|
||||
self.assertRaises(UserError, DatetimeRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
datetime6, = DatetimeRequired.create([{
|
||||
'datetime': today,
|
||||
@@ -2080,7 +2080,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assertEqual(time5.time, datetime.time(12, 0))
|
||||
|
||||
self.assertRaises(UserError, TimeRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
time6, = TimeRequired.create([{
|
||||
'time': evening,
|
||||
@@ -2197,17 +2197,17 @@ class FieldsTestCase(unittest.TestCase):
|
||||
'name': 'one2one3',
|
||||
'one2one': target1.id,
|
||||
}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, One2one.write, [one2one2], {
|
||||
'one2one': target1.id,
|
||||
})
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, One2oneRequired.create, [{
|
||||
'name': 'one2one3',
|
||||
}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
target3, = One2oneTarget.create([{
|
||||
'name': 'target3',
|
||||
@@ -2226,7 +2226,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
'name': 'one2one4',
|
||||
'one2one': target4.id,
|
||||
}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
target5, = One2oneTarget.create([{
|
||||
'name': 'domain',
|
||||
@@ -2434,12 +2434,12 @@ class FieldsTestCase(unittest.TestCase):
|
||||
])
|
||||
self.assertEqual(targets, [])
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, One2manyRequired.create, [{
|
||||
'name': 'origin3',
|
||||
}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
origin3_id, = One2manyRequired.create([{
|
||||
'name': 'origin3',
|
||||
@@ -2642,12 +2642,12 @@ class FieldsTestCase(unittest.TestCase):
|
||||
])
|
||||
self.assertEqual(targets, [])
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Many2manyRequired.create, [{
|
||||
'name': 'origin3',
|
||||
}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
origin3_id, = Many2manyRequired.create([{
|
||||
'name': 'origin3',
|
||||
@@ -2663,7 +2663,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
'name': str(i),
|
||||
} for i in range(6)])
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@with_transaction()
|
||||
def test_many2many_tree(self):
|
||||
@@ -2846,7 +2846,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assertRaises(UserError, ReferenceRequired.create, [{
|
||||
'name': 'reference4',
|
||||
}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
target4, = ReferenceTarget.create([{
|
||||
'name': 'target4_id',
|
||||
@@ -2942,7 +2942,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
Property.write([prop_b], {'char': 'Test'})
|
||||
self.assertEqual(prop_b.char, 'Test')
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
# Test Many2One
|
||||
char_a, = Char.create([{'char': 'Test'}])
|
||||
@@ -2985,7 +2985,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
Property.write([prop_b], {'many2one': char_a.id})
|
||||
self.assertEqual(prop_b.many2one, char_a)
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
# Test Numeric
|
||||
prop_a, = Property.create([{'numeric': Decimal('1.1')}])
|
||||
@@ -3069,7 +3069,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
Property.write([prop_b], {'numeric': Decimal('3.11')})
|
||||
self.assertEqual(prop_b.numeric, Decimal('3.11'))
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
# Test Selection
|
||||
prop_a, = Property.create([{'selection': 'option_a'}])
|
||||
@@ -3173,11 +3173,11 @@ class FieldsTestCase(unittest.TestCase):
|
||||
[{'select': 'hexa', 'dyn_select': '3'}])
|
||||
|
||||
self.assertRaises(UserError, SelectionRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, SelectionRequired.create,
|
||||
[{'select': None}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
selection6, = SelectionRequired.create([{'select': 'latin'}])
|
||||
self.assert_(selection6)
|
||||
@@ -3239,7 +3239,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assert_(dict3.dico == {'a': 1})
|
||||
|
||||
self.assertRaises(UserError, DictRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
dict4, = DictRequired.create([{'dico': dict(a=1)}])
|
||||
self.assert_(dict4.dico == {'a': 1})
|
||||
@@ -3276,7 +3276,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assert_(bin3.binary == fields.Binary.cast(b'default'))
|
||||
|
||||
self.assertRaises(UserError, BinaryRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
bin4, = BinaryRequired.create([{
|
||||
'binary': fields.Binary.cast(b'baz'),
|
||||
@@ -3332,7 +3332,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.assertTrue(all(x.many2one.value <= y.many2one.value
|
||||
for x, y in zip(search, search[1:])))
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
target1, target2 = Many2oneTarget.create([
|
||||
{'value': 1},
|
||||
@@ -3356,7 +3356,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
('many2one.value', '=', 1),
|
||||
]), [search1])
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
for model in ['test.many2one_tree', 'test.many2one_mptt']:
|
||||
pool = Pool()
|
||||
@@ -3413,7 +3413,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
[('many2one', 'not parent_of', clause)])
|
||||
self.assertEqual(result, not_(test))
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@with_transaction()
|
||||
def test_timedelta(self):
|
||||
@@ -3621,7 +3621,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
})
|
||||
|
||||
self.assertRaises(UserError, TimedeltaRequired.create, [{}])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
timedelta6, = TimedeltaRequired.create([{
|
||||
'timedelta': day,
|
||||
|
||||
@@ -21,12 +21,13 @@ class HistoryTestCase(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
pool = Pool()
|
||||
History = pool.get('test.history')
|
||||
cursor = Transaction().cursor
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
table = History.__table__()
|
||||
history_table = History.__table_history__()
|
||||
cursor.execute(*table.delete())
|
||||
cursor.execute(*history_table.delete())
|
||||
cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
@with_transaction()
|
||||
def test_read(self):
|
||||
@@ -42,21 +43,21 @@ class HistoryTestCase(unittest.TestCase):
|
||||
history_id = history.id
|
||||
first = history.create_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 2
|
||||
history.save()
|
||||
second = history.write_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 3
|
||||
history.save()
|
||||
third = history.write_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
for timestamp, value in [
|
||||
(first, 1),
|
||||
@@ -92,14 +93,14 @@ class HistoryTestCase(unittest.TestCase):
|
||||
|
||||
self.assertEqual(first, second)
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 3
|
||||
history.save()
|
||||
third = history.write_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
for timestamp, value in [
|
||||
(first, 2),
|
||||
@@ -121,21 +122,21 @@ class HistoryTestCase(unittest.TestCase):
|
||||
history_id = history.id
|
||||
first = history.create_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 2
|
||||
history.save()
|
||||
second = history.write_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 3
|
||||
history.save()
|
||||
third = history.write_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
revisions = History.history_revisions([history_id])
|
||||
self.assertEqual(revisions, [
|
||||
@@ -156,28 +157,28 @@ class HistoryTestCase(unittest.TestCase):
|
||||
history_id = history.id
|
||||
first = history.create_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 2
|
||||
history.save()
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
History.restore_history([history_id], first)
|
||||
history = History(history_id)
|
||||
self.assertEqual(history.value, 1)
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
History.restore_history([history_id], datetime.datetime.min)
|
||||
self.assertRaises(UserError, History.read, [history_id])
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
History.delete([History(history_id)])
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
History.restore_history([history_id], datetime.datetime.max)
|
||||
self.assertRaises(UserError, History.read, [history_id])
|
||||
@@ -193,20 +194,20 @@ class HistoryTestCase(unittest.TestCase):
|
||||
history.save()
|
||||
history_id = history.id
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 2
|
||||
history.save()
|
||||
second = history.write_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 3
|
||||
history.save()
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
History.restore_history_before([history_id], second)
|
||||
history = History(history_id)
|
||||
@@ -231,13 +232,13 @@ class HistoryTestCase(unittest.TestCase):
|
||||
|
||||
self.assertEqual(first, second)
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 3
|
||||
history.save()
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
History.restore_history([history_id], first)
|
||||
history = History(history_id)
|
||||
@@ -255,14 +256,14 @@ class HistoryTestCase(unittest.TestCase):
|
||||
history.save()
|
||||
first_id = history.id
|
||||
first_stamp = history.create_date
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(value=2)
|
||||
history.save()
|
||||
second_id = history.id
|
||||
second_stamp = history.create_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
first, second = History.search([], order=order)
|
||||
|
||||
@@ -272,7 +273,7 @@ class HistoryTestCase(unittest.TestCase):
|
||||
first.value = 3
|
||||
first.save()
|
||||
third_stamp = first.write_date
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
results = [
|
||||
(first_stamp, [first]),
|
||||
@@ -285,14 +286,14 @@ class HistoryTestCase(unittest.TestCase):
|
||||
with Transaction().set_context(_datetime=timestamp):
|
||||
records = History.search([], order=order)
|
||||
self.assertEqual(records, instances)
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
to_delete, _ = History.search([], order=order)
|
||||
|
||||
self.assertEqual(to_delete.id, second.id)
|
||||
|
||||
History.delete([to_delete])
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
results = [
|
||||
(first_stamp, [first]),
|
||||
@@ -306,7 +307,7 @@ class HistoryTestCase(unittest.TestCase):
|
||||
from_test=True):
|
||||
records = History.search([], order=order)
|
||||
self.assertEqual(records, instances)
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@unittest.skipUnless(backend.name() == 'postgresql',
|
||||
'CURRENT_TIMESTAMP as transaction_timestamp is specific to postgresql')
|
||||
@@ -326,7 +327,7 @@ class HistoryTestCase(unittest.TestCase):
|
||||
second_stamp = history.write_date
|
||||
|
||||
self.assertEqual(first_stamp, second_stamp)
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
results = [
|
||||
(second_stamp, [history], [4]),
|
||||
@@ -340,7 +341,7 @@ class HistoryTestCase(unittest.TestCase):
|
||||
records = History.search([], order=order)
|
||||
self.assertEqual(records, instances)
|
||||
self.assertEqual([x.value for x in records], values)
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@with_transaction()
|
||||
def test_browse(self):
|
||||
@@ -365,7 +366,7 @@ class HistoryTestCase(unittest.TestCase):
|
||||
history.stamp = first_stamp
|
||||
history.save()
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
history.value = 2
|
||||
@@ -379,7 +380,7 @@ class HistoryTestCase(unittest.TestCase):
|
||||
|
||||
second_stamp = line_a.write_date
|
||||
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
history = History(history_id)
|
||||
self.assertEqual(history.value, 2)
|
||||
@@ -403,53 +404,53 @@ class HistoryTestCase(unittest.TestCase):
|
||||
|
||||
@with_transaction()
|
||||
def test_search_cursor_max(self):
|
||||
'Test search with number of history entries at cursor.IN_MAX'
|
||||
'Test search with number of history entries at database.IN_MAX'
|
||||
pool = Pool()
|
||||
History = pool.get('test.history')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
database = transaction.database
|
||||
|
||||
history = History(value=-1)
|
||||
history.save()
|
||||
|
||||
for history.value in range(cursor.IN_MAX + 1):
|
||||
for history.value in range(database.IN_MAX + 1):
|
||||
history.save()
|
||||
|
||||
with transaction.set_context(_datetime=datetime.datetime.max):
|
||||
record, = History.search([])
|
||||
|
||||
self.assertEqual(record.value, cursor.IN_MAX)
|
||||
self.assertEqual(record.value, database.IN_MAX)
|
||||
|
||||
@with_transaction()
|
||||
def test_search_cursor_max_entries(self):
|
||||
'Test search for skipping first history entries at cursor.IN_MAX'
|
||||
'Test search for skipping first history entries at database.IN_MAX'
|
||||
pool = Pool()
|
||||
History = pool.get('test.history')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
database = transaction.database
|
||||
|
||||
for i in xrange(0, 2):
|
||||
history = History(value=-1)
|
||||
history.save()
|
||||
|
||||
for history.value in range(cursor.IN_MAX + 1):
|
||||
for history.value in range(database.IN_MAX + 1):
|
||||
history.save()
|
||||
|
||||
with transaction.set_context(_datetime=datetime.datetime.max):
|
||||
records = History.search([])
|
||||
|
||||
self.assertEqual({r.value for r in records}, {cursor.IN_MAX})
|
||||
self.assertEqual({r.value for r in records}, {database.IN_MAX})
|
||||
self.assertEqual(len(records), 2)
|
||||
|
||||
@with_transaction()
|
||||
def test_search_cursor_max_histories(self):
|
||||
'Test search with number of histories at cursor.IN_MAX'
|
||||
'Test search with number of histories at database.IN_MAX'
|
||||
pool = Pool()
|
||||
History = pool.get('test.history')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.cursor
|
||||
database = transaction.database
|
||||
|
||||
n = cursor.IN_MAX + 1
|
||||
n = database.IN_MAX + 1
|
||||
History.create([{'value': 1}] * n)
|
||||
|
||||
with transaction.set_context(_datetime=datetime.datetime.max):
|
||||
|
||||
@@ -89,7 +89,7 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertRaises(UserError, IntegerRequired.import_data,
|
||||
['integer'], [['']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertEqual(IntegerRequired.import_data(['integer'],
|
||||
[['1'], ['2']]), 2)
|
||||
@@ -154,7 +154,7 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertRaises(UserError, FloatRequired.import_data,
|
||||
['float'], [['']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertEqual(FloatRequired.import_data(['float'],
|
||||
[['1.1'], ['2.2']]), 2)
|
||||
@@ -216,7 +216,7 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertRaises(UserError, NumericRequired.import_data,
|
||||
['numeric'], [['']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertEqual(NumericRequired.import_data(['numeric'],
|
||||
[['1.1'], ['2.2']]), 2)
|
||||
@@ -335,19 +335,19 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertRaises(UserError, Many2one.import_data,
|
||||
['many2one'], [['foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Many2one.import_data,
|
||||
['many2one'], [['Duplicate']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Many2one.import_data,
|
||||
['many2one:id'], [['foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(Exception, Many2one.import_data,
|
||||
['many2one:id'], [['tests.foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@with_transaction()
|
||||
def test_many2many(self):
|
||||
@@ -383,19 +383,19 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertRaises(UserError, Many2many.import_data,
|
||||
['many2many'], [['foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Many2many.import_data,
|
||||
['many2many'], [['Test 1,foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Many2many.import_data,
|
||||
['many2many'], [['Duplicate']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Many2many.import_data,
|
||||
['many2many'], [['Test 1,Duplicate']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@with_transaction()
|
||||
def test_one2many(self):
|
||||
@@ -429,7 +429,7 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
reference, = Reference.search([])
|
||||
self.assertEqual(reference.reference.__name__,
|
||||
'test.import_data.reference.selection')
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertEqual(Reference.import_data(['reference:id'],
|
||||
[['test.import_data.reference.selection,'
|
||||
@@ -437,13 +437,13 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
reference, = Reference.search([])
|
||||
self.assertEqual(reference.reference.__name__,
|
||||
'test.import_data.reference.selection')
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertEqual(Reference.import_data(['reference'],
|
||||
[['']]), 1)
|
||||
reference, = Reference.search([])
|
||||
self.assertEqual(reference.reference, None)
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertEqual(Reference.import_data(['reference'],
|
||||
[['test.import_data.reference.selection,Test'],
|
||||
@@ -451,26 +451,26 @@ class ImportDataTestCase(unittest.TestCase):
|
||||
for reference in Reference.search([]):
|
||||
self.assertEqual(reference.reference.__name__,
|
||||
'test.import_data.reference.selection')
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Reference.import_data,
|
||||
['reference'], [['test.import_data.reference.selection,foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Reference.import_data,
|
||||
['reference'],
|
||||
[['test.import_data.reference.selection,Duplicate']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(UserError, Reference.import_data,
|
||||
['reference:id'],
|
||||
[['test.import_data.reference.selection,foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
self.assertRaises(Exception, Reference.import_data,
|
||||
['reference:id'],
|
||||
[['test.import_data.reference.selection,test.foo']])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
|
||||
def suite():
|
||||
|
||||
@@ -23,7 +23,7 @@ class UrlTestCase(unittest.TestCase):
|
||||
"Test model URLs"
|
||||
pool = Pool()
|
||||
UrlObject = pool.get('test.urlobject')
|
||||
db_name = Transaction().database.database_name
|
||||
db_name = Transaction().database.name
|
||||
|
||||
self.assertEqual(UrlObject.__url__,
|
||||
'tryton://%s/%s/model/test.urlobject' % (
|
||||
@@ -38,7 +38,7 @@ class UrlTestCase(unittest.TestCase):
|
||||
"Test wizard URLs"
|
||||
pool = Pool()
|
||||
UrlWizard = pool.get('test.test_wizard', type='wizard')
|
||||
db_name = Transaction().database.database_name
|
||||
db_name = Transaction().database.name
|
||||
|
||||
self.assertEqual(UrlWizard.__url__,
|
||||
'tryton://%s/%s/wizard/test.test_wizard' % (
|
||||
|
||||
@@ -44,7 +44,7 @@ class ModelSQLTestCase(unittest.TestCase):
|
||||
self.assertTrue(key not in err.message, msg)
|
||||
else:
|
||||
self.fail('UserError should be caught')
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
@with_transaction()
|
||||
def test_check_timestamp(self):
|
||||
@@ -52,11 +52,10 @@ class ModelSQLTestCase(unittest.TestCase):
|
||||
pool = Pool()
|
||||
ModelsqlTimestamp = pool.get('test.modelsql.timestamp')
|
||||
transaction = Transaction()
|
||||
# cursor must be committed between each changes otherwise NOW() returns
|
||||
# always the same timestamp.
|
||||
cursor = transaction.cursor
|
||||
# transaction must be committed between each changes otherwise NOW()
|
||||
# returns always the same timestamp.
|
||||
record, = ModelsqlTimestamp.create([{}])
|
||||
cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
timestamp = ModelsqlTimestamp.read([record.id],
|
||||
['_timestamp'])[0]['_timestamp']
|
||||
@@ -66,7 +65,7 @@ class ModelSQLTestCase(unittest.TestCase):
|
||||
time.sleep(1)
|
||||
|
||||
ModelsqlTimestamp.write([record], {})
|
||||
cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
transaction.timestamp[str(record)] = timestamp
|
||||
self.assertRaises(ConcurrencyException,
|
||||
@@ -78,9 +77,9 @@ class ModelSQLTestCase(unittest.TestCase):
|
||||
|
||||
transaction.timestamp.pop(str(record), None)
|
||||
ModelsqlTimestamp.write([record], {})
|
||||
cursor.commit()
|
||||
transaction.commit()
|
||||
ModelsqlTimestamp.delete([record])
|
||||
cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
|
||||
def suite():
|
||||
|
||||
@@ -16,17 +16,6 @@ def empty_transaction(*args, **kwargs):
|
||||
return True
|
||||
|
||||
|
||||
def manipulate_cursor(*args, **kwargs):
|
||||
'''
|
||||
Just start a transaction in the context manager and close the cursor
|
||||
during the transaction so that the cursor.close in the stop fails
|
||||
'''
|
||||
with Transaction().start(*args, **kwargs) as transaction:
|
||||
transaction.cursor.close()
|
||||
transaction.cursor = None
|
||||
return True
|
||||
|
||||
|
||||
class TransactionTestCase(unittest.TestCase):
|
||||
'Test the Transaction Context manager'
|
||||
|
||||
@@ -43,14 +32,6 @@ class TransactionTestCase(unittest.TestCase):
|
||||
context=CONTEXT)
|
||||
self.assertTrue(empty_transaction(DB_NAME, USER, context=CONTEXT))
|
||||
|
||||
def test_cursorclose(self):
|
||||
'''Manipulate the cursor during the transaction so that
|
||||
the close in transaction stop fails.
|
||||
Ensure that this does not affect opening of another transaction'''
|
||||
self.assertRaises(
|
||||
Exception, manipulate_cursor, DB_NAME, USER, context=CONTEXT)
|
||||
self.assertTrue(empty_transaction(DB_NAME, USER, context=CONTEXT))
|
||||
|
||||
def test_set_user(self):
|
||||
'Test set_user'
|
||||
with Transaction().start(DB_NAME, USER, context=CONTEXT) \
|
||||
@@ -84,6 +65,16 @@ class TransactionTestCase(unittest.TestCase):
|
||||
with Transaction().set_user(2):
|
||||
self.assertEqual(transaction.user, 2)
|
||||
|
||||
def test_stacked_transactions(self):
|
||||
'Test that transactions are stacked / unstacked correctly'
|
||||
with Transaction().start(DB_NAME, USER, context=CONTEXT) \
|
||||
as transaction:
|
||||
with transaction.new_transaction() as new_transaction:
|
||||
self.assertIsNot(new_transaction, transaction)
|
||||
self.assertIsNot(Transaction(), transaction)
|
||||
self.assertIs(Transaction(), new_transaction)
|
||||
self.assertIs(Transaction(), transaction)
|
||||
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromTestCase(TransactionTestCase)
|
||||
|
||||
@@ -46,7 +46,7 @@ class TriggerTestCase(unittest.TestCase):
|
||||
}
|
||||
self.assert_(Trigger.create([values]))
|
||||
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
# on_exclusive
|
||||
for i in range(1, 4):
|
||||
@@ -57,14 +57,14 @@ class TriggerTestCase(unittest.TestCase):
|
||||
combination_values['on_%s' % mode] = True
|
||||
self.assertRaises(UserError, Trigger.create,
|
||||
[combination_values])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
# check_condition
|
||||
condition_values = values.copy()
|
||||
condition_values['condition'] = '='
|
||||
self.assertRaises(UserError, Trigger.create,
|
||||
[condition_values])
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
|
||||
# Restart the cache on the get_triggers method of ir.trigger
|
||||
Trigger._get_triggers_cache.clear()
|
||||
|
||||
@@ -60,15 +60,15 @@ def install_module(name):
|
||||
return
|
||||
|
||||
Module.install(modules)
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
InstallUpgrade = POOL.get('ir.module.install_upgrade',
|
||||
type='wizard')
|
||||
instance_id, _, _ = InstallUpgrade.create()
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
InstallUpgrade(instance_id).transition_upgrade()
|
||||
InstallUpgrade.delete(instance_id)
|
||||
transaction.cursor.commit()
|
||||
transaction.commit()
|
||||
|
||||
|
||||
def with_transaction(user=1, context=None):
|
||||
@@ -78,7 +78,7 @@ def with_transaction(user=1, context=None):
|
||||
transaction = Transaction()
|
||||
with transaction.start(DB_NAME, user, context=context):
|
||||
result = func(*args, **kwargs)
|
||||
transaction.cursor.rollback()
|
||||
transaction.rollback()
|
||||
# Drop the cache as the transaction is rollbacked
|
||||
Cache.drop(DB_NAME)
|
||||
return result
|
||||
@@ -275,10 +275,7 @@ class ModuleTestCase(unittest.TestCase):
|
||||
def db_exist():
|
||||
Database = backend.get('Database')
|
||||
database = Database().connect()
|
||||
cursor = database.cursor()
|
||||
databases = database.list(cursor)
|
||||
cursor.close()
|
||||
return DB_NAME in databases
|
||||
return DB_NAME in database.list()
|
||||
|
||||
|
||||
def create_db():
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 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 itertools import izip
|
||||
|
||||
from .misc import *
|
||||
from .datetime_strftime import *
|
||||
from .decimal_ import *
|
||||
@@ -8,3 +10,13 @@ from .decimal_ import *
|
||||
class ClassProperty(property):
|
||||
def __get__(self, cls, owner):
|
||||
return self.fget.__get__(None, owner)()
|
||||
|
||||
|
||||
def cursor_dict(cursor, size=None):
|
||||
size = cursor.arraysize if size is None else size
|
||||
while True:
|
||||
rows = cursor.fetchmany(size)
|
||||
if not rows:
|
||||
break
|
||||
for row in rows:
|
||||
yield {d[0]: v for d, v in izip(cursor.description, row)}
|
||||
|
||||
@@ -374,7 +374,7 @@ def grouped_slice(records, count=None):
|
||||
'Grouped slice'
|
||||
from trytond.transaction import Transaction
|
||||
if count is None:
|
||||
count = Transaction().cursor.IN_MAX
|
||||
count = Transaction().database.IN_MAX
|
||||
for i in xrange(0, len(records), count):
|
||||
yield islice(records, i, i + count)
|
||||
|
||||
|
||||
+74
-60
@@ -3,20 +3,8 @@
|
||||
from threading import local
|
||||
from sql import Flavor
|
||||
|
||||
from trytond.tools.singleton import Singleton
|
||||
from trytond import backend
|
||||
|
||||
|
||||
class _TransactionManager(object):
|
||||
'''
|
||||
Manage transaction start/stop
|
||||
'''
|
||||
|
||||
def __enter__(self):
|
||||
return Transaction()
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
Transaction().stop()
|
||||
from trytond.config import config
|
||||
|
||||
|
||||
class _AttributeManager(object):
|
||||
@@ -35,30 +23,25 @@ class _AttributeManager(object):
|
||||
setattr(Transaction(), name, value)
|
||||
|
||||
|
||||
class _CursorManager(object):
|
||||
'''
|
||||
Manage cursor of transaction
|
||||
'''
|
||||
class _Local(local):
|
||||
|
||||
def __init__(self, cursor):
|
||||
self.cursor = cursor
|
||||
|
||||
def __enter__(self):
|
||||
return Transaction()
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
Transaction().cursor.close()
|
||||
Transaction().cursor = self.cursor
|
||||
def __init__(self):
|
||||
# Transaction stack control
|
||||
self.transactions = []
|
||||
|
||||
|
||||
class Transaction(local):
|
||||
class Transaction(object):
|
||||
'''
|
||||
Control the transaction
|
||||
'''
|
||||
__metaclass__ = Singleton
|
||||
|
||||
cursor = None
|
||||
_local = _Local()
|
||||
|
||||
cache_keys = {'language', 'fuzzy_translation', '_datetime'}
|
||||
|
||||
database = None
|
||||
readonly = False
|
||||
connection = None
|
||||
close = None
|
||||
user = None
|
||||
context = None
|
||||
@@ -67,6 +50,24 @@ class Transaction(local):
|
||||
delete = None # TODO check to merge with delete_records
|
||||
timestamp = None
|
||||
|
||||
def __new__(cls, new=False):
|
||||
transactions = cls._local.transactions
|
||||
if new or not transactions:
|
||||
instance = super(Transaction, cls).__new__(cls)
|
||||
instance.cache = {}
|
||||
transactions.append(instance)
|
||||
else:
|
||||
instance = transactions[-1]
|
||||
return instance
|
||||
|
||||
def get_cache(self):
|
||||
from trytond.cache import LRUDict
|
||||
keys = tuple(((key, self.context[key])
|
||||
for key in sorted(self.cache_keys)
|
||||
if key in self.context))
|
||||
return self.cache.setdefault((self.user, keys),
|
||||
LRUDict(config.getint('cache', 'model')))
|
||||
|
||||
def start(self, database_name, user, readonly=False, context=None,
|
||||
close=False, autocommit=False):
|
||||
'''
|
||||
@@ -75,7 +76,6 @@ class Transaction(local):
|
||||
Database = backend.get('Database')
|
||||
assert self.user is None
|
||||
assert self.database is None
|
||||
assert self.cursor is None
|
||||
assert self.close is None
|
||||
assert self.context is None
|
||||
if not database_name:
|
||||
@@ -83,11 +83,11 @@ class Transaction(local):
|
||||
else:
|
||||
database = Database(database_name).connect()
|
||||
Flavor.set(Database.flavor)
|
||||
cursor = database.cursor(readonly=readonly,
|
||||
autocommit=autocommit)
|
||||
self.user = user
|
||||
self.database = database
|
||||
self.cursor = cursor
|
||||
self.readonly = readonly
|
||||
self.connection = database.get_connection(readonly=readonly,
|
||||
autocommit=autocommit)
|
||||
self.close = close
|
||||
self.context = context or {}
|
||||
self.create_records = {}
|
||||
@@ -95,24 +95,32 @@ class Transaction(local):
|
||||
self.delete = {}
|
||||
self.timestamp = {}
|
||||
self.counter = 0
|
||||
return _TransactionManager()
|
||||
return self
|
||||
|
||||
def stop(self):
|
||||
'''
|
||||
Stop transaction
|
||||
'''
|
||||
try:
|
||||
self.cursor.close(close=self.close)
|
||||
finally:
|
||||
self.cursor = None
|
||||
self.database = None
|
||||
self.close = None
|
||||
self.user = None
|
||||
self.context = None
|
||||
self.create_records = None
|
||||
self.delete_records = None
|
||||
self.delete = None
|
||||
self.timestamp = None
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
transactions = self._local.transactions
|
||||
if transactions.count(self) == 1:
|
||||
if type is None and not self.readonly:
|
||||
self.commit()
|
||||
try:
|
||||
self.rollback()
|
||||
self.database.put_connection(self.connection)
|
||||
finally:
|
||||
self.database = None
|
||||
self.readonly = False
|
||||
self.connection = None
|
||||
self.close = None
|
||||
self.user = None
|
||||
self.context = None
|
||||
self.create_records = None
|
||||
self.delete_records = None
|
||||
self.delete = None
|
||||
self.timestamp = None
|
||||
current_instance = transactions.pop()
|
||||
assert current_instance is self
|
||||
|
||||
def set_context(self, context=None, **kwargs):
|
||||
if context is None:
|
||||
@@ -143,17 +151,23 @@ class Transaction(local):
|
||||
self.user = user
|
||||
return manager
|
||||
|
||||
def set_cursor(self, cursor):
|
||||
manager = _AttributeManager(cursor=self.cursor)
|
||||
self.cursor = cursor
|
||||
return manager
|
||||
def set_current_transaction(self, transaction):
|
||||
self._local.transactions.append(transaction)
|
||||
return transaction
|
||||
|
||||
def new_cursor(self, autocommit=False, readonly=False):
|
||||
Database = backend.get('Database')
|
||||
manager = _CursorManager(self.cursor)
|
||||
database = Database(self.cursor.database_name).connect()
|
||||
self.cursor = database.cursor(autocommit=autocommit, readonly=readonly)
|
||||
return manager
|
||||
def new_transaction(self, autocommit=False, readonly=False):
|
||||
transaction = Transaction(new=True)
|
||||
return transaction.start(self.database.name, self.user,
|
||||
context=self.context, close=self.close, readonly=readonly,
|
||||
autocommit=autocommit)
|
||||
|
||||
def commit(self):
|
||||
self.connection.commit()
|
||||
|
||||
def rollback(self):
|
||||
for cache in self.cache.itervalues():
|
||||
cache.clear()
|
||||
self.connection.rollback()
|
||||
|
||||
@property
|
||||
def language(self):
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ class URLAccessor(object):
|
||||
raise NotImplementedError
|
||||
|
||||
url_part['name'] = cls.__name__
|
||||
url_part['database'] = Transaction().cursor.database_name
|
||||
url_part['database'] = Transaction().database.name
|
||||
|
||||
local_part = urllib.quote('%(database)s/%(type)s/%(name)s' % url_part)
|
||||
if isinstance(inst, Model) and inst.id:
|
||||
|
||||
Reference in New Issue
Block a user