Skip to content

Commit 324ea17

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 5d24564 commit 324ea17

File tree

2 files changed

+67
-24
lines changed

2 files changed

+67
-24
lines changed

src/base/tests/test_util.py

+43
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,49 @@ 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+
"ttype": "many2one",
1359+
"model_id": partner_model.id,
1360+
"relation": "res.currency",
1361+
"company_dependent": True,
1362+
}
1363+
)
1364+
c1 = self.env["res.currency"].create({"name": "RC1", "symbol": "RC1"})
1365+
c2 = self.env["res.currency"].create({"name": "RC2", "symbol": "RC2"})
1366+
c3 = self.env["res.currency"].create({"name": "RC3", "symbol": "RC3"})
1367+
1368+
p1 = self.env["res.partner"].create({"name": "Captain Jack"})
1369+
p2 = self.env["res.partner"].create({"name": "River Song"})
1370+
1371+
old = {p1.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":null}}', p2.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":{c3.id}}}'}
1372+
for id, value in old.items():
1373+
self.env.cr.execute(
1374+
"""
1375+
UPDATE res_partner
1376+
SET x_test_curr = %s
1377+
WHERE id = %s
1378+
""",
1379+
[value, id],
1380+
)
1381+
mapping = {
1382+
c1.id: c2.id,
1383+
c2.id: c3.id,
1384+
c3.id: c1.id,
1385+
}
1386+
util.replace_record_references_batch(self.env.cr, mapping, "res.currency")
1387+
new = {p1.id: {"1": c2.id, "2": c3.id, "3": None}, p2.id: {"1": c2.id, "2": c3.id, "3": c1.id}}
1388+
self.env.cr.execute("SELECT id, x_test_curr FROM res_partner WHERE id IN %s", [(p1.id, p2.id)])
1389+
for id, currencies in self.env.cr.fetchall():
1390+
expected = new[id]
1391+
self.assertEqual(len(currencies), len(expected))
1392+
for comp in currencies:
1393+
self.assertEqual(currencies[comp], expected[comp])
1394+
13521395
def _prepare_test_delete_unused(self):
13531396
def create_cat():
13541397
name = f"test_{uuid.uuid4().hex}"

src/util/records.py

+24-24
Original file line numberDiff line numberDiff line change
@@ -1582,30 +1582,30 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
15821582
ir.res_id,
15831583
],
15841584
)
1585-
json_path = cr.mogrify(
1586-
"$.* ? ({})".format(" || ".join(["@ == %s"] * len(id_mapping))),
1587-
list(id_mapping),
1588-
).decode()
1589-
1590-
query = cr.mogrify(
1591-
format_query(
1592-
cr,
1593-
"""
1594-
UPDATE {table}
1595-
SET {column} = (
1596-
SELECT jsonb_object_agg(key, COALESCE(((jsonb_object(%s::text[]))->>value)::int, value::int))
1597-
FROM jsonb_each_text({column})
1598-
)
1599-
WHERE {column} IS NOT NULL
1600-
AND {column} @? {json_path}
1601-
""",
1602-
table=ir.table,
1603-
column=ir.res_id,
1604-
json_path=sql.Literal(json_path),
1605-
),
1606-
[list(map(list, id_mapping.items()))],
1607-
).decode()
1608-
explode_execute(cr, query, table=ir.table)
1585+
query = format_query(
1586+
cr,
1587+
"""
1588+
WITH _upg_cd AS (
1589+
SELECT t.id,
1590+
jsonb_object_agg(j.key, COALESCE(r.new, j.value::int)) as value
1591+
FROM {table} t
1592+
JOIN jsonb_each_text(t.{column}) j
1593+
ON true
1594+
LEFT JOIN _upgrade_rrr r
1595+
ON r.old = j.value::integer
1596+
WHERE {{parallel_filter}}
1597+
GROUP BY t.id
1598+
HAVING bool_or(r.new IS NOT NULL)
1599+
)
1600+
UPDATE {table} t
1601+
SET {column} = u.value
1602+
FROM _upg_cd u
1603+
WHERE u.id = t.id
1604+
""",
1605+
table=ir.table,
1606+
column=ir.res_id,
1607+
)
1608+
explode_execute(cr, query, table=ir.table, alias="t")
16091609
# ensure all new ids exist
16101610
cr.execute(
16111611
format_query(

0 commit comments

Comments
 (0)