Skip to content

Commit 7bdd490

Browse files
committed
[FIX] records: use temp table in replacing refs
For the jsonb company dependent indirect references when replacing record refs, building the query using the original id mapping input can generate a large query based on the number of references being updated, leading to a 'stack depth limit exceeded' error. We can use the mapping saved in the temp table _upgrade_rrr instead.
1 parent e37d57c commit 7bdd490

File tree

2 files changed

+68
-24
lines changed

2 files changed

+68
-24
lines changed

src/base/tests/test_util.py

+44
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,50 @@ def test_replace_record_references_batch__uniqueness(self):
13491349
[count] = self.env.cr.fetchone()
13501350
self.assertEqual(count, 1)
13511351

1352+
@unittest.skipUnless(util.version_gte("18.0"), "Only work on Odoo >= 18")
1353+
def test_replace_record_references_batch__company_dependent(self):
1354+
partner_model = self.env["ir.model"].search([("model", "=", "res.partner")])
1355+
self.env["ir.model.fields"].create(
1356+
{
1357+
"name": "x_test_curr",
1358+
"company_dependent": True,
1359+
"translate": True,
1360+
"ttype": "many2one",
1361+
"model_id": partner_model.id,
1362+
"relation": "res.currency",
1363+
}
1364+
)
1365+
c1 = self.env["res.currency"].create({"name": "RC1", "symbol": "RC1"})
1366+
c2 = self.env["res.currency"].create({"name": "RC2", "symbol": "RC2"})
1367+
c3 = self.env["res.currency"].create({"name": "RC3", "symbol": "RC3"})
1368+
1369+
p1 = self.env["res.partner"].create({"name": "Captain Jack"})
1370+
p2 = self.env["res.partner"].create({"name": "River Song"})
1371+
1372+
old = {p1.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":null}}', p2.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":{c3.id}}}'}
1373+
for id, value in old.items():
1374+
self.env.cr.execute(
1375+
"""
1376+
UPDATE res_partner
1377+
SET x_test_curr = %s
1378+
WHERE id = %s
1379+
""",
1380+
[value, id],
1381+
)
1382+
mapping = {
1383+
c1.id: c2.id,
1384+
c2.id: c3.id,
1385+
c3.id: c1.id,
1386+
}
1387+
util.replace_record_references_batch(self.env.cr, mapping, "res.currency")
1388+
new = {p1.id: {"1": c2.id, "2": c3.id, "3": None}, p2.id: {"1": c2.id, "2": c3.id, "3": c1.id}}
1389+
self.env.cr.execute("SELECT id, x_test_curr FROM res_partner WHERE id IN %s", [(p1.id, p2.id)])
1390+
for id, currencies in self.env.cr.fetchall():
1391+
expected = new[id]
1392+
self.assertEqual(len(currencies), len(expected))
1393+
for comp in currencies:
1394+
self.assertEqual(currencies[comp], expected[comp])
1395+
13521396
def _prepare_test_delete_unused(self):
13531397
def create_cat():
13541398
name = f"test_{uuid.uuid4().hex}"

src/util/records.py

+24-24
Original file line numberDiff line numberDiff line change
@@ -1544,30 +1544,30 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
15441544
ir.res_id,
15451545
],
15461546
)
1547-
json_path = cr.mogrify(
1548-
"$.* ? ({})".format(" || ".join(["@ == %s"] * len(id_mapping))),
1549-
list(id_mapping),
1550-
).decode()
1551-
1552-
query = cr.mogrify(
1553-
format_query(
1554-
cr,
1555-
"""
1556-
UPDATE {table}
1557-
SET {column} = (
1558-
SELECT jsonb_object_agg(key, COALESCE(((jsonb_object(%s::text[]))->>value)::int, value::int))
1559-
FROM jsonb_each_text({column})
1560-
)
1561-
WHERE {column} IS NOT NULL
1562-
AND {column} @? {json_path}
1563-
""",
1564-
table=ir.table,
1565-
column=ir.res_id,
1566-
json_path=sql.Literal(json_path),
1567-
),
1568-
[list(map(list, id_mapping.items()))],
1569-
).decode()
1570-
explode_execute(cr, query, table=ir.table)
1547+
query = format_query(
1548+
cr,
1549+
"""
1550+
WITH _upg_cd AS (
1551+
SELECT t.id,
1552+
jsonb_object_agg(j.key, COALESCE(r.new, j.value::int)) as value
1553+
FROM {table} t
1554+
JOIN jsonb_each_text(t.{column}) j
1555+
ON true
1556+
LEFT JOIN _upgrade_rrr r
1557+
ON r.old = j.value::integer
1558+
WHERE {{parallel_filter}}
1559+
GROUP BY t.id
1560+
HAVING bool_or(r.new IS NOT NULL)
1561+
)
1562+
UPDATE {table} t
1563+
SET {column} = u.value
1564+
FROM _upg_cd u
1565+
WHERE u.id = t.id
1566+
""",
1567+
table=ir.table,
1568+
column=ir.res_id,
1569+
)
1570+
explode_execute(cr, query, table=ir.table, alias="t")
15711571
# ensure all new ids exist
15721572
cr.execute(
15731573
format_query(

0 commit comments

Comments
 (0)