Skip to content

Commit 0c36147

Browse files
committed
[FIX] util/records: correctly replace values in JSON
The `\y` group cannot be used before a non word character otherwise we won't math anything. ``` > select REGEXP_MATCH('a <x> b', '\y<x>\y') +--------------+ | regexp_match | |--------------| | <null> | +--------------+ > select REGEXP_MATCH('a <x> b', '<x>') +--------------+ | regexp_match | |--------------| | ['<x>'] | +--------------+ ``` Also use `psycopg2.extras.Json` class to escape a string value for the match. Example result: ``` >>> print(str(Json('\y t-if="True" >'))) '"\\\\y t-if=\\"True\\" >"' >>> print(str(Json('\y' + re.escape(' t-if="True" >')))) '"\\\\y\\\\ t\\\\-if=\\"True\\"\\\\ >"' ``` Note that we must remove the surrounding `'`. opw-3526516 Part of odoo/upgrade#5233 Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 925fd6e commit 0c36147

File tree

2 files changed

+21
-5
lines changed

2 files changed

+21
-5
lines changed

src/base/tests/test_util.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,9 @@ def test_ensure_xmlid_match_record(self):
753753

754754
@unittest.skipUnless(util.version_gte("16.0"), "Only work on Odoo >= 16")
755755
def test_replace_in_all_jsonb_values(self):
756-
test_partner_title = self.env["res.partner.title"].create({"name": "object.number object.numbercombined"})
756+
test_partner_title = self.env["res.partner.title"].create(
757+
{"name": r"""object.number '<"x">\y object.numbercombined"""}
758+
)
757759

758760
pattern_old = re.compile(r"\b\.number\b")
759761
pattern_new = re.compile(r"\b\.name\b")
@@ -765,11 +767,17 @@ def test_replace_in_all_jsonb_values(self):
765767

766768
extra_filter = self.env.cr.mogrify("t.id = %s", (test_partner_title.id,)).decode()
767769
util.replace_in_all_jsonb_values(self.env.cr, "res_partner_title", "name", ".number", ".name", extra_filter)
770+
util.replace_in_all_jsonb_values(
771+
self.env.cr, "res_partner_title", "name", r"""'<"x">\y""", "GONE", extra_filter
772+
)
768773
test_partner_title.invalidate_recordset(["name"])
769774

770775
self.assertRegex(test_partner_title.name, pattern_new)
771776
self.assertRegex(test_partner_title.name, pattern_notouch)
772777
self.assertNotRegex(test_partner_title.name, pattern_old)
778+
# ensure replacing works for patterns that do not start with a valid word start \w
779+
# also ensure the replace works for multiple embedded quotes
780+
self.assertEqual(test_partner_title.name, "object.name GONE object.numbercombined")
773781

774782

775783
class TestMisc(UnitTestCase):

src/util/records.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,8 +1147,16 @@ def replace_in_all_jsonb_values(cr, table, column, old, new, extra_filter=None):
11471147
execute the query in parallel.
11481148
`old` can be a simple term (str) or a regexp (util.PGRegexp)
11491149
"""
1150-
re_old = old if isinstance(old, PGRegexp) else r"\y{}\y".format(re.escape(old))
1151-
match = 'exists($.* ? (@ like_regex "{}"))'.format(re_old.replace("\\", "\\\\").replace('"', r"\""))
1150+
re_old = (
1151+
old
1152+
if isinstance(old, PGRegexp)
1153+
else "{}{}{}".format(
1154+
r"\y" if re.match(r"\w", old[0]) else "",
1155+
re.escape(old),
1156+
r"\y" if re.match(r"\w", old[-1]) else "",
1157+
)
1158+
)
1159+
match = str(Json(re_old))[1:-1] # escapes re_old into json string
11521160

11531161
if extra_filter is None:
11541162
extra_filter = "true"
@@ -1161,7 +1169,7 @@ def replace_in_all_jsonb_values(cr, table, column, old, new, extra_filter=None):
11611169
FROM "{table}" t
11621170
JOIN LATERAL jsonb_each_text(t."{column}") v
11631171
ON true
1164-
WHERE jsonb_path_match(t."{column}", %s)
1172+
WHERE jsonb_path_match(t."{column}", 'exists($.* ? (@ like_regex {match}))')
11651173
AND {extra_filter}
11661174
GROUP BY t.id
11671175
)
@@ -1172,7 +1180,7 @@ def replace_in_all_jsonb_values(cr, table, column, old, new, extra_filter=None):
11721180
""".format(
11731181
**locals()
11741182
),
1175-
[re_old, new, match],
1183+
[re_old, new],
11761184
).decode()
11771185

11781186
if "{parallel_filter}" in query:

0 commit comments

Comments
 (0)