Skip to content

Commit 65d35c3

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 ed097dd commit 65d35c3

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
@@ -1364,6 +1364,49 @@ def test_replace_record_references_batch__uniqueness(self):
13641364
[count] = self.env.cr.fetchone()
13651365
self.assertEqual(count, 1)
13661366

1367+
@unittest.skipUnless(util.version_gte("18.0"), "Only work on Odoo >= 18")
1368+
def test_replace_record_references_batch__company_dependent(self):
1369+
partner_model = self.env["ir.model"].search([("model", "=", "res.partner")])
1370+
self.env["ir.model.fields"].create(
1371+
{
1372+
"name": "x_test_curr",
1373+
"ttype": "many2one",
1374+
"model_id": partner_model.id,
1375+
"relation": "res.currency",
1376+
"company_dependent": True,
1377+
}
1378+
)
1379+
c1 = self.env["res.currency"].create({"name": "RC1", "symbol": "RC1"})
1380+
c2 = self.env["res.currency"].create({"name": "RC2", "symbol": "RC2"})
1381+
c3 = self.env["res.currency"].create({"name": "RC3", "symbol": "RC3"})
1382+
1383+
p1 = self.env["res.partner"].create({"name": "Captain Jack"})
1384+
p2 = self.env["res.partner"].create({"name": "River Song"})
1385+
1386+
old = {p1.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":null}}', p2.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":{c3.id}}}'}
1387+
for id, value in old.items():
1388+
self.env.cr.execute(
1389+
"""
1390+
UPDATE res_partner
1391+
SET x_test_curr = %s
1392+
WHERE id = %s
1393+
""",
1394+
[value, id],
1395+
)
1396+
mapping = {
1397+
c1.id: c2.id,
1398+
c2.id: c3.id,
1399+
c3.id: c1.id,
1400+
}
1401+
util.replace_record_references_batch(self.env.cr, mapping, "res.currency")
1402+
new = {p1.id: {"1": c2.id, "2": c3.id, "3": None}, p2.id: {"1": c2.id, "2": c3.id, "3": c1.id}}
1403+
self.env.cr.execute("SELECT id, x_test_curr FROM res_partner WHERE id IN %s", [(p1.id, p2.id)])
1404+
for id, currencies in self.env.cr.fetchall():
1405+
expected = new[id]
1406+
self.assertEqual(len(currencies), len(expected))
1407+
for comp in currencies:
1408+
self.assertEqual(currencies[comp], expected[comp])
1409+
13671410
def _prepare_test_delete_unused(self):
13681411
def create_cat():
13691412
name = f"test_{uuid.uuid4().hex}"

src/util/records.py

+24-24
Original file line numberDiff line numberDiff line change
@@ -1600,30 +1600,30 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
16001600
ir.res_id,
16011601
],
16021602
)
1603-
json_path = cr.mogrify(
1604-
"$.* ? ({})".format(" || ".join(["@ == %s"] * len(id_mapping))),
1605-
list(id_mapping),
1606-
).decode()
1607-
1608-
query = cr.mogrify(
1609-
format_query(
1610-
cr,
1611-
"""
1612-
UPDATE {table}
1613-
SET {column} = (
1614-
SELECT jsonb_object_agg(key, COALESCE(((jsonb_object(%s::text[]))->>value)::int, value::int))
1615-
FROM jsonb_each_text({column})
1616-
)
1617-
WHERE {column} IS NOT NULL
1618-
AND {column} @? {json_path}
1619-
""",
1620-
table=ir.table,
1621-
column=ir.res_id,
1622-
json_path=sql.Literal(json_path),
1623-
),
1624-
[list(map(list, id_mapping.items()))],
1625-
).decode()
1626-
explode_execute(cr, query, table=ir.table)
1603+
query = format_query(
1604+
cr,
1605+
"""
1606+
WITH _upg_cd AS (
1607+
SELECT t.id,
1608+
jsonb_object_agg(j.key, COALESCE(r.new, j.value::int)) as value
1609+
FROM {table} t
1610+
JOIN jsonb_each_text(t.{column}) j
1611+
ON true
1612+
LEFT JOIN _upgrade_rrr r
1613+
ON r.old = j.value::integer
1614+
WHERE {{parallel_filter}}
1615+
GROUP BY t.id
1616+
HAVING bool_or(r.new IS NOT NULL)
1617+
)
1618+
UPDATE {table} t
1619+
SET {column} = u.value
1620+
FROM _upg_cd u
1621+
WHERE u.id = t.id
1622+
""",
1623+
table=ir.table,
1624+
column=ir.res_id,
1625+
)
1626+
explode_execute(cr, query, table=ir.table, alias="t")
16271627
# ensure all new ids exist
16281628
cr.execute(
16291629
format_query(

0 commit comments

Comments
 (0)