Skip to content

Commit 956ceb5

Browse files
committed
[FIX] util.change_field_selection_values
Correctly handle `None` as source. It should match `NULL` in SQL. As [pointed by Chong], it will be coerced to the `"null"` string when converted to json and we can't use `=` to compare to NULL in SQL. [pointed by Chong]: #400 (review) closes #413 Signed-off-by: Alvaro Fuentes Suarez (afu) <afu@odoo.com>
1 parent 32c3250 commit 956ceb5

1 file changed

Lines changed: 54 additions & 39 deletions

File tree

src/util/fields.py

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,17 +1190,30 @@ def change_field_selection_values(cr, model, field, mapping, skip_inherit=()):
11901190
if column_exists(cr, table, field):
11911191
ctype = column_type(cr, table, field)
11921192
if ctype == "varchar":
1193-
query = "UPDATE {table} t SET {column} = %(json)s::jsonb->>t.{column} WHERE t.{column} = ANY(%(keys)s)"
1193+
query = """
1194+
WITH _mapping AS (
1195+
SELECT * FROM UNNEST(%s::text[], %s::text[]) AS u(old, new)
1196+
)
1197+
UPDATE {table} t
1198+
SET {column} = m.new
1199+
FROM _mapping m
1200+
WHERE m.old IS NOT DISTINCT FROM t.{column}
1201+
"""
11941202
elif ctype == "jsonb":
11951203
# company dependent selection field
11961204
query = """
1197-
WITH upd AS (
1205+
WITH _mapping AS (
1206+
SELECT * FROM UNNEST(%s::text[], %s::text[]) AS u(old, new)
1207+
),
1208+
upd AS (
11981209
SELECT t.id,
1199-
jsonb_object_agg(v.key, COALESCE(%(json)s::jsonb->>v.value, v.value)) AS value
1210+
jsonb_object_agg(v.key, COALESCE(m.new, v.value)) AS value
12001211
FROM {table} t
12011212
JOIN LATERAL jsonb_each_text(t.{column}) v
12021213
ON true
1203-
WHERE jsonb_path_query_array(t.{column}, '$.*') ?| %(keys)s
1214+
LEFT JOIN _mapping m
1215+
ON m.old IS NOT DISTINCT FROM v.value
1216+
WHERE jsonb_path_query_array(t.{column}, '$.*') ?| (SELECT array_agg(old) FROM _mapping)
12041217
AND {{parallel_filter}}
12051218
GROUP BY t.id
12061219
)
@@ -1212,10 +1225,7 @@ def change_field_selection_values(cr, model, field, mapping, skip_inherit=()):
12121225
else:
12131226
raise UpgradeError("unsupported column type for selection field: {}".format(ctype))
12141227

1215-
data = {
1216-
"keys": list(mapping),
1217-
"json": json.dumps(mapping),
1218-
}
1228+
data = [list(mapping.keys()), list(mapping.values())]
12191229
queries = [
12201230
cr.mogrify(q, data).decode()
12211231
for q in explode_query_range(cr, format_query(cr, query, table=table, column=field), table=table, alias="t")
@@ -1227,17 +1237,19 @@ def change_field_selection_values(cr, model, field, mapping, skip_inherit=()):
12271237
if cr.rowcount:
12281238
fields_id = cr.fetchone()[0]
12291239
query = """
1230-
UPDATE ir_property
1231-
SET value_text = %(json)s::jsonb->>value_text
1232-
WHERE value_text IN %(keys)s
1233-
AND fields_id = %(fields_id)s
1240+
WITH _mapping AS (
1241+
SELECT * FROM UNNEST(%s::text[], %s::text[]) AS u(old, new)
1242+
)
1243+
UPDATE ir_property p
1244+
SET value_text = m.new
1245+
FROM _mapping m
1246+
WHERE p.value_text IS NOT DISTINCT FROM m.old
1247+
AND p.fields_id = %s
12341248
"""
1235-
data = {
1236-
"keys": tuple(mapping),
1237-
"json": json.dumps(mapping),
1238-
"fields_id": fields_id,
1239-
}
1240-
queries = [cr.mogrify(q, data).decode() for q in explode_query_range(cr, query, table="ir_property")]
1249+
data = [list(mapping.keys()), list(mapping.values()), fields_id]
1250+
queries = [
1251+
cr.mogrify(q, data).decode() for q in explode_query_range(cr, query, table="ir_property", alias="p")
1252+
]
12411253
parallel_execute(cr, queries)
12421254

12431255
if table_exists(cr, "ir_model_fields_selection"):
@@ -1250,45 +1262,48 @@ def change_field_selection_values(cr, model, field, mapping, skip_inherit=()):
12501262
AND f.name = %s
12511263
AND s.value = ANY(%s)
12521264
""",
1253-
[model, field, [k for k in mapping if k not in mapping.values()]],
1265+
[model, field, [k for k in mapping if k is not None and k not in mapping.values()]],
12541266
)
12551267

12561268
if table_exists(cr, "ir_default"):
12571269
query = """
1270+
WITH _mapping AS (
1271+
SELECT * FROM UNNEST(%s::text[], %s::text[]) AS u(old, new)
1272+
)
12581273
UPDATE ir_default d
1259-
SET json_value = (%(json)s::jsonb->>d.json_value)
1260-
FROM ir_model_fields f
1274+
SET json_value = m.new
1275+
FROM _mapping m,
1276+
ir_model_fields f
12611277
WHERE d.field_id = f.id
1262-
AND f.model = %(model)s
1263-
AND f.name = %(name)s
1264-
AND d.json_value IN %(keys)s
1278+
AND f.model = %s
1279+
AND f.name = %s
1280+
AND d.json_value = m.old
12651281
"""
12661282
dumped_map = {json.dumps(k): json.dumps(v) for k, v in mapping.items()}
12671283
else:
12681284
query = """
1269-
UPDATE ir_values
1270-
SET value = %(json)s::jsonb->>value
1271-
WHERE model = %(model)s
1272-
AND name = %(name)s
1273-
AND key = 'default'
1274-
AND value IN %(keys)s
1285+
WITH _mapping AS (
1286+
SELECT * FROM UNNEST(%s::text[], %s::text[]) AS u(old, new)
1287+
)
1288+
UPDATE ir_values d
1289+
SET value = m.new
1290+
FROM _mapping m
1291+
WHERE d.model = %s
1292+
AND d.name = %s
1293+
AND d.key = 'default'
1294+
AND d.value = m.old
12751295
"""
12761296
dumped_map = {pickle.dumps(k): pickle.dumps(v) for k, v in mapping.items()}
12771297

1278-
data = {
1279-
"keys": tuple(dumped_map),
1280-
"json": json.dumps(dumped_map),
1281-
"model": model,
1282-
"name": field,
1283-
}
1298+
data = [list(dumped_map.keys()), list(dumped_map.values()), model, field]
12841299
cr.execute(query, data)
12851300

12861301
def adapter(leaf, _or, _neg):
12871302
left, op, right = leaf
1288-
if isinstance(right, (tuple, list)): # noqa: SIM108
1289-
right = [mapping.get(r, r) for r in right]
1303+
if isinstance(right, (tuple, list)):
1304+
right = [mapping.get(r or None, r or False) for r in right]
12901305
else:
1291-
right = mapping.get(right, right)
1306+
right = mapping.get(right or None, right or False)
12921307
return [(left, op, right)]
12931308

12941309
# skip all inherit, they will be handled by the recursive call

0 commit comments

Comments
 (0)