Skip to content

Commit 41abe70

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. closes #224 Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 903ee87 commit 41abe70

File tree

2 files changed

+69
-24
lines changed

2 files changed

+69
-24
lines changed

src/base/tests/test_util.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,51 @@ def test_replace_record_references_batch__uniqueness(self):
14601460
[count] = self.env.cr.fetchone()
14611461
self.assertEqual(count, 1)
14621462

1463+
@unittest.skipUnless(util.version_gte("18.0"), "Only work on Odoo >= 18")
1464+
def test_replace_record_references_batch__company_dependent(self):
1465+
partner_model = self.env["ir.model"].search([("model", "=", "res.partner")])
1466+
self.env["ir.model.fields"].create(
1467+
{
1468+
"name": "x_test_curr",
1469+
"ttype": "many2one",
1470+
"model_id": partner_model.id,
1471+
"relation": "res.currency",
1472+
"company_dependent": True,
1473+
}
1474+
)
1475+
c1 = self.env["res.currency"].create({"name": "RC1", "symbol": "RC1"})
1476+
c2 = self.env["res.currency"].create({"name": "RC2", "symbol": "RC2"})
1477+
c3 = self.env["res.currency"].create({"name": "RC3", "symbol": "RC3"})
1478+
c4 = self.env["res.currency"].create({"name": "RC4", "symbol": "RC4"})
1479+
1480+
p1 = self.env["res.partner"].create({"name": "Captain Jack"})
1481+
p2 = self.env["res.partner"].create({"name": "River Song"})
1482+
p3 = self.env["res.partner"].create({"name": "Donna Noble"})
1483+
1484+
old = {
1485+
p1.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":null}}',
1486+
p2.id: f'{{"1":{c1.id}, "2":{c2.id}, "3":{c3.id}, "4":{c4.id}}}',
1487+
p3.id: f'{{"1":{c4.id}}}',
1488+
}
1489+
for id, value in old.items():
1490+
self.env.cr.execute("UPDATE res_partner SET x_test_curr = %s WHERE id = %s", [value, id])
1491+
mapping = {
1492+
c1.id: c2.id,
1493+
c2.id: c3.id,
1494+
c3.id: c1.id,
1495+
}
1496+
with self.assertNotUpdated("res_partner", ids=[p3.id]):
1497+
util.replace_record_references_batch(self.env.cr, mapping, "res.currency")
1498+
new = {
1499+
p1.id: {"1": c2.id, "2": c3.id, "3": None},
1500+
p2.id: {"1": c2.id, "2": c3.id, "3": c1.id, "4": c4.id},
1501+
p3.id: {"1": c4.id},
1502+
}
1503+
self.env.cr.execute("SELECT id, x_test_curr FROM res_partner WHERE id IN %s", [(p1.id, p2.id)])
1504+
for id, currencies in self.env.cr.fetchall():
1505+
expected = new[id]
1506+
self.assertEqual(currencies, expected)
1507+
14631508
def _prepare_test_delete_unused(self):
14641509
def create_cat():
14651510
name = f"test_{uuid.uuid4().hex}"

src/util/records.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,30 +1613,30 @@ def replace_record_references_batch(cr, id_mapping, model_src, model_dst=None, r
16131613
ir.res_id,
16141614
],
16151615
)
1616-
json_path = cr.mogrify(
1617-
"$.* ? ({})".format(" || ".join(["@ == %s"] * len(id_mapping))),
1618-
list(id_mapping),
1619-
).decode()
1620-
1621-
query = cr.mogrify(
1622-
format_query(
1623-
cr,
1624-
"""
1625-
UPDATE {table}
1626-
SET {column} = (
1627-
SELECT jsonb_object_agg(key, COALESCE(((jsonb_object(%s::text[]))->>value)::int, value::int))
1628-
FROM jsonb_each_text({column})
1629-
)
1630-
WHERE {column} IS NOT NULL
1631-
AND {column} @? {json_path}
1632-
""",
1633-
table=ir.table,
1634-
column=ir.res_id,
1635-
json_path=sql.Literal(json_path),
1636-
),
1637-
[list(map(list, id_mapping.items()))],
1638-
).decode()
1639-
explode_execute(cr, query, table=ir.table)
1616+
query = format_query(
1617+
cr,
1618+
"""
1619+
WITH _upg_cd AS (
1620+
SELECT t.id,
1621+
jsonb_object_agg(j.key, COALESCE(r.new, j.value::int)) as value
1622+
FROM {table} t
1623+
JOIN jsonb_each_text(t.{column}) j
1624+
ON true
1625+
LEFT JOIN _upgrade_rrr r
1626+
ON r.old = j.value::integer
1627+
WHERE {{parallel_filter}}
1628+
GROUP BY t.id
1629+
HAVING bool_or(r.new IS NOT NULL)
1630+
)
1631+
UPDATE {table} t
1632+
SET {column} = u.value
1633+
FROM _upg_cd u
1634+
WHERE u.id = t.id
1635+
""",
1636+
table=ir.table,
1637+
column=ir.res_id,
1638+
)
1639+
explode_execute(cr, query, table=ir.table, alias="t")
16401640
# ensure all new ids exist
16411641
cr.execute(
16421642
format_query(

0 commit comments

Comments
 (0)