diff options
| author | Raphael Collet <rco@odoo.com> | 2020-04-02 06:45:54 +0000 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2020-04-03 11:40:29 +0000 |
| commit | 6eb7d5c6a07f678e1d1adca7137652e0626ea17e (patch) | |
| tree | b9c9c2d8ef5988a48df50daa7c1b12c2dc6d2bd2 /odoo/modules | |
| parent | e0c2dde3244e1eed6c6fc2541281d0ec5ce86168 (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.py | 82 |
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. |
