From 948ce04649e46520a7f1d94d0249bbc776d27c2b Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 17 Sep 2019 11:42:53 +0000 Subject: [FIX] sql_db: retrieve correct environment When creating a new database from the database manager, with no open cursor, the wrong env was retrieved during the cr.commit() call. 2019-09-09 18:16:07,454 19886 ERROR None odoo.service.db: CREATE DATABASE failed: Traceback (most recent call last): File "/home/odoo/odoo/service/db.py", line 60, in _initialize_db cr.commit() File "/home/odoo/odoo/sql_db.py", line 165, in wrapper return f(self, *args, **kwargs) File "/home/odoo/odoo/sql_db.py", line 389, in commit env = get_env(currentframe(), 2) File "/home/odoo/odoo/sql_db.py", line 72, in get_env env = getattr(frame.f_locals.get('self'), 'env', SENTINEL) File "/home/odoo/odoo/http.py", line 268, in env self._env = odoo.api.Environment(self.cr, self.uid, self.context) File "/home/odoo/odoo/http.py", line 238, in cr raise RuntimeError('request not bound to a database') get_env method checks every frame to find a variable called 'env' As none was found, it used the one of the WebRequest object. In a WebRequest, env is a property that will try to create a new Environment, which fails as no database is available (yet). At eafdf1812d3 the iteration on Environment.envs was removed as envs is a weakset and it introduced potential errors where the content was garbage-collected during the iteration. Go back to the simpler iteration but convert the set to list to avoid garbage-collection issues. Fixes odoo/odoo#36588 closes odoo/odoo#37005 Signed-off-by: Martin Trigaux (mat) Co-authored-by: Raphael Collet --- odoo/sql_db.py | 59 ++++++++++++++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 35 deletions(-) (limited to 'odoo/sql_db.py') diff --git a/odoo/sql_db.py b/odoo/sql_db.py index c7990b025ef..1d1f74748a0 100644 --- a/odoo/sql_db.py +++ b/odoo/sql_db.py @@ -22,6 +22,8 @@ from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ from psycopg2.pool import PoolError from werkzeug import urls +from odoo.api import Environment + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) _logger = logging.getLogger(__name__) @@ -58,23 +60,19 @@ import threading from inspect import currentframe -SENTINEL = object() - - -def get_env(frame, back=0): - """ Retrieve an environment from the given stack frame. """ - for i in range(back): - frame = frame.f_back - while frame: - env = frame.f_locals.get('env', SENTINEL) - if env is not SENTINEL: - return env - env = getattr(frame.f_locals.get('self'), 'env', SENTINEL) - if env is not SENTINEL: - return env - frame = frame.f_back - return None +def flush_env(cr): + """ Retrieve and flush an environment corresponding to the given cursor """ + for env in list(Environment.envs): + if env.cr is cr: + env['base'].flush() + break +def clear_env(cr): + """ Retrieve and clear an environment corresponding to the given cursor """ + for env in list(Environment.envs): + if env.cr is cr: + env.clear() + break import re re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$') @@ -386,9 +384,7 @@ class Cursor(object): def commit(self): """ Perform an SQL `COMMIT` """ - env = get_env(currentframe(), 2) - if env is not None: - env['base'].flush() + flush_env(self) result = self._cnx.commit() for func in self._pop_event_handlers()['commit']: func() @@ -398,9 +394,7 @@ class Cursor(object): def rollback(self): """ Perform an SQL `ROLLBACK` """ - env = get_env(currentframe(), 2) - if env is not None: - env.clear() + clear_env(self) result = self._cnx.rollback() for func in self._pop_event_handlers()['rollback']: func() @@ -428,17 +422,16 @@ class Cursor(object): def savepoint(self, flush=True): """context manager entering in a new savepoint""" name = uuid.uuid1().hex - env = get_env(currentframe(), 2) if flush else None - if env is not None: - env['base'].flush() + if flush: + flush_env(self) self.execute('SAVEPOINT "%s"' % name) try: yield - if env is not None: - env['base'].flush() + if flush: + flush_env(self) except Exception: - if env is not None: - env.clear() + if flush: + clear_env(self) self.execute('ROLLBACK TO SAVEPOINT "%s"' % name) raise else: @@ -495,15 +488,11 @@ class TestCursor(object): _logger.debug("TestCursor.autocommit(%r) does nothing", on) def commit(self): - env = get_env(currentframe(), 1) - if env is not None: - env['base'].flush() + flush_env(self) self._cursor.execute('SAVEPOINT "%s"' % self._savepoint) def rollback(self): - env = get_env(currentframe(), 1) - if env is not None: - env.clear() + clear_env(self) self._cursor.execute('ROLLBACK TO SAVEPOINT "%s"' % self._savepoint) def __enter__(self): -- cgit v1.2.3