diff options
Diffstat (limited to 'odoo/sql_db.py')
| -rw-r--r-- | odoo/sql_db.py | 78 |
1 files changed, 50 insertions, 28 deletions
diff --git a/odoo/sql_db.py b/odoo/sql_db.py index 50e4ac5dce7..8414dcf3dcc 100644 --- a/odoo/sql_db.py +++ b/odoo/sql_db.py @@ -10,6 +10,7 @@ the ORM does, in fact. from contextlib import contextmanager from functools import wraps +import itertools import logging import time import uuid @@ -425,42 +426,68 @@ class Cursor(object): def closed(self): return self._closed -class TestCursor(Cursor): - """ A cursor to be used for tests. It keeps the transaction open across - several requests, and simulates committing, rolling back, and closing. - """ - def __init__(self, *args, **kwargs): - super(TestCursor, self).__init__(*args, **kwargs) - # in order to simulate commit and rollback, the cursor maintains a - # savepoint at its last commit - self.execute("SAVEPOINT test_cursor") - # we use a lock to serialize concurrent requests - self._lock = threading.RLock() - def acquire(self): - self._lock.acquire() +class TestCursor(object): + """ A pseudo-cursor to be used for tests, on top of a real cursor. It keeps + the transaction open across requests, and simulates committing, rolling + back, and closing: + + test cursor | queries on actual cursor + ------------------------+--------------------------------------- + cr = TestCursor(...) | SAVEPOINT test_cursor_N + | + cr.execute(query) | query + | + cr.commit() | SAVEPOINT test_cursor_N + | + cr.rollback() | ROLLBACK TO SAVEPOINT test_cursor_N + | + cr.close() | ROLLBACK TO SAVEPOINT test_cursor_N + | - def release(self): - self._lock.release() + """ + _savepoint_seq = itertools.count() - def force_close(self): - super(TestCursor, self).close() + def __init__(self, cursor, lock): + self._closed = False + self._cursor = cursor + # we use a lock to serialize concurrent requests + self._lock = lock + self._lock.acquire() + # in order to simulate commit and rollback, the cursor maintains a + # savepoint at its last commit + self._savepoint = "test_cursor_%s" % next(self._savepoint_seq) + self._cursor.execute('SAVEPOINT "%s"' % self._savepoint) def close(self): if not self._closed: - self.rollback() # for stuff that has not been committed - self.release() + self._closed = True + self._cursor.execute('ROLLBACK TO SAVEPOINT "%s"' % self._savepoint) + self._lock.release() def autocommit(self, on): _logger.debug("TestCursor.autocommit(%r) does nothing", on) def commit(self): - self.execute("RELEASE SAVEPOINT test_cursor") - self.execute("SAVEPOINT test_cursor") + self._cursor.execute('SAVEPOINT "%s"' % self._savepoint) def rollback(self): - self.execute("ROLLBACK TO SAVEPOINT test_cursor") - self.execute("SAVEPOINT test_cursor") + self._cursor.execute('ROLLBACK TO SAVEPOINT "%s"' % self._savepoint) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + if exc_type is None: + self.commit() + self.close() + + def __getattr__(self, name): + value = getattr(self._cursor, name) + if callable(value) and self._closed: + raise psycopg2.OperationalError('Unable to use a closed cursor.') + return value + class LazyCursor(object): """ A proxy object to a cursor. The cursor itself is allocated only if it is @@ -633,11 +660,6 @@ class Connection(object): _logger.debug('create %scursor to %r', cursor_type, self.dsn) return Cursor(self.__pool, self.dbname, self.dsn, serialized=serialized) - def test_cursor(self, serialized=True): - cursor_type = serialized and 'serialized ' or '' - _logger.debug('create test %scursor to %r', cursor_type, self.dsn) - return TestCursor(self.__pool, self.dbname, self.dsn, serialized=serialized) - # serialized_cursor is deprecated - cursors are serialized by default serialized_cursor = cursor |
