Skip to content

Commit 646d5f8

Browse files
committed
[IMP] util/records: add update_parent_path
After some operations in the DB it often happens that the parent path stored in some tables needs to be updated. This is critical in some cases. We provide here a new function that performs the update of parent paths. We would trigger it when replacing references. For parent checks before saas~11.3 we user two columns: `parent_left/right`. They are computed in a complex way that's better trigered via the ORM. Here we just warn about the issue. closes #277 Note: warnings can be disabled with `parent_field=None` Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 6ba49a9 commit 646d5f8

File tree

1 file changed

+63
-3
lines changed

1 file changed

+63
-3
lines changed

src/util/records.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
resolve_model_fields_path,
3232
table_of_model,
3333
)
34+
from .inconsistencies import break_recursive_loops
3435
from .indirect_references import indirect_references
3536
from .inherit import direct_inherit_parents, for_each_inherit
3637
from .misc import Sentinel, chunks, parse_version, version_gte
@@ -1423,7 +1424,7 @@ def delete_unused(cr, *xmlids, **kwargs):
14231424
return deleted
14241425

14251426

1426-
def replace_record_references(cr, old, new, replace_xmlid=True):
1427+
def replace_record_references(cr, old, new, replace_xmlid=True, parent_field="parent_id"):
14271428
"""
14281429
Replace all (in)direct references of a record by another.
14291430
@@ -1436,10 +1437,12 @@ def replace_record_references(cr, old, new, replace_xmlid=True):
14361437
if not old[1]:
14371438
return None
14381439

1439-
return replace_record_references_batch(cr, {old[1]: new[1]}, old[0], new[0], replace_xmlid)
1440+
return replace_record_references_batch(cr, {old[1]: new[1]}, old[0], new[0], replace_xmlid, parent_field)
14401441

14411442

1442-
def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, replace_xmlid=True, ignores=()):
1443+
def replace_record_references_batch(
1444+
cr, id_mapping, model_src, model_dst=None, replace_xmlid=True, ignores=(), parent_field="parent_id"
1445+
):
14431446
"""
14441447
Replace all references to records.
14451448
@@ -1456,6 +1459,9 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
14561459
the source records
14571460
:param list(str) ignores: list of **table** names to skip when updating the referenced
14581461
values
1462+
:paream str parent_field: when the target and source model are the same, and the model
1463+
table has `parent_path` column, this field will be used to
1464+
update it.
14591465
"""
14601466
_validate_model(model_src)
14611467
if model_dst is None:
@@ -1781,6 +1787,20 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
17811787
)
17821788

17831789
cr.execute("DROP TABLE _upgrade_rrr")
1790+
if parent_field and model_dst == model_src:
1791+
if column_exists(cr, model_src_table, "parent_path"):
1792+
fk_target = target_of(cr, model_src_table, parent_field)
1793+
if fk_target:
1794+
if fk_target[0] != model_src_table:
1795+
_logger.warning(
1796+
"`%s` has a `parent_path` but `%s` is not a self-referencing FK", model_src_table, parent_field
1797+
)
1798+
else:
1799+
update_parent_path(cr, model_src, parent_field)
1800+
elif parent_field != "parent_id": # check non-default value
1801+
_logger.error("`%s` in `%s` is not a self-referencing FK", parent_field, model_src_table)
1802+
elif column_exists(cr, model_src_table, "parent_left"):
1803+
_logger.warning("Possibly missing update of parent_left/right in `%s`", model_src_table)
17841804

17851805

17861806
def replace_in_all_jsonb_values(cr, table, column, old, new, extra_filter=None):
@@ -1935,3 +1955,43 @@ def _remove_redundant_tcalls(cr, match):
19351955
vid,
19361956
match,
19371957
)
1958+
1959+
1960+
def update_parent_path(cr, model, parent_field="parent_id"):
1961+
"""
1962+
Trigger the update of parent paths in a model.
1963+
1964+
:meta private: exclude from online docs
1965+
"""
1966+
if not version_gte("saas~11.3"):
1967+
_logger.error("parent_left and parent_right must be computed via the ORM")
1968+
return
1969+
table = table_of_model(cr, model)
1970+
name_field = "name" if column_exists(cr, table, "name") else "id"
1971+
break_recursive_loops(cr, model, parent_field, name_field)
1972+
query = format_query(
1973+
cr,
1974+
"""
1975+
WITH RECURSIVE __parent_store_compute(id, parent_path) AS (
1976+
SELECT row.id,
1977+
concat(row.id, '/')
1978+
FROM {table} row
1979+
WHERE row.{parent_field} IS NULL
1980+
1981+
UNION
1982+
1983+
SELECT row.id,
1984+
concat(comp.parent_path, row.id, '/')
1985+
FROM {table} row
1986+
JOIN __parent_store_compute comp
1987+
ON row.{parent_field} = comp.id
1988+
)
1989+
UPDATE {table} row
1990+
SET parent_path = comp.parent_path
1991+
FROM __parent_store_compute comp
1992+
WHERE row.id = comp.id
1993+
""",
1994+
table=table,
1995+
parent_field=parent_field,
1996+
)
1997+
cr.execute(query)

0 commit comments

Comments
 (0)