Skip to content

Register the aliases of table functions and MATCH_RECOGNIZE - #13

Merged
ravenac95 merged 3 commits into
mainfrom
claude/sharp-archimedes-e6egci-table-aliases
Oct 6, 2026
Merged

ravenac95 merged 3 commits into
mainfrom
claude/sharp-archimedes-e6egci-table-aliases

Conversation

@ravenac95

Copy link
Copy Markdown
Member

Summary

build_scope lost two kinds of aliased FROM items:

  • Aliased table functions. my_func(x) AS g is parsed as Alias(Function). add_table_to_scope recursed into the inner Function, which registers nothing, so the alias was dropped and g.y could not resolve.
  • Aliased MATCH_RECOGNIZE. t MATCH_RECOGNIZE (...) AS m was not handled at all: the whole item was reported through ScopeVisitor::skipped and nothing was registered, so m.col could not resolve either.

This builds on #12 (now merged), which registers the relations inside parenthesized joins.

The change

  • Table functions. Alias(Function) is now registered as a virtual source, the same way an aliased UNNEST already was; the function's arguments are reported through skipped. In the resolver, the source's columns are the alias's column list (my_func(x) AS g(y, z)), or unknown when the alias has none (one shared table_function_output_columns helper for both resolver paths).
  • MATCH_RECOGNIZE. An aliased MATCH_RECOGNIZE is registered as its alias with SourceKind::DerivedTable and is_scope = false, like an aliased parenthesized join. It is not registered as Virtual because lineage scans a virtual source's whole expression for column references, which would pick up the MEASURES/DEFINE pattern-variable references such as A.tstamp as if they were real columns.
    • The alias hides the input relation: the input is still built and reported through ScopeVisitor::reference at depth + 1, before the MATCH_RECOGNIZE itself, but it adds no entry to Scope::sources and a later LATERAL item does not see it.
    • When the input is an unaliased parenthesized join, its join conditions may use the hidden names, so they are reported via skipped (through the shared for_each_join_condition) instead of getting subquery scopes.
    • The PARTITION BY, ORDER BY, MEASURES and DEFINE clauses are reported via skipped. An unaliased MATCH_RECOGNIZE is still reported whole through skipped.
  • The save-and-restore of names behind an alias is now shared by the aliased-join and MATCH_RECOGNIZE paths through a hide_names helper.

Behaviour changes visible to callers

  • qualify_columns now qualifies a reference to an aliased table function's column: SELECT x, y FROM t, my_func(t.x) AS g(y) becomes SELECT t.x AS x, g.y AS y FROM t, MY_FUNC(t.x) AS g(y).
  • Lineage traces g.y to the function's arguments, under a synthetic _N source name (e.g. _0.y) like other virtual sources, with source_alias = "g".
  • Without a schema, an unqualified column is now resolved among all FROM items, the function included. So SELECT a FROM t, my_func(t.x) AS g no longer assumes a is t.a.

Known gaps, left for follow-up

  • Star expansion in qualify_columns, lineage's get_source_name, and the ordered-source helpers do not yet recognise Alias(Function) or MatchRecognize, so g.* and * miss those items' columns.
  • The parser rejects MATCH_RECOGNIZE after an aliased table (t AS x MATCH_RECOGNIZE (...)) or an aliased derived table, so those shapes cannot be tested yet.

Tests

  • scope::tests::test_aliased_table_function_is_a_virtual_source: AS g, bare g and AS g(y, z) register g as a virtual source, with the arguments skipped.
  • scope::tests::test_aliased_match_recognize_hides_its_input (Snowflake and Oracle): the alias is a non-scope DerivedTable; the input is reported at depth + 1 before the item; exactly the four clauses are skipped; an input that shares a name with an earlier source hides it only inside the item; a parenthesized-join input's condition is skipped and not scoped; an unaliased MATCH_RECOGNIZE is still reported whole; a later LATERAL item sees the alias but not the input.
  • qualify_columns::tests::test_qualify_aliased_table_function_columns: with and without a column list.
  • lineage::tests::test_lineage_aliased_table_function_is_a_virtual_source: g.y maps to _0.y (virtual, alias g), with t.x downstream.

Local CI

Run on the final tree with CARGO_INCREMENTAL=0 CARGO_PROFILE_DEV_DEBUG=0:

  • cargo fmt --all -- --check: clean
  • make check-consistency: OK (15 tests, project consistent)
  • cargo clippy -p polyglot-sql-ffi --all-targets --no-deps -- -D warnings: clean
  • cargo clippy -p polyglot-sql-wasm --lib --no-deps -- -D warnings: clean
  • make test-rust-verify-core: all green
    • lib unit tests: 1343 passed, 0 failed (2 ignored)
    • deep_nesting_regression 23, transform_regression 20, dialect_matrix 232: all passed
    • SQLGlot identity 977/977, dialect identity 4086/4086, transpilation 6045/6045, transpile 154/154, parser 32/32
    • custom dialects: identity 466/466, transpilation 388/388
  • cargo test --lib -p polyglot-sql prints no warnings. The first commit also passes the lib tests on its own (1342 passed).

🤖 Generated with Claude Code

https://claude.ai/code/session_016uUGFKsqsLyKC6tprkPgFp


Generated by Claude Code

claude added 2 commits October 5, 2026 23:31
`build_scope` dropped the alias of an aliased table function FROM item such
as `my_func(x) AS g`: it registered nothing, so `g.y` could not resolve. The
alias is now registered as a virtual source, as for an aliased UNNEST, and a
column list on the alias becomes the source's columns.

An aliased MATCH_RECOGNIZE item is now registered as its alias, a source with
no scope of its own. The alias hides the input relation, which is still
built and reported one depth below the item, the way the items inside an
aliased parenthesized join are. The hiding is shared through `hide_names`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uUGFKsqsLyKC6tprkPgFp
The conditions of a parenthesized join that is the input of an aliased
MATCH_RECOGNIZE may use the names the alias hides, so they are reported as
skipped, as inside an aliased join, instead of being neither scoped nor
reported.

Adds tests for the exact skipped clauses, a name collision with the hidden
input, a parenthesized-join input, an unaliased MATCH_RECOGNIZE, a table
function without a column list, and the lineage of an aliased table
function's column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uUGFKsqsLyKC6tprkPgFp

@ravenac95 ravenac95 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY-focused review. 0 Critical, 1 Important, 2 Minor.

Spec compliance was not assessed: no Linear issue (out of scope for this pass). polyglot has no REVIEW.md; severities follow the code-reviewer taxonomy.


Generated by Claude Code

Comment thread crates/polyglot-sql/src/scope.rs Outdated
Comment thread crates/polyglot-sql/src/resolver.rs
Comment thread crates/polyglot-sql/src/scope.rs Outdated
An aliased parenthesized join and an aliased MATCH_RECOGNIZE both
registered their inner relations, hid them, and registered the item as a
non-scope derived-table source under its alias. That sequence now lives
in add_behind_alias, which absorbs hide_names; each caller passes only
the closure that registers its inner relations. Reporting the join
conditions behind an alias through `skipped` moves into
skip_join_conditions, so the reason they stay unscoped is stated once.

In the resolver, identifier_names replaces the three copies of the
column-alias-to-name mapping used for aliased UNNEST, table functions
and Hive LATERAL VIEW. Behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uUGFKsqsLyKC6tprkPgFp
@ravenac95
ravenac95 merged commit 415d931 into main Oct 6, 2026
12 checks passed
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.

2 participants