Skip to content

fix: stop inlining SELECT-list aliases that shadow FROM columns (#131)#133

Merged
rampage644 merged 3 commits intomainfrom
claude/fix-issue-131-DWLnm
Apr 23, 2026
Merged

fix: stop inlining SELECT-list aliases that shadow FROM columns (#131)#133
rampage644 merged 3 commits intomainfrom
claude/fix-issue-131-DWLnm

Conversation

@rampage644
Copy link
Copy Markdown
Contributor

Summary

Fixes #131 — a SELECT-list alias that shadows a FROM-clause column name was being silently inlined into other projection expressions, producing wrong aggregate results.

InlineAliasesInSelect (crates/functions/src/visitors/inline_aliases_in_query.rs) is a pre-planning SQL-AST rewriter that replaces references to SELECT-list aliases with their underlying expressions. For derived subqueries in FROM it already gathers the inner column/alias names into subquery_idents and skips substitutions that would collide. But for named tables, CTEs, and table functions it had no schema information and inlined blindly, producing:

SELECT user_start_tstamp AS start_tstamp,
       MAX(CASE WHEN start_tstamp = user_start_tstamp THEN sid END) ...
FROM s  -- CTE with a real `start_tstamp` column

rewritten into CASE WHEN user_start_tstamp = user_start_tstamp ... — a tautology — collapsing MAX(CASE ...) to MAX(sid). Per ANSI SQL and Snowflake the reference must resolve to the FROM-clause column.

Fix

Skip projection-list alias inlining whenever FROM contains any non-derived relation (named table, CTE, table function, or a nested join that leads to one). WHERE / QUALIFY inlining is unchanged. Derived-subquery FROMs continue to rely on the existing subquery_idents path.

Test plan

  • cargo test -p functions --lib tests::visitors — 9/9 pass, including a new case for the issue reproducer.
  • cargo test -p executor --lib tests::sql::dml::select — 5/5 pass, including a new end-to-end snapshot query_alias_shadows_column_in_aggregate_case that runs the exact query from the issue and asserts first_sid = S1 (the Snowflake-correct answer, vs. the S2 that Embucket was returning pre-fix).
  • CI green.

https://claude.ai/code/session_01RqHxFFaNxKbhGTGWD6A19z


Generated by Claude Code

claude added 3 commits April 23, 2026 14:54
…ations

`InlineAliasesInSelect` textually substituted SELECT-list alias references
into other projection expressions before the planner ran. When an alias
shadowed a real column of the FROM-clause relation, this silently rewrote
the query into an incorrect one -- e.g. `start_tstamp = user_start_tstamp`
inside `MAX(CASE WHEN ...)` was turned into the tautology
`user_start_tstamp = user_start_tstamp`, and `MAX(CASE WHEN TRUE THEN sid
END)` degenerated to `MAX(sid)`. Snowflake and ANSI SQL resolve such
references against the FROM-clause column, not the alias.

For derived subqueries in FROM the visitor already collects the inner
column/alias names into `subquery_idents` and skips substitutions that
collide. For named tables, CTEs, and table functions we have no schema at
the AST level, so we now conservatively skip projection-list alias
inlining whenever any such relation appears in FROM. WHERE/QUALIFY
inlining is unchanged.

Fixes #131.
…le_if)

Rust 1.95 promoted two lints from `pedantic` to `correctness`/
`complexity`:
- `useless_conversion` — `.into_iter()` on arguments already accepting
  `IntoIterator` (in `stream::iter`, `Extend::extend`, `Iterator::zip`,
  `Iterator::chain`, `ScalarValue::iter_to_array`).
- `collapsible_if` — `if` inside a match arm that only gates the entire
  arm; the condition belongs on a `match` guard.

The workspace's `[workspace.lints.clippy] all = "deny"` turns both into
hard errors, so CI broke on the first clippy run after the toolchain
bump. Mechanical, behavior-preserving fixes across `catalog`,
`executor`, and `functions`.
@rampage644 rampage644 merged commit 2df570f into main Apr 23, 2026
3 checks passed
@rampage644 rampage644 deleted the claude/fix-issue-131-DWLnm branch April 23, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Column reference inside aggregate CASE resolves to SELECT-list alias instead of FROM-column (silently wrong result)

2 participants