tdfs4ds.agent.sql_invariants¶
tdfs4ds.agent.sql_invariants
¶
Structural invariants for detecting business-logic drift in a SQL rewrite.
Why this exists¶
The optimizer accepts a rewrite when EXPLAIN compiles and user_score does not
regress. Neither condition has anything to do with the query still meaning the
same thing — and the score is actively anti-correlated with safety: it is
derived from the count of [You] items in the EXPLAIN plan, so a candidate that
drops a predicate, loses a DISTINCT or removes a join produces a simpler plan,
scores higher, and is accepted preferentially.
Column qualification (:mod:tdfs4ds.agent.sql_qualify) removes one failure mode
— silent rebinding of a bare column when scopes merge. It does nothing about the
others, all of which compile cleanly:
- a dropped
WHERE/ONpredicate - an
INNERjoin silently becomingLEFT(or vanishing) during a flatten - a changed aggregation grain when two
GROUP BYlevels collapse - a lost
DISTINCT/QUALIFY - a narrowed window
PARTITION BY - a dropped source table
This module extracts a coarse structural fingerprint from a query and diffs two fingerprints. It is deliberately conservative in one direction only: it can miss a semantic change (it is a fingerprint, not a prover), but a violation it does report is a real structural difference that a simplification is not entitled to make on its own.
It is a cheap, offline, always-on gate — not a substitute for executing both queries and comparing rows, which remains the only complete oracle.
__all__
module-attribute
¶
_JOIN_RE
module-attribute
¶
_JOIN_RE = compile('\\b(?:(LEFT|RIGHT|FULL)\\s+(?:OUTER\\s+)?JOIN|(INNER)\\s+JOIN|(CROSS)\\s+JOIN|(?<!\\w)(JOIN))\\b', IGNORECASE)
_TABLE_REF_RE
module-attribute
¶
_AGG_RE
module-attribute
¶
_AGG_RE = compile('\\b(SUM|COUNT|AVG|MIN|MAX|STDDEV_SAMP|STDDEV_POP|VAR_SAMP|VAR_POP|MEDIAN|PERCENTILE_CONT|PERCENTILE_DISC|REGR_SLOPE|CORR)\\s*\\(', IGNORECASE)
_WINDOW_PARTITION_RE
module-attribute
¶
_WINDOW_PARTITION_RE = compile('PARTITION\\s+BY\\s+(.*?)(?:\\bORDER\\s+BY\\b|\\bROWS\\b|\\bRANGE\\b|\\))', IGNORECASE | DOTALL)
_GROUP_BY_RE
module-attribute
¶
_GROUP_BY_RE = compile('\\bGROUP\\s+BY\\b(.*?)(?=\\bHAVING\\b|\\bQUALIFY\\b|\\bORDER\\s+BY\\b|\\bUNION\\b|\\bINTERSECT\\b|\\bEXCEPT\\b|\\bMINUS\\b|\\)|\\Z)', IGNORECASE | DOTALL)
_QUALIFIER_RE
module-attribute
¶
InvariantViolation
¶
One structural difference between an original query and a rewrite.
_strip_noise
¶
Remove comments and blank out string literals (keeping length stable-ish).
_strip_qualifiers
¶
Drop alias. prefixes from an expression.
Grain and partitioning must be compared alias-insensitively: renaming
c to cl is a legitimate rewrite the LLM performs constantly, and
comparing the raw text would flag every one of them as a grain change,
making the guard useless.
The cost is precision when two relations expose the same column name and the
rewrite switches which one it groups by. That is exactly the case the
qualifier pass (:mod:tdfs4ds.agent.sql_qualify) makes explicit upstream, so
the two layers cover each other.
_split_top_level
¶
Split on sep at paren depth 0.
_count_predicates
¶
Count boolean conjuncts in WHERE / ON / HAVING / QUALIFY clauses.
A count, not a set: predicate text legitimately changes under rewriting
(a.x = b.y ↔ b.y = a.x), but a rewrite that reduces the number of
filtering conditions has almost certainly dropped one.
extract_invariants
¶
Return a coarse structural fingerprint of sql.
Keys
tables set of DB.OBJ referenced in FROM/JOIN
join_kinds multiset (as a sorted list) of join flavours
n_predicates count of boolean conjuncts across WHERE/ON/HAVING/QUALIFY
has_distinct bool
has_qualify bool
group_by_keys sorted list of normalised GROUP BY expressions
n_group_by number of GROUP BY clauses
partition_keys sorted list of normalised window PARTITION BY key lists
agg_funcs sorted list of aggregate function names used
set_ops sorted list of UNION/INTERSECT/EXCEPT operators used
diff_invariants
¶
Compare two queries structurally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
original_sql
|
str
|
The trusted query. |
required |
candidate_sql
|
str
|
The proposed rewrite. |
required |
allow
|
Optional[set]
|
Violation |
None
|
Returns:
| Type | Description |
|---|---|
list
|
A list of :class: |
list
|
structurally consistent with the original. |
Direction matters: only losses are blocking. A rewrite that adds a predicate or a GROUP BY key is doing something intentional and is reported as a warning, not a block — the dangerous direction is silently doing less work than the original.
describe_violations
¶
One-line-per-violation rendering, blocking first.