feat(column-usage): expose WHERE-clause function wrappers on ColumnUsage#56
Merged
Merged
Conversation
Adds `ColumnUsage.Function` (`*FunctionWrapper`) carrying typed metadata about an allowlisted function call wrapping a bare column reference in a WHERE-clause predicate. The eight allowlisted wrappers — length, lower, upper, coalesce, extract, date_trunc, char_length, octet_length — are the structural-semantic boundary for "function calls that wrap a single column in a predicate position and change the comparison's semantics". Any wider set would dilute the contract for downstream consumers (ORMs, linters, query rewriters, AI-assisted SQL generators) that key off it. `FunctionWrapper` carries: - Name: canonical lowercase, unqualified. - Schema: "" for unqualified or pg_catalog (canonicalised); any other schema rejects the wrapper outright. - Args: literal arguments other than the column itself (extract field, date_trunc unit, coalesce defaults). Each FunctionArg has a *string Literal for recoverable literals (NULL handled separately via IsNull), nil for non-literal expressions. - IsNested: outermost-only attribution; true when the wrapped column is reached through more allowlisted-function wrappers. - Cast: outermost cast target type (length(col)::int, length(col)::int::bigint, CAST(length(col) AS bigint)). Empty when there is no cast. Scope is enforced by threading `wantWrappers bool` through findAndRecordComparisons. WHERE call sites (SELECT, UPDATE, DELETE, set-op branches, subqueries, CTE bodies) pass true; HAVING passes false. JOIN ON, ORDER BY, GROUP BY, window PARTITION/ORDER, RETURNING and SELECT projection use other extraction paths and never produce a wrapper by construction. The legacy ColumnUsage.Functions []string is unchanged on WHERE entries (stays empty there); document the asymmetry. `Functions []string` continues to apply to projection/order/group/etc. contexts unchanged. Mirrored in analysis as SQLFunctionWrapper / SQLFunctionArg type aliases plus a deep-clone in convertColumnUsage so analysis-side mutation cannot affect the parser IR. Test matrix covers: all 8 functions, all comparison operators (=, <>, <, >, <=, >=, LIKE, ILIKE, NOT LIKE, IN, NOT IN, BETWEEN, IS NULL), both LHS and RHS positions, case insensitivity, pg_catalog canonicalisation, three nesting depths, casts on every operator type, casts via :: and CAST(... AS ...), DML UPDATE/DELETE WHERE, subquery WHERE, CTE body WHERE, every branch of UNION/UNION ALL/INTERSECT/EXCEPT, quoted columns with parens, non-literal date_trunc unit, placeholder extract field, NULL coalesce default. Negative cases: HAVING, INNER/LEFT/RIGHT/FULL/CROSS/USING joins, ORDER BY, GROUP BY, window PARTITION/ORDER, RETURNING, projection, foreign schemas, non-allowlisted functions, expressions inside wrappers, casts on bare columns and on expressions. Closes #51
eitamring
marked this pull request as draft
May 5, 2026 22:12
Contributor
Author
|
In testing |
Add a Column Usage subsection that documents the new Function field on ColumnUsage, the FunctionWrapper struct, the FunctionArg struct, the 8-name allowlist, the WHERE-equivalent attribution scope, schema canonicalisation, outermost-only nesting attribution, cast capture, and the rule for expression-wrapped columns. Add a usage snippet under Practical Guidance showing how downstream consumers walk filter usages and switch on Function.Name.
Move the contents of parser_ir_function_wrapper_test.go into the existing parser_ir_advanced_test.go and delete the standalone file. This continues the trend of merging narrowly-scoped test files back into the broader advanced-IR suite rather than growing the flat top-level file count. The strPtr / findUsageWithFunction / flattenColumnUsage helpers live with the function-wrapper tests now and are only referenced from those tests, so consolidation is a no-op for compile + go test.
eitamring
marked this pull request as ready for review
May 6, 2026 22:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
ColumnUsage.Function(*FunctionWrapper) carrying typed metadata about an allowlisted function call wrapping a bare column reference in a WHERE-clause predicate. The eight allowlisted wrappers — length, lower, upper, coalesce, extract, date_trunc, char_length, octet_length — are the structural-semantic boundary for "function calls that wrap a single column in a predicate position and change the comparison's semantics". Any wider set would dilute the contract for downstream consumers (ORMs, linters, query rewriters, AI-assisted SQL generators) that key off it.FunctionWrappercarries:Scope is enforced by threading
wantWrappers boolthrough findAndRecordComparisons. WHERE call sites (SELECT, UPDATE, DELETE, set-op branches, subqueries, CTE bodies) pass true; HAVING passes false. JOIN ON, ORDER BY, GROUP BY, window PARTITION/ORDER, RETURNING and SELECT projection use other extraction paths and never produce a wrapper by construction. The legacy ColumnUsage.Functions []string is unchanged on WHERE entries (stays empty there); document the asymmetry.Functions []stringcontinues to apply to projection/order/group/etc. contexts unchanged.Mirrored in analysis as SQLFunctionWrapper / SQLFunctionArg type aliases plus a deep-clone in convertColumnUsage so analysis-side mutation cannot affect the parser IR.
Test matrix covers: all 8 functions, all comparison operators (=, <>, <,
Closes #51
Summary
Layer
ir.go,ddl.go,select.go,dml_*.go,merge.go,setops.go,entry.go)analysis/)SQL examples
-- exampleTest plan
make testpasses (race detector + coverage)make vetpassesexamples/Related issues
Checklist
gen/(auto-generated ANTLR code)docs/parsed-query.md(if applicable)