summaryrefslogtreecommitdiff
path: root/odoo/sql_db.py
diff options
context:
space:
mode:
Diffstat (limited to 'odoo/sql_db.py')
-rw-r--r--odoo/sql_db.py78
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