Skip to content

Commit 1e1bb9b

Browse files
committed
[FIX] util/records: deduplicate before batch update
When updating record references we need to dedup uniq indexes. In the case of inderect references the case when two old references become the same new reference was missing. Example to trigger the issue ``` test_15=> select * from mail_followers where res_model='res.partner.bank' +----+------------------+--------+------------+ | id | res_model | res_id | partner_id | |----+------------------+--------+------------| | 5 | res.partner.bank | 2 | 3 | | 7 | res.partner.bank | 3 | 3 | +----+------------------+--------+------------+ test_15=> select id,sanitized_acc_number,partner_id,company_id from res_partner_bank +----+----------------------+------------+------------+ | id | sanitized_acc_number | partner_id | company_id | |----+----------------------+------------+------------| | 1 | 1 | 3 | 1 | | 2 | 1 | 3 | 2 | | 3 | 1 | 3 | 3 | +----+----------------------+------------+------------+ ``` In such case the record replacer will replace references to the partner bank as in the mapping `{2:1, 3:1}`. But this causes an error ``` 2025-03-07 17:12:12,145 502959 ERROR test_15_16 odoo.sql_db: bad query: UPDATE "mail_followers" t SET "res_model" = 'res.partner.bank' , "res_id" = _upgrade_rrr.new FROM _upgrade_rrr WHERE "res_model"='res.partner.bank' AND _upgrade_rrr.old = res_id AND NOT EXISTS(SELECT 1 FROM mail_followers WHERE "res_model" = 'res.partner.bank' AND "res_id" = _upgrade_rrr.new AND "partner_id"=t."partner_id") ; DELETE FROM mail_followers USING _upgrade_rrr WHERE "res_model"='res.partner.bank' AND res_id = _upgrade_rrr.old; ERROR: duplicate key value violates unique constraint "mail_followers_mail_followers_res_partner_res_model_id_uniq" DETAIL: Key (res_model, res_id, partner_id)=(res.partner.bank, 1, 3) already exists. ```
1 parent 1f2f266 commit 1e1bb9b

File tree

1 file changed

+38
-16
lines changed

1 file changed

+38
-16
lines changed

src/util/records.py

+38-16
Original file line numberDiff line numberDiff line change
@@ -1694,34 +1694,56 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
16941694
if unique_indexes:
16951695
conditions = []
16961696
for _, uniq_cols in unique_indexes:
1697-
uniq_cols = set(uniq_cols) - {ir.res_id, ir.res_model, ir.res_model_id} # noqa: PLW2901
1697+
uniq_cols = set(uniq_cols) - {ir.res_model, ir.res_model_id} # noqa: PLW2901
1698+
if not uniq_cols:
1699+
continue
16981700
conditions.append(
16991701
format_query(
17001702
cr,
1701-
"NOT EXISTS(SELECT 1 FROM {table} WHERE {res_model_whr} AND {jmap_expr} AND {where_clause})",
1702-
table=ir.table,
1703-
res_model_whr=res_model_whr,
1704-
jmap_expr=jmap_expr,
1705-
where_clause=SQLStr(" AND ".join(format_query(cr, "{0}=t.{0}", col) for col in uniq_cols))
1706-
if uniq_cols
1707-
else SQLStr("True"),
1703+
"(ROW_NUMBER() OVER(PARTITION BY {cols} ORDER BY t.id)) > 1",
1704+
cols=SQLStr(
1705+
", ".join(
1706+
"_upgrade_rrr.new" if col == ir.res_id else format_query(cr, "t.{0}", col)
1707+
for col in uniq_cols
1708+
)
1709+
),
17081710
)
17091711
)
1710-
query = format_query(
1712+
dedup_query = format_query(
17111713
cr,
1712-
"""{prev_query} AND {cond};
1713-
1714-
DELETE FROM {table} USING _upgrade_rrr WHERE {whr} AND {res_id} = _upgrade_rrr.old;
1714+
"""
1715+
WITH dedup AS (
1716+
SELECT t.id,
1717+
{is_dup} is_dup
1718+
FROM {table} t
1719+
JOIN _upgrade_rrr
1720+
ON _upgrade_rrr.old = t.{res_id}
1721+
AND {model_match}
1722+
)
1723+
DELETE FROM {table} t
1724+
USING dedup
1725+
WHERE dedup.id = t.id
1726+
AND dedup.is_dup
1727+
""",
1728+
model_match=ir.model_filter(placeholder="%(model_src)s", prefix="t."),
1729+
table=ir.table,
1730+
is_dup=SQLStr("\nOR ".join(conditions)),
1731+
res_id=ir.res_id,
1732+
)
1733+
cleanup_query = format_query(
1734+
cr,
1735+
"""
1736+
DELETE FROM {table} t USING _upgrade_rrr WHERE {whr} AND {res_id} = _upgrade_rrr.old;
17151737
""",
1716-
prev_query=query,
1717-
cond=SQLStr("\nAND ".join(conditions)),
17181738
table=ir.table,
17191739
whr=whr,
17201740
res_id=ir.res_id,
17211741
)
1722-
cr.execute(query, {"model_dst": model_dst, "model_src": model_src})
1742+
cr.execute(dedup_query, locals())
1743+
cr.execute(query, locals())
1744+
cr.execute(cleanup_query, locals())
17231745
else:
1724-
fmt_query = cr.mogrify(query.format(**locals()), locals()).decode()
1746+
fmt_query = cr.mogrify(query, locals()).decode()
17251747
parallel_execute(cr, explode_query_range(cr, fmt_query, table=ir.table, alias="t"))
17261748

17271749
# reference fields

0 commit comments

Comments
 (0)