Skip to content

Commit b5688a9

Browse files
committed
[FIX] util.replace_record_references_batch: handle unique constraints
We already do it for indirect references (res_model/res_id), why not for the direct ones? closes #46 Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 53d1f56 commit b5688a9

File tree

2 files changed

+37
-10
lines changed

2 files changed

+37
-10
lines changed

src/base/tests/test_util.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,19 @@ def test_replace_in_all_jsonb_values(self):
937937
# also ensure the replace works for multiple embedded quotes
938938
self.assertEqual(test_partner_title.name, "object.name GONE object.numbercombined")
939939

940+
def test_replace_record_references_batch__uniqueness(self):
941+
c1 = self.env["res.country"].create(
942+
{"name": "TEST1", "code": "T1", "state_ids": [(0, 0, {"name": "STATE1", "code": "STATE"})]}
943+
)
944+
c2 = self.env["res.country"].create(
945+
{"name": "TEST2", "code": "T2", "state_ids": [(0, 0, {"name": "STATE2", "code": "STATE"})]}
946+
)
947+
948+
util.replace_record_references_batch(self.env.cr, {c2.id: c1.id}, "res.country")
949+
self.env.cr.execute("SELECT count(1) FROM res_country_state WHERE country_id=%s", [c1.id])
950+
[count] = self.env.cr.fetchone()
951+
self.assertEqual(count, 1)
952+
940953

941954
class TestMisc(UnitTestCase):
942955
@parametrize(

src/util/records.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,19 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
10611061
FROM _upgrade_rrr r
10621062
WHERE r.old = t.{fk}
10631063
"""
1064+
unique_indexes = _get_unique_indexes_with(cr, table, fk)
1065+
if unique_indexes:
1066+
conditions = [""]
1067+
for _, uniq_cols in unique_indexes:
1068+
uniq_cols = set(uniq_cols) - {fk} # noqa: PLW2901
1069+
ands = (
1070+
" AND ".join(format_query(cr, "u.{col} = t.{col}", col=col) for col in uniq_cols)
1071+
if uniq_cols
1072+
else "true"
1073+
)
1074+
conditions.append("NOT EXISTS(SELECT 1 FROM {table} u WHERE u.{fk} = r.new AND %s)" % ands)
1075+
1076+
query += " AND ".join(conditions)
10641077

10651078
if not column_exists(cr, table, "id"):
10661079
# seems to be a m2m table. Avoid duplicated entries
@@ -1091,22 +1104,15 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
10911104
col2=col2,
10921105
)
10931106
)
1094-
query += """
1095-
AND NOT EXISTS(SELECT 1 FROM {table} e WHERE e.{col2} = t.{col2} AND e.{fk} = r.new);
1096-
1097-
DELETE
1098-
FROM {table} t
1099-
USING _upgrade_rrr r
1100-
WHERE t.{fk} = r.old;
1101-
"""
1107+
query += " AND NOT EXISTS(SELECT 1 FROM {table} e WHERE e.{col2} = t.{col2} AND e.{fk} = r.new)"
11021108

11031109
col2_info = target_of(cr, table, col2) # col2 may not be a FK
11041110
if col2_info and col2_info[:2] == (model_src_table, "id"):
11051111
# a m2m on itself, remove the self referencing entries
11061112
# It only handle 1-level recursions. For multi-level recursions, it should be handled manually.
11071113
# We can't decide which link to break.
11081114
# XXX: add a warning?
1109-
query += """
1115+
query += """;
11101116
DELETE
11111117
FROM {table} t
11121118
USING _upgrade_rrr r
@@ -1117,13 +1123,21 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
11171123
cr.execute(format_query(cr, query, table=table, fk=fk, col2=col2))
11181124

11191125
else: # it's a model
1120-
fmt_query = cr.mogrify(format_query(cr, query, table=table, fk=fk)).decode()
1126+
fmt_query = format_query(cr, query, table=table, fk=fk)
11211127
parallel_execute(cr, explode_query_range(cr, fmt_query, table=table, alias="t"))
11221128

11231129
# track default values to update
11241130
model = model_of_table(cr, table)
11251131
fk_def.append((model, fk))
11261132

1133+
delete_query = """
1134+
DELETE
1135+
FROM {table} t
1136+
USING _upgrade_rrr r
1137+
WHERE t.{fk} = r.old
1138+
"""
1139+
cr.execute(format_query(cr, delete_query, table=table, fk=fk))
1140+
11271141
if fk_def:
11281142
if table_exists(cr, "ir_values"):
11291143
column_read, cast_write = _ir_values_value(cr, prefix="v")

0 commit comments

Comments
 (0)