Skip to content

Commit 88e48ff

Browse files
refactor: literal parameterized SQL in geofence tag migrations
Replaces psycopg2.sql identifier composition with prebuilt literal queries (the aux table name and the valid-link filter are compile-time constants), clearing the odoo-sql-injection-string-format code-scanning alerts. Values remain parameterized; behavior unchanged (spp_gis suite 92/0).
1 parent 9963347 commit 88e48ff

2 files changed

Lines changed: 57 additions & 51 deletions

File tree

spp_gis/migrations/19.0.2.1.0/post-migration.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,30 @@
44
Creates one spp.gis.geofence.tag per distinct legacy vocabulary name and
55
restores the geofence links, then drops the aux table. See pre-migration.py
66
for the full story.
7+
8+
All SQL is literal and value-parameterized; identifiers are never composed.
79
"""
810

911
import logging
1012

11-
from psycopg2 import sql
12-
1313
from odoo import SUPERUSER_ID, api
1414

1515
_logger = logging.getLogger("odoo.addons.spp_gis.migrations.geofence_tags")
1616

17-
AUX_TABLE = "spp_gis_geofence_tag_legacy_migration"
18-
1917

2018
def _table_exists(cr, table):
2119
cr.execute("SELECT 1 FROM information_schema.tables WHERE table_name = %s", (table,))
2220
return bool(cr.fetchone())
2321

2422

2523
def migrate(cr, version):
26-
if not _table_exists(cr, AUX_TABLE):
24+
if not _table_exists(cr, "spp_gis_geofence_tag_legacy_migration"):
2725
return
2826

2927
env = api.Environment(cr, SUPERUSER_ID, {})
3028
Tag = env["spp.gis.geofence.tag"]
31-
aux = sql.Identifier(AUX_TABLE)
3229

33-
cr.execute(sql.SQL("SELECT DISTINCT vocab_name FROM {aux}").format(aux=aux))
30+
cr.execute("SELECT DISTINCT vocab_name FROM spp_gis_geofence_tag_legacy_migration")
3431
tag_ids_by_name = {}
3532
for (name,) in cr.fetchall():
3633
tag = Tag.search([("name", "=", name)], limit=1)
@@ -39,7 +36,7 @@ def migrate(cr, version):
3936
tag_ids_by_name[name] = tag.id
4037

4138
restored = 0
42-
cr.execute(sql.SQL("SELECT geofence_id, vocab_name FROM {aux}").format(aux=aux))
39+
cr.execute("SELECT geofence_id, vocab_name FROM spp_gis_geofence_tag_legacy_migration")
4340
for geofence_id, name in cr.fetchall():
4441
cr.execute(
4542
"""
@@ -51,7 +48,7 @@ def migrate(cr, version):
5148
)
5249
restored += cr.rowcount
5350

54-
cr.execute(sql.SQL("DROP TABLE {aux}").format(aux=aux))
51+
cr.execute("DROP TABLE spp_gis_geofence_tag_legacy_migration")
5552
_logger.info(
5653
"spp_gis geofence tag migration: created %s tags, restored %s links",
5754
len(tag_ids_by_name),

spp_gis/migrations/19.0.2.1.0/pre-migration.py

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,55 @@
1010
This pre-migration moves legacy rows (with the referenced vocabulary's name)
1111
into an aux table; the matching post-migration recreates them as
1212
spp.gis.geofence.tag links and drops the aux table.
13+
14+
All SQL is literal and value-parameterized; identifiers are never composed.
1315
"""
1416

1517
import logging
1618

17-
from psycopg2 import sql
18-
1919
_logger = logging.getLogger("odoo.addons.spp_gis.migrations.geofence_tags")
2020

21-
AUX_TABLE = "spp_gis_geofence_tag_legacy_migration"
21+
_CREATE_AUX = """
22+
CREATE TABLE IF NOT EXISTS spp_gis_geofence_tag_legacy_migration (
23+
geofence_id integer NOT NULL,
24+
vocab_name varchar NOT NULL
25+
)
26+
"""
27+
28+
# name is a translated (jsonb) field: prefer en_US, else any value.
29+
_PARK_LEGACY = """
30+
INSERT INTO spp_gis_geofence_tag_legacy_migration (geofence_id, vocab_name)
31+
SELECT rel.geofence_id,
32+
COALESCE(v.name->>'en_US', (SELECT t.value FROM jsonb_each_text(v.name) t LIMIT 1))
33+
FROM spp_gis_geofence_tag_rel rel
34+
JOIN spp_vocabulary v ON v.id = rel.tag_id
35+
"""
36+
37+
_DELETE_LEGACY = """
38+
DELETE FROM spp_gis_geofence_tag_rel rel
39+
USING spp_vocabulary v
40+
WHERE v.id = rel.tag_id
41+
"""
42+
43+
# Variants used when the new tag table already exists (migration rerun or
44+
# tests): a row is a valid new-style link if its tag_id exists there; it is
45+
# legacy if it references a vocabulary instead. Checking the new table first
46+
# resolves numeric id collisions in favor of valid links.
47+
_PARK_LEGACY_SKIP_VALID = """
48+
INSERT INTO spp_gis_geofence_tag_legacy_migration (geofence_id, vocab_name)
49+
SELECT rel.geofence_id,
50+
COALESCE(v.name->>'en_US', (SELECT t.value FROM jsonb_each_text(v.name) t LIMIT 1))
51+
FROM spp_gis_geofence_tag_rel rel
52+
JOIN spp_vocabulary v ON v.id = rel.tag_id
53+
WHERE rel.tag_id NOT IN (SELECT id FROM spp_gis_geofence_tag)
54+
"""
55+
56+
_DELETE_LEGACY_SKIP_VALID = """
57+
DELETE FROM spp_gis_geofence_tag_rel rel
58+
USING spp_vocabulary v
59+
WHERE v.id = rel.tag_id
60+
AND rel.tag_id NOT IN (SELECT id FROM spp_gis_geofence_tag)
61+
"""
2262

2363

2464
def _table_exists(cr, table):
@@ -30,48 +70,17 @@ def migrate(cr, version):
3070
if not _table_exists(cr, "spp_gis_geofence_tag_rel"):
3171
return
3272

33-
# A row is a valid new-style link if its tag_id exists in the new tag
34-
# table (only possible when this migration reruns or in tests); it is
35-
# legacy if it references a vocabulary instead. Checking the new table
36-
# first resolves numeric id collisions in favor of valid links.
3773
if _table_exists(cr, "spp_gis_geofence_tag"):
38-
valid_clause = sql.SQL("AND rel.tag_id NOT IN (SELECT id FROM spp_gis_geofence_tag)")
74+
park_query = _PARK_LEGACY_SKIP_VALID
75+
delete_query = _DELETE_LEGACY_SKIP_VALID
3976
else:
40-
valid_clause = sql.SQL("")
41-
42-
aux = sql.Identifier(AUX_TABLE)
43-
cr.execute(
44-
sql.SQL(
45-
"""
46-
CREATE TABLE IF NOT EXISTS {aux} (
47-
geofence_id integer NOT NULL,
48-
vocab_name varchar NOT NULL
49-
)
50-
"""
51-
).format(aux=aux)
52-
)
53-
cr.execute(
54-
sql.SQL(
55-
"""
56-
INSERT INTO {aux} (geofence_id, vocab_name)
57-
SELECT rel.geofence_id,
58-
COALESCE(v.name->>'en_US', (SELECT t.value FROM jsonb_each_text(v.name) t LIMIT 1))
59-
FROM spp_gis_geofence_tag_rel rel
60-
JOIN spp_vocabulary v ON v.id = rel.tag_id
61-
WHERE true {valid_clause}
62-
"""
63-
).format(aux=aux, valid_clause=valid_clause)
64-
)
77+
park_query = _PARK_LEGACY
78+
delete_query = _DELETE_LEGACY
79+
80+
cr.execute(_CREATE_AUX)
81+
cr.execute(park_query)
6582
parked = cr.rowcount
66-
cr.execute(
67-
sql.SQL(
68-
"""
69-
DELETE FROM spp_gis_geofence_tag_rel rel
70-
USING spp_vocabulary v
71-
WHERE v.id = rel.tag_id {valid_clause}
72-
"""
73-
).format(valid_clause=valid_clause)
74-
)
83+
cr.execute(delete_query)
7584
_logger.info(
7685
"spp_gis geofence tag migration: parked %s legacy vocabulary tag links (%s rows removed)",
7786
parked,

0 commit comments

Comments
 (0)