refactor composer to use sqlalchemy, avoid string concatenations and follow conventions
This commit is contained in:
30
api/composer/metrics.py
Normal file
30
api/composer/metrics.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Metric-fragment qualification.
|
||||
|
||||
Metric expressions in `metrics.yaml` are written *unaliased* — e.g.
|
||||
`AVG(CASE WHEN status = 'B' THEN 1.0 WHEN status = 'A' THEN 0.0 END)`, `A13`,
|
||||
`SUM(amount)`. Before such a fragment can sit in a multi-table query it has to
|
||||
be bound to the metric's own table alias, so the composer can introduce other
|
||||
tables via joins without ambiguity.
|
||||
|
||||
We parse the fragment with sqlglot and inject the alias as the table qualifier
|
||||
on every bare column reference, returning the resulting expression node (not a
|
||||
string) so it splices directly into the query tree the composer assembles.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlglot
|
||||
from sqlglot import exp
|
||||
|
||||
|
||||
def qualify_metric_expr(sql_fragment: str, table_alias: str) -> exp.Expression:
|
||||
"""Parse `sql_fragment` and qualify its bare column refs to `table_alias`.
|
||||
|
||||
Used for both the metric's SELECT expression and its optional WHERE filter
|
||||
(both are written as bare expressions over the metric's table)."""
|
||||
tree = sqlglot.parse_one(sql_fragment, dialect="postgres")
|
||||
for col in tree.find_all(exp.Column):
|
||||
if col.table:
|
||||
continue
|
||||
col.set("table", exp.Identifier(this=table_alias, quoted=True))
|
||||
return tree
|
||||
Reference in New Issue
Block a user