refactor composer to use sqlalchemy, avoid string concatenations and follow conventions
This commit is contained in:
46
api/composer/filters.py
Normal file
46
api/composer/filters.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""Filter rendering — one typed `Filter` → one SQL predicate node.
|
||||
|
||||
Each filter shape maps to a sqlglot expression node (`exp.EQ`, `exp.In`,
|
||||
`exp.Between`, or the date-range subtree). Literals go through `exp.convert`,
|
||||
so quoting and escaping are sqlglot's job, not ours. The column is resolved to
|
||||
its owning table and qualified via the shared `AliasMap` (allocating an alias,
|
||||
and implying a join later, if the filter introduces a new table).
|
||||
|
||||
Adding a new filter shape is a new branch here that returns an `exp` node — no
|
||||
change to the assembly in `compose.py`. The `Filter` type stays the constraint
|
||||
boundary: there is no raw-SQL escape hatch.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlglot import exp
|
||||
|
||||
from api.composer.aliases import AliasMap
|
||||
from api.composer.dates import resolve_date_range
|
||||
from api.composer.types import Filter, PickValidationError
|
||||
from api.recon.types import Recon
|
||||
|
||||
|
||||
def render_filter(
|
||||
f: Filter,
|
||||
recon: Recon,
|
||||
aliases: AliasMap,
|
||||
encodings: dict[str, dict[str, str]],
|
||||
) -> exp.Expression:
|
||||
"""Render one `Filter` to a predicate node bound to the column's table."""
|
||||
table, col = recon.resolve_column(f.column)
|
||||
col_expr = aliases.col(table, col.name)
|
||||
|
||||
kind = f.kind()
|
||||
if kind == "equals":
|
||||
return exp.EQ(this=col_expr, expression=exp.convert(f.equals))
|
||||
if kind == "in_values":
|
||||
if not f.in_values:
|
||||
raise PickValidationError(f"filter on {f.column!r} has empty in_values")
|
||||
return exp.In(this=col_expr, expressions=[exp.convert(v) for v in f.in_values])
|
||||
if kind == "between":
|
||||
lo, hi = f.between
|
||||
return exp.Between(this=col_expr, low=exp.convert(lo), high=exp.convert(hi))
|
||||
if kind == "date_range":
|
||||
return resolve_date_range(f.date_range, col, col_expr, encodings)
|
||||
raise PickValidationError(f"unknown filter kind {kind!r}")
|
||||
Reference in New Issue
Block a user