summaryrefslogtreecommitdiff
path: root/odoo/modules
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2020-04-02 06:45:54 +0000
committerRaphael Collet <rco@odoo.com>2020-04-03 11:40:29 +0000
commit6eb7d5c6a07f678e1d1adca7137652e0626ea17e (patch)
treeb9c9c2d8ef5988a48df50daa7c1b12c2dc6d2bd2 /odoo/modules
parente0c2dde3244e1eed6c6fc2541281d0ec5ce86168 (diff)
[IMP] core: add/update foreign keys in batch
This greatly reduces the number of queries to add foreign keys (notably for many2one fields). This saves 6.5% of the total installation time. X-original-commit: 13a2666b4da09fc0ec7608a974dadde45f76fd7a
Diffstat (limited to 'odoo/modules')
-rw-r--r--odoo/modules/registry.py82
1 files changed, 67 insertions, 15 deletions
diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py
index 9d9833308e4..4e1fe137eb5 100644
--- a/odoo/modules/registry.py
+++ b/odoo/modules/registry.py
@@ -113,7 +113,6 @@ class Registry(Mapping):
self._assertion_report = assertion_report.assertion_report()
self._fields_by_model = None
self._ordinary_tables = None
- self._post_init_queue = deque()
self._constraint_queue = deque()
# modules fully loaded (maintained during init phase by `loading` module)
@@ -367,26 +366,33 @@ class Registry(Mapping):
env = odoo.api.Environment(cr, SUPERUSER_ID, context)
models = [env[model_name] for model_name in model_names]
- # make sure the queue does not contain some leftover from a former call
- self._post_init_queue.clear()
- self._is_install = install
+ try:
+ self._post_init_queue = deque()
+ self._foreign_keys = {}
+ self._is_install = install
- for model in models:
- model._auto_init()
- model.init()
+ for model in models:
+ model._auto_init()
+ model.init()
- self._ordinary_tables = None
+ self._ordinary_tables = None
+
+ while self._post_init_queue:
+ func = self._post_init_queue.popleft()
+ func()
- self.check_indexes(cr, model_names)
+ self.check_indexes(cr, model_names)
+ self.check_foreign_keys(cr)
- while self._post_init_queue:
- func = self._post_init_queue.popleft()
- func()
+ env['base'].flush()
- env['base'].flush()
+ # make sure all tables are present
+ self.check_tables_exist(cr)
- # make sure all tables are present
- self.check_tables_exist(cr)
+ finally:
+ del self._post_init_queue
+ del self._foreign_keys
+ del self._is_install
def check_indexes(self, cr, model_names):
""" Create or drop column indexes for the given models. """
@@ -415,6 +421,52 @@ class Registry(Mapping):
elif not index and indexname in existing:
sql.drop_index(cr, indexname, tablename)
+ def add_foreign_key(self, table1, column1, table2, column2, ondelete,
+ model, module, force=True):
+ """ Specify an expected foreign key. """
+ key = (table1, column1)
+ val = (table2, column2, ondelete, model, module)
+ if force:
+ self._foreign_keys[key] = val
+ else:
+ self._foreign_keys.setdefault(key, val)
+
+ def check_foreign_keys(self, cr):
+ """ Create or update the expected foreign keys. """
+ if not self._foreign_keys:
+ return
+
+ # determine existing foreign keys on the tables
+ query = """
+ SELECT fk.conname, c1.relname, a1.attname, c2.relname, a2.attname, fk.confdeltype
+ FROM pg_constraint AS fk
+ JOIN pg_class AS c1 ON fk.conrelid = c1.oid
+ JOIN pg_class AS c2 ON fk.confrelid = c2.oid
+ JOIN pg_attribute AS a1 ON a1.attrelid = c1.oid AND fk.conkey[1] = a1.attnum
+ JOIN pg_attribute AS a2 ON a2.attrelid = c2.oid AND fk.confkey[1] = a2.attnum
+ WHERE fk.contype = 'f' AND c1.relname IN %s
+ """
+ cr.execute(query, [tuple({table for table, column in self._foreign_keys})])
+ existing = {
+ (table1, column1): (name, table2, column2, deltype)
+ for name, table1, column1, table2, column2, deltype in cr.fetchall()
+ }
+
+ # create or update foreign keys
+ for key, val in self._foreign_keys.items():
+ table1, column1 = key
+ table2, column2, ondelete, model, module = val
+ conname = '%s_%s_fkey' % key
+ deltype = sql._CONFDELTYPES[ondelete.upper()]
+ spec = existing.get(key)
+ if spec is None:
+ sql.add_foreign_key(cr, table1, column1, table2, column2, ondelete)
+ model.env['ir.model.constraint']._reflect_constraint(model, conname, 'f', None, module)
+ elif spec != (conname, table2, column2, deltype):
+ sql.drop_constraint(cr, table1, spec[0])
+ sql.add_foreign_key(cr, table1, column1, table2, column2, ondelete)
+ model.env['ir.model.constraint']._reflect_constraint(model, conname, 'f', None, module)
+
def check_tables_exist(self, cr):
"""
Verify that all tables are present and try to initialize those that are missing.