I'm trying to create materialized views in a Postgres (with TimescaleDB) where the user can control one of the selected fields. I understand that materialized views cannot be parametrized as a prepared statement because these parameters would need to be saved for when the view is updated later on (thanks to this answer).
I'm looking for a way to convince myself that my code is safe when trying to do something like this:
from sqlalchemy import text
from databases import Database
db = Database('postgres+asyncpg://localhost')
await db.connect()
path = '{some_key,nested_key}' # User controlled string
await db.execute(text(
'''
CREATE MATERIALIZED VIEW test_view WITH (timescaledb.continuous) AS
SELECT
time_bucket(interval '5 minutes', timestamp) AS bucket,
avg(:variable_part)
FROM metrics
GROUP BY bucket
WITH NO DATA;
''')
.bindparams(variable_part=f"results#>>'{path}'") # JSONB path selection
)
Of course this example doesn't work, my driver (asyncpg) correctly reports FeatureNotSupportedError: materialized views may not be defined using bound parameters
.
I don't actually want a bound parameter, I want to define a materialized view with the values as provided. If if weren't for worries about injection, we'd just plop the result path in the query string itself.
Am I stuck manually validating the user controlled path
variable with a regex of some kind? I obviously need to be worried about single quotes, parens, braces, comment characters, escapes, etc.
What's the best way forward here?