2011-05-12 08:46:46 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# OpenERP, Open Source Management Solution
|
|
|
|
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
2011-05-19 07:39:35 +00:00
|
|
|
""" Models registries.
|
2011-05-12 08:46:46 +00:00
|
|
|
|
|
|
|
"""
|
2013-04-04 10:25:24 +00:00
|
|
|
from collections import Mapping
|
2012-08-13 15:05:01 +00:00
|
|
|
from contextlib import contextmanager
|
2011-08-11 11:01:18 +00:00
|
|
|
import logging
|
2012-01-24 12:42:52 +00:00
|
|
|
import threading
|
2011-05-12 08:46:46 +00:00
|
|
|
|
|
|
|
import openerp.sql_db
|
2011-06-14 14:22:26 +00:00
|
|
|
import openerp.osv.orm
|
2011-07-13 15:35:21 +00:00
|
|
|
import openerp.tools
|
2011-07-22 08:10:30 +00:00
|
|
|
import openerp.modules.db
|
2011-08-11 11:01:18 +00:00
|
|
|
import openerp.tools.config
|
2012-11-02 13:47:32 +00:00
|
|
|
from openerp.tools import assertion_report
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2012-01-24 12:42:52 +00:00
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
2013-04-04 10:25:24 +00:00
|
|
|
class Registry(Mapping):
|
2011-06-14 14:22:26 +00:00
|
|
|
""" Model registry for a particular database.
|
|
|
|
|
|
|
|
The registry is essentially a mapping between model names and model
|
|
|
|
instances. There is one registry instance per database.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, db_name):
|
2013-04-04 10:25:24 +00:00
|
|
|
super(Registry, self).__init__()
|
2012-08-13 15:05:01 +00:00
|
|
|
self.models = {} # model name/model instance mapping
|
2011-06-14 14:22:26 +00:00
|
|
|
self._sql_error = {}
|
|
|
|
self._store_function = {}
|
|
|
|
self._init = True
|
|
|
|
self._init_parent = {}
|
2012-11-02 13:47:32 +00:00
|
|
|
self._assertion_report = assertion_report.assertion_report()
|
2012-12-14 14:11:14 +00:00
|
|
|
self.fields_by_model = None
|
2012-08-13 15:05:01 +00:00
|
|
|
|
[IMP] modules, ir.ui.view: improve view validation + avoid validation errors during updates
As of 7.0, RNG validation is not possible for form views
that have a version attribute equal to "7.0", due to the
allowed usage of HTML syntax mixed with the regular OpenERP
view syntax. RNG validation is still enabled for regular
form views (@version missing or less than "7.0"), and for
all other views types.
Validation of 7.0 form views should be improved with the
addition of an assertion-based schema, still to be done.
The above is also complemented with an explicit call to fields_view_get()
during view installation, in order to immediately verify
that the updated view hierarchy does not cause any
issue when loaded along with its related views (i.e
parent and siblings, for inheriting views).
In addition to that, fields_view_get() will now only
consider loading views that belong to modules that have
already been loaded. This avoids a lot of validation errors
during a module update operation, which runs on top of
an existing database with all previous views visible,
even those whose module is not loaded yet.
bzr revid: odo@openerp.com-20120611122758-qcw9xdhupl24busq
2012-06-11 12:27:58 +00:00
|
|
|
# modules fully loaded (maintained during init phase by `loading` module)
|
|
|
|
self._init_modules = set()
|
|
|
|
|
2011-06-14 14:22:26 +00:00
|
|
|
self.db_name = db_name
|
2014-04-09 13:35:15 +00:00
|
|
|
self._db = openerp.sql_db.db_connect(db_name)
|
2011-06-14 14:22:26 +00:00
|
|
|
|
2014-04-08 15:17:36 +00:00
|
|
|
# special cursor for test mode; None means "normal" mode
|
|
|
|
self.test_cr = None
|
|
|
|
|
2012-12-21 13:37:51 +00:00
|
|
|
# Indicates that the registry is
|
|
|
|
self.ready = False
|
2012-12-09 17:04:47 +00:00
|
|
|
|
2012-12-08 18:11:51 +00:00
|
|
|
# Inter-process signaling (used only when openerp.multi_process is True):
|
|
|
|
# The `base_registry_signaling` sequence indicates the whole registry
|
|
|
|
# must be reloaded.
|
|
|
|
# The `base_cache_signaling sequence` indicates all caches must be
|
|
|
|
# invalidated (i.e. cleared).
|
2014-01-16 15:11:57 +00:00
|
|
|
self.base_registry_signaling_sequence = None
|
|
|
|
self.base_cache_signaling_sequence = None
|
2012-12-08 18:11:51 +00:00
|
|
|
|
|
|
|
# Flag indicating if at least one model cache has been cleared.
|
|
|
|
# Useful only in a multi-process context.
|
|
|
|
self._any_cache_cleared = False
|
|
|
|
|
2014-04-09 09:56:04 +00:00
|
|
|
cr = self.cursor()
|
2014-04-09 13:52:25 +00:00
|
|
|
has_unaccent = openerp.modules.db.has_unaccent(cr)
|
2011-08-11 11:01:18 +00:00
|
|
|
if openerp.tools.config['unaccent'] and not has_unaccent:
|
2012-01-24 12:42:52 +00:00
|
|
|
_logger.warning("The option --unaccent was given but no unaccent() function was found in database.")
|
2011-08-11 11:01:18 +00:00
|
|
|
self.has_unaccent = openerp.tools.config['unaccent'] and has_unaccent
|
2011-07-22 08:10:30 +00:00
|
|
|
cr.close()
|
|
|
|
|
2013-04-04 10:25:24 +00:00
|
|
|
#
|
|
|
|
# Mapping abstract methods implementation
|
|
|
|
# => mixin provides methods keys, items, values, get, __eq__, and __ne__
|
|
|
|
#
|
|
|
|
def __len__(self):
|
|
|
|
""" Return the size of the registry. """
|
|
|
|
return len(self.models)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
""" Return an iterator over all model names. """
|
|
|
|
return iter(self.models)
|
|
|
|
|
|
|
|
def __contains__(self, model_name):
|
|
|
|
""" Test whether the model with the given name exists. """
|
|
|
|
return model_name in self.models
|
|
|
|
|
|
|
|
def __getitem__(self, model_name):
|
|
|
|
""" Return the model with the given name or raise KeyError if it doesn't exist."""
|
|
|
|
return self.models[model_name]
|
|
|
|
|
2014-04-08 11:49:36 +00:00
|
|
|
def __call__(self, model_name):
|
|
|
|
""" Same as ``self[model_name]``. """
|
|
|
|
return self.models[model_name]
|
|
|
|
|
2011-06-14 14:22:26 +00:00
|
|
|
def do_parent_store(self, cr):
|
|
|
|
for o in self._init_parent:
|
|
|
|
self.get(o)._parent_store_compute(cr)
|
|
|
|
self._init = False
|
|
|
|
|
|
|
|
def obj_list(self):
|
|
|
|
""" Return the list of model names in this registry."""
|
2013-04-04 10:25:24 +00:00
|
|
|
return self.keys()
|
2011-06-14 14:22:26 +00:00
|
|
|
|
|
|
|
def add(self, model_name, model):
|
|
|
|
""" Add or replace a model in the registry."""
|
|
|
|
self.models[model_name] = model
|
|
|
|
|
2011-09-02 13:27:12 +00:00
|
|
|
def load(self, cr, module):
|
|
|
|
""" Load a given module in the registry.
|
2011-06-14 14:22:26 +00:00
|
|
|
|
2011-09-02 13:27:12 +00:00
|
|
|
At the Python level, the modules are already loaded, but not yet on a
|
|
|
|
per-registry level. This method populates a registry with the given
|
|
|
|
modules, i.e. it instanciates all the classes of a the given module
|
|
|
|
and registers them in the registry.
|
2011-06-14 14:22:26 +00:00
|
|
|
|
2011-09-02 13:27:12 +00:00
|
|
|
"""
|
2012-12-17 21:46:45 +00:00
|
|
|
models_to_load = [] # need to preserve loading order
|
2011-08-31 09:13:25 +00:00
|
|
|
# Instantiate registered classes (via the MetaModel automatic discovery
|
|
|
|
# or via explicit constructor call), and add them to the pool.
|
2011-09-02 13:27:12 +00:00
|
|
|
for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
|
2012-12-17 21:46:45 +00:00
|
|
|
# models register themselves in self.models
|
|
|
|
model = cls.create_instance(self, cr)
|
|
|
|
if model._name not in models_to_load:
|
|
|
|
# avoid double-loading models whose declaration is split
|
|
|
|
models_to_load.append(model._name)
|
|
|
|
return [self.models[m] for m in models_to_load]
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-08-31 07:56:39 +00:00
|
|
|
def clear_caches(self):
|
2011-08-25 12:47:11 +00:00
|
|
|
""" Clear the caches
|
|
|
|
This clears the caches associated to methods decorated with
|
|
|
|
``tools.ormcache`` or ``tools.ormcache_multi`` for all the models.
|
|
|
|
"""
|
|
|
|
for model in self.models.itervalues():
|
|
|
|
model.clear_caches()
|
2012-12-08 18:11:51 +00:00
|
|
|
# Special case for ir_ui_menu which does not use openerp.tools.ormcache.
|
|
|
|
ir_ui_menu = self.models.get('ir.ui.menu')
|
|
|
|
if ir_ui_menu:
|
|
|
|
ir_ui_menu.clear_cache()
|
|
|
|
|
|
|
|
|
|
|
|
# Useful only in a multi-process context.
|
|
|
|
def reset_any_cache_cleared(self):
|
|
|
|
self._any_cache_cleared = False
|
|
|
|
|
|
|
|
# Useful only in a multi-process context.
|
|
|
|
def any_cache_cleared(self):
|
|
|
|
return self._any_cache_cleared
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def setup_multi_process_signaling(cls, cr):
|
|
|
|
if not openerp.multi_process:
|
2014-01-21 17:18:36 +00:00
|
|
|
return None, None
|
2012-12-08 18:11:51 +00:00
|
|
|
|
|
|
|
# Inter-process signaling:
|
|
|
|
# The `base_registry_signaling` sequence indicates the whole registry
|
|
|
|
# must be reloaded.
|
|
|
|
# The `base_cache_signaling sequence` indicates all caches must be
|
|
|
|
# invalidated (i.e. cleared).
|
|
|
|
cr.execute("""SELECT sequence_name FROM information_schema.sequences WHERE sequence_name='base_registry_signaling'""")
|
|
|
|
if not cr.fetchall():
|
|
|
|
cr.execute("""CREATE SEQUENCE base_registry_signaling INCREMENT BY 1 START WITH 1""")
|
|
|
|
cr.execute("""SELECT nextval('base_registry_signaling')""")
|
|
|
|
cr.execute("""CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1""")
|
|
|
|
cr.execute("""SELECT nextval('base_cache_signaling')""")
|
2014-01-21 17:18:36 +00:00
|
|
|
|
|
|
|
cr.execute("""
|
|
|
|
SELECT base_registry_signaling.last_value,
|
|
|
|
base_cache_signaling.last_value
|
|
|
|
FROM base_registry_signaling, base_cache_signaling""")
|
|
|
|
r, c = cr.fetchone()
|
|
|
|
_logger.debug("Multiprocess load registry signaling: [Registry: # %s] "\
|
|
|
|
"[Cache: # %s]",
|
|
|
|
r, c)
|
|
|
|
return r, c
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2014-04-08 15:17:36 +00:00
|
|
|
def enter_test_mode(self):
|
|
|
|
""" Enter the 'test' mode, where one cursor serves several requests. """
|
|
|
|
assert self.test_cr is None
|
2014-04-09 13:35:15 +00:00
|
|
|
self.test_cr = self._db.test_cursor()
|
2014-04-08 15:17:36 +00:00
|
|
|
RegistryManager.enter_test_mode()
|
|
|
|
|
|
|
|
def leave_test_mode(self):
|
|
|
|
""" Leave the test mode. """
|
|
|
|
assert self.test_cr is not None
|
2014-04-14 07:59:06 +00:00
|
|
|
self.test_cr.force_close()
|
2014-04-08 15:17:36 +00:00
|
|
|
self.test_cr = None
|
|
|
|
RegistryManager.leave_test_mode()
|
|
|
|
|
2014-04-09 09:56:04 +00:00
|
|
|
def cursor(self):
|
|
|
|
""" Return a new cursor for the database. The cursor itself may be used
|
|
|
|
as a context manager to commit/rollback and close automatically.
|
|
|
|
"""
|
2014-04-14 07:59:06 +00:00
|
|
|
cr = self.test_cr
|
|
|
|
if cr is not None:
|
2014-04-08 15:17:36 +00:00
|
|
|
# While in test mode, we use one special cursor across requests. The
|
|
|
|
# test cursor uses a reentrant lock to serialize accesses. The lock
|
2014-04-09 09:56:04 +00:00
|
|
|
# is granted here by cursor(), and automatically released by the
|
2014-04-08 15:17:36 +00:00
|
|
|
# cursor itself in its method close().
|
2014-04-14 07:59:06 +00:00
|
|
|
cr.acquire()
|
|
|
|
return cr
|
2014-04-09 13:35:15 +00:00
|
|
|
return self._db.cursor()
|
2014-04-08 12:51:22 +00:00
|
|
|
|
2014-04-08 09:45:13 +00:00
|
|
|
class DummyRLock(object):
|
|
|
|
""" Dummy reentrant lock, to be used while running rpc and js tests """
|
2014-02-09 00:40:05 +00:00
|
|
|
def acquire(self):
|
2014-04-08 09:45:13 +00:00
|
|
|
pass
|
2014-02-09 00:40:05 +00:00
|
|
|
def release(self):
|
2014-04-08 09:45:13 +00:00
|
|
|
pass
|
2014-02-09 00:40:05 +00:00
|
|
|
def __enter__(self):
|
|
|
|
self.acquire()
|
|
|
|
def __exit__(self, type, value, traceback):
|
|
|
|
self.release()
|
|
|
|
|
2011-05-12 08:46:46 +00:00
|
|
|
class RegistryManager(object):
|
2011-05-19 07:39:35 +00:00
|
|
|
""" Model registries manager.
|
|
|
|
|
2011-06-14 14:22:26 +00:00
|
|
|
The manager is responsible for creation and deletion of model
|
2011-05-19 07:39:35 +00:00
|
|
|
registries (essentially database connection/model registry pairs).
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-05-19 07:39:35 +00:00
|
|
|
"""
|
2011-06-14 14:22:26 +00:00
|
|
|
# Mapping between db name and model registry.
|
|
|
|
# Accessed through the methods below.
|
|
|
|
registries = {}
|
2014-04-08 09:45:13 +00:00
|
|
|
_lock = threading.RLock()
|
|
|
|
_saved_lock = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def lock(cls):
|
|
|
|
""" Return the current registry lock. """
|
|
|
|
return cls._lock
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def enter_test_mode(cls):
|
|
|
|
""" Enter the 'test' mode, where the registry is no longer locked. """
|
|
|
|
assert cls._saved_lock is None
|
|
|
|
cls._lock, cls._saved_lock = DummyRLock(), cls._lock
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def leave_test_mode(cls):
|
|
|
|
""" Leave the 'test' mode. """
|
|
|
|
assert cls._saved_lock is not None
|
|
|
|
cls._lock, cls._saved_lock = cls._saved_lock, None
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-06-14 14:22:26 +00:00
|
|
|
@classmethod
|
2012-12-21 13:37:51 +00:00
|
|
|
def get(cls, db_name, force_demo=False, status=None, update_module=False):
|
2011-06-14 14:22:26 +00:00
|
|
|
""" Return a registry for a given database name."""
|
2014-04-08 09:45:13 +00:00
|
|
|
with cls.lock():
|
2013-10-11 09:26:32 +00:00
|
|
|
try:
|
|
|
|
return cls.registries[db_name]
|
|
|
|
except KeyError:
|
|
|
|
return cls.new(db_name, force_demo, status,
|
|
|
|
update_module)
|
|
|
|
finally:
|
|
|
|
# set db tracker - cleaned up at the WSGI
|
|
|
|
# dispatching phase in openerp.service.wsgi_server.application
|
|
|
|
threading.current_thread().dbname = db_name
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-06-14 14:22:26 +00:00
|
|
|
@classmethod
|
|
|
|
def new(cls, db_name, force_demo=False, status=None,
|
2012-12-21 13:37:51 +00:00
|
|
|
update_module=False):
|
2011-06-14 14:22:26 +00:00
|
|
|
""" Create and return a new registry for a given database name.
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-06-14 14:22:26 +00:00
|
|
|
The (possibly) previous registry for that database name is discarded.
|
2011-05-12 08:46:46 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
import openerp.modules
|
2014-04-08 09:45:13 +00:00
|
|
|
with cls.lock():
|
2011-09-16 07:52:27 +00:00
|
|
|
registry = Registry(db_name)
|
|
|
|
|
|
|
|
# Initializing a registry will call general code which will in turn
|
|
|
|
# call registries.get (this object) to obtain the registry being
|
|
|
|
# initialized. Make it available in the registries dictionary then
|
|
|
|
# remove it if an exception is raised.
|
|
|
|
cls.delete(db_name)
|
|
|
|
cls.registries[db_name] = registry
|
|
|
|
try:
|
2014-01-21 17:18:36 +00:00
|
|
|
with registry.cursor() as cr:
|
|
|
|
seq_registry, seq_cache = Registry.setup_multi_process_signaling(cr)
|
|
|
|
registry.base_registry_signaling_sequence = seq_registry
|
|
|
|
registry.base_cache_signaling_sequence = seq_cache
|
2011-09-16 07:52:27 +00:00
|
|
|
# This should be a method on Registry
|
2014-04-09 13:35:15 +00:00
|
|
|
openerp.modules.load_modules(registry._db, force_demo, status, update_module)
|
2011-09-16 07:52:27 +00:00
|
|
|
except Exception:
|
|
|
|
del cls.registries[db_name]
|
|
|
|
raise
|
|
|
|
|
2013-02-12 08:53:11 +00:00
|
|
|
# load_modules() above can replace the registry by calling
|
|
|
|
# indirectly new() again (when modules have to be uninstalled).
|
|
|
|
# Yeah, crazy.
|
|
|
|
registry = cls.registries[db_name]
|
|
|
|
|
2014-04-09 09:56:04 +00:00
|
|
|
cr = registry.cursor()
|
2011-09-16 07:52:27 +00:00
|
|
|
try:
|
|
|
|
registry.do_parent_store(cr)
|
|
|
|
cr.commit()
|
|
|
|
finally:
|
|
|
|
cr.close()
|
|
|
|
|
2012-12-21 13:37:51 +00:00
|
|
|
registry.ready = True
|
2011-09-16 07:52:27 +00:00
|
|
|
|
2013-03-01 14:32:03 +00:00
|
|
|
if update_module:
|
|
|
|
# only in case of update, otherwise we'll have an infinite reload loop!
|
|
|
|
cls.signal_registry_change(db_name)
|
2011-09-30 15:00:26 +00:00
|
|
|
return registry
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-06-14 14:22:26 +00:00
|
|
|
@classmethod
|
|
|
|
def delete(cls, db_name):
|
2012-12-09 19:05:43 +00:00
|
|
|
"""Delete the registry linked to a given database. """
|
2014-04-08 09:45:13 +00:00
|
|
|
with cls.lock():
|
2011-09-16 07:52:27 +00:00
|
|
|
if db_name in cls.registries:
|
2011-09-28 21:13:26 +00:00
|
|
|
cls.registries[db_name].clear_caches()
|
2011-09-16 07:52:27 +00:00
|
|
|
del cls.registries[db_name]
|
2011-09-28 21:13:26 +00:00
|
|
|
|
2011-07-13 15:35:21 +00:00
|
|
|
@classmethod
|
|
|
|
def delete_all(cls):
|
2011-09-28 21:13:26 +00:00
|
|
|
"""Delete all the registries. """
|
2014-04-08 09:45:13 +00:00
|
|
|
with cls.lock():
|
2011-09-28 21:13:26 +00:00
|
|
|
for db_name in cls.registries.keys():
|
|
|
|
cls.delete(db_name)
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-08-25 12:47:11 +00:00
|
|
|
@classmethod
|
2011-08-31 07:56:39 +00:00
|
|
|
def clear_caches(cls, db_name):
|
2011-09-28 21:13:26 +00:00
|
|
|
"""Clear caches
|
2011-08-25 12:47:11 +00:00
|
|
|
|
|
|
|
This clears the caches associated to methods decorated with
|
|
|
|
``tools.ormcache`` or ``tools.ormcache_multi`` for all the models
|
|
|
|
of the given database name.
|
|
|
|
|
|
|
|
This method is given to spare you a ``RegistryManager.get(db_name)``
|
|
|
|
that would loads the given database if it was not already loaded.
|
|
|
|
"""
|
2014-04-08 09:45:13 +00:00
|
|
|
with cls.lock():
|
2011-09-16 07:52:27 +00:00
|
|
|
if db_name in cls.registries:
|
|
|
|
cls.registries[db_name].clear_caches()
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2012-12-08 18:11:51 +00:00
|
|
|
@classmethod
|
|
|
|
def check_registry_signaling(cls, db_name):
|
2013-06-19 10:53:35 +00:00
|
|
|
"""
|
|
|
|
Check if the modules have changed and performs all necessary operations to update
|
|
|
|
the registry of the corresponding database.
|
|
|
|
|
|
|
|
|
|
|
|
:returns: True if changes has been detected in the database and False otherwise.
|
|
|
|
"""
|
|
|
|
changed = False
|
2012-12-08 18:11:51 +00:00
|
|
|
if openerp.multi_process and db_name in cls.registries:
|
2012-12-21 13:37:51 +00:00
|
|
|
registry = cls.get(db_name)
|
2014-04-09 09:56:04 +00:00
|
|
|
cr = registry.cursor()
|
2012-12-08 18:11:51 +00:00
|
|
|
try:
|
|
|
|
cr.execute("""
|
|
|
|
SELECT base_registry_signaling.last_value,
|
|
|
|
base_cache_signaling.last_value
|
|
|
|
FROM base_registry_signaling, base_cache_signaling""")
|
|
|
|
r, c = cr.fetchone()
|
2014-01-16 15:11:57 +00:00
|
|
|
_logger.debug("Multiprocess signaling check: [Registry - old# %s new# %s] "\
|
|
|
|
"[Cache - old# %s new# %s]",
|
|
|
|
registry.base_registry_signaling_sequence, r,
|
|
|
|
registry.base_cache_signaling_sequence, c)
|
2012-12-08 18:11:51 +00:00
|
|
|
# Check if the model registry must be reloaded (e.g. after the
|
|
|
|
# database has been updated by another process).
|
2014-01-16 15:11:57 +00:00
|
|
|
if registry.base_registry_signaling_sequence is not None and registry.base_registry_signaling_sequence != r:
|
2013-06-19 10:53:35 +00:00
|
|
|
changed = True
|
2012-12-08 18:11:51 +00:00
|
|
|
_logger.info("Reloading the model registry after database signaling.")
|
2012-12-21 13:37:51 +00:00
|
|
|
registry = cls.new(db_name)
|
2012-12-08 18:11:51 +00:00
|
|
|
# Check if the model caches must be invalidated (e.g. after a write
|
|
|
|
# occured on another process). Don't clear right after a registry
|
|
|
|
# has been reload.
|
2014-01-16 15:11:57 +00:00
|
|
|
elif registry.base_cache_signaling_sequence is not None and registry.base_cache_signaling_sequence != c:
|
2013-06-19 10:53:35 +00:00
|
|
|
changed = True
|
2012-12-08 18:11:51 +00:00
|
|
|
_logger.info("Invalidating all model caches after database signaling.")
|
|
|
|
registry.clear_caches()
|
|
|
|
registry.reset_any_cache_cleared()
|
|
|
|
# One possible reason caches have been invalidated is the
|
|
|
|
# use of decimal_precision.write(), in which case we need
|
|
|
|
# to refresh fields.float columns.
|
|
|
|
for model in registry.models.values():
|
|
|
|
for column in model._columns.values():
|
|
|
|
if hasattr(column, 'digits_change'):
|
|
|
|
column.digits_change(cr)
|
2013-10-11 10:03:13 +00:00
|
|
|
registry.base_registry_signaling_sequence = r
|
|
|
|
registry.base_cache_signaling_sequence = c
|
2012-12-08 18:11:51 +00:00
|
|
|
finally:
|
|
|
|
cr.close()
|
2013-06-19 10:53:35 +00:00
|
|
|
return changed
|
2012-12-08 18:11:51 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def signal_caches_change(cls, db_name):
|
|
|
|
if openerp.multi_process and db_name in cls.registries:
|
|
|
|
# Check the registries if any cache has been cleared and signal it
|
|
|
|
# through the database to other processes.
|
2012-12-21 13:37:51 +00:00
|
|
|
registry = cls.get(db_name)
|
2012-12-08 18:11:51 +00:00
|
|
|
if registry.any_cache_cleared():
|
|
|
|
_logger.info("At least one model cache has been cleared, signaling through the database.")
|
2014-04-09 09:56:04 +00:00
|
|
|
cr = registry.cursor()
|
2012-12-08 18:11:51 +00:00
|
|
|
r = 1
|
|
|
|
try:
|
|
|
|
cr.execute("select nextval('base_cache_signaling')")
|
|
|
|
r = cr.fetchone()[0]
|
|
|
|
finally:
|
|
|
|
cr.close()
|
|
|
|
registry.base_cache_signaling_sequence = r
|
|
|
|
registry.reset_any_cache_cleared()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def signal_registry_change(cls, db_name):
|
|
|
|
if openerp.multi_process and db_name in cls.registries:
|
2014-01-16 15:11:57 +00:00
|
|
|
_logger.info("Registry changed, signaling through the database")
|
2012-12-21 13:37:51 +00:00
|
|
|
registry = cls.get(db_name)
|
2014-04-09 09:56:04 +00:00
|
|
|
cr = registry.cursor()
|
2012-12-08 18:11:51 +00:00
|
|
|
r = 1
|
|
|
|
try:
|
|
|
|
cr.execute("select nextval('base_registry_signaling')")
|
|
|
|
r = cr.fetchone()[0]
|
|
|
|
finally:
|
|
|
|
cr.close()
|
|
|
|
registry.base_registry_signaling_sequence = r
|
2011-05-12 08:46:46 +00:00
|
|
|
|
2011-09-30 15:00:26 +00:00
|
|
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|