Skip to content

fix(wren): resolve model aliases within SQL query scopes - #2798

Merged
goldmedal merged 2 commits into
Canner:mainfrom
dafyy321-pixel:codex/fix-cte-alias-scopes
Oct 20, 2026
Merged

goldmedal merged 2 commits into
Canner:mainfrom
dafyy321-pixel:codex/fix-cte-alias-scopes

Conversation

@dafyy321-pixel

@dafyy321-pixel dafyy321-pixel commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #2795.

Resolve model aliases within each SQL query scope so valid UNION, INTERSECT, EXCEPT, and nested queries can reuse table aliases. Previously, an alias reused by another SELECT could make Wren select a column from the wrong model and reject the query during planning.

The fix uses sqlglot's existing traverse_scope() support to record aliases separately for each scope before identifier normalization. Column collection then uses the map belonging to the column's scope. Derived-table and CTE sources resolve to scope objects, so their output columns are no longer attributed to a model merely because an alias has the same spelling elsewhere.

The same scope maps are used for the case-sensitive column path and for qualified-column normalization in Oracle/Snowflake output. Star detection now checks each SELECT's own sources, including nested SELECTs: t.* expands the model bound to that t, and a bare * marks the models in that scope. Model expansion continues to pass stars through wren-core. This replaces the old top-level-only SELECT walker, which is no longer needed.

What failure does this repair?

With an orders model exposing id and amount, and a customers model exposing id and name, this SQL is valid:

SELECT t.amount FROM orders t
UNION ALL
SELECT length(t.name) FROM customers t

Before this change, WrenEngine.dry_plan() produces the following error:

Failed to create logical plan: Schema error: No field named customers.amount. Valid fields are customers.id, customers.name.
WrenError [INVALID_SQL] Schema error: No field named customers.amount. Valid fields are customers.id, customers.name. phase=SQL_PLANNING

_build_alias_map() previously stored both uses of t in a single dictionary. The customers branch overwrote the orders branch, and column collection built a per-model query containing customers.amount. SQL gives each UNION branch its own alias scope, so these references must be collected separately.

The complete MDL, setup, and runnable SDK reproduction are in #2795. With orders amounts 10 and 20, and customer names Alice and Bob, direct DuckDB execution returns [(3,), (5,), (10,), (20,)] after sorting. The same reproduction now plans successfully and returns those values when the planned SQL is executed in DuckDB.

The regression also occurs without a set operation:

SELECT t.o_orderkey
FROM orders t
WHERE EXISTS (
    SELECT 1 FROM customer t WHERE t.c_name = 'Alice'
)

Here the inner t belongs to customer; it must not change the binding of the outer t.o_orderkey. Related regression tests cover an outer alias reused for a derived table or CTE and a qualified star whose alias is reused in a nested SELECT.

How is it tested?

Added 19 test cases in tests/unit/test_cte_rewriter.py. They call the real CTE rewriter and semantic engine rather than matching source text. The result tests execute rewritten SQL in a local DuckDB database.

  • Reused aliases in UNION ALL, INTERSECT, and EXCEPT, parameterized over DuckDB, PostgreSQL, Oracle, and Snowflake dialects.
  • An independent subquery that shadows an outer alias, plus derived-table and CTE output aliases with the same spelling as a model alias in another scope.
  • A correlated reference that must still resolve to the outer scope.
  • Qualified and nested stars, with recorded transform_sql() calls to check that the correct model receives SELECT *.
  • Reused aliases when a model declares case-distinct column names, exercising the case-sensitive collection path.

The correlated-reference case also guards an outer-scope binding that already worked before the change.

From core/wren, the related suites pass:

uv run --no-sync pytest tests/unit/test_cte_rewriter.py \
  tests/unit/test_case_sensitivity.py tests/unit/test_engine.py -q
# 97 passed

The changed files also pass ruff check, ruff format --check, and git diff --check. The new tests are in the ordinary unit-test tree, which .github/workflows/wren-ci.yml runs without requiring a connector service or an optional extra.

The PostgreSQL, Oracle, and Snowflake cases use their dialect parsers and the real semantic engine, then transpile the planned SQL to DuckDB to compare results. They do not connect to live instances of those databases. The local run used wren-core-py 0.8.0; CI's unit-test job builds the engine bindings from source.

Duplicate check

The alias-map limitation was acknowledged by AmirF194 in this comment on #2743, which describes it as a pre-existing column-resolution gap deserving a separate follow-up. The comment does not explicitly claim that follow-up. This PR implements the separate scope-resolution fix discussed there.

Checked the open #2783, which also changes core/wren/src/wren/mdl/cte_rewriter.py. That PR repairs ClickHouse SQL translation and changes different hunks in the rendering/translation path. This PR changes alias maps, column attribution, column-case handling, and star collection; it leaves those translation hooks alone. The shared file still needs a fresh comparison when rebasing before merge.

Summary by CodeRabbit

  • Bug Fixes
    • Improved SQL handling so model and column references are resolved correctly within nested queries, common table expressions, and separate branches of set operations.
    • Corrected handling of correlated references and wildcard selections, including qualified wildcards, to ensure the appropriate models are included when queries are rewritten.

@github-actions github-actions Bot added python Pull requests that update Python code core labels Oct 9, 2026
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6bfaaa68-c262-4867-820c-b75c5a95dda7

📥 Commits

Reviewing files that changed from the base of the PR and between 2141dd2 and 6775299.


📒 Files selected for processing (2)
  • core/wren/src/wren/mdl/cte_rewriter.py
  • core/wren/tests/unit/test_cte_rewriter.py

🚧 Files skipped from review as they are similar to previous changes (2)
  • core/wren/tests/unit/test_cte_rewriter.py
  • core/wren/src/wren/mdl/cte_rewriter.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.



Walkthrough

CTE rewriting now resolves model aliases and column references per SQL scope. It also detects stars by scope. Regression tests cover reused aliases, correlated references, nested queries, and case-distinct columns.

Changes

Scope-aware CTE rewriting

Layer / File(s) Summary
Scoped model resolution and star detection
core/wren/src/wren/mdl/cte_rewriter.py, core/wren/tests/unit/test_cte_rewriter.py
Model aliases and column references are resolved within SQL scopes. Case-sensitive collection and canonicalization use scoped bindings. Star detection handles bare and qualified stars within each scope. Tests cover set-operation branches, subqueries, derived tables, CTEs, correlated references, and case-distinct columns.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 67752

No merge-blocking issue is identified; merge after normal checks.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: resolving Wren model aliases within SQL query scopes.
Description check Passed The description includes all required sections. It explains the scope-resolution fix, provides a concrete reproduction and actual error output, documents the test coverage and commands, and records th…
Linked Issues check Passed The implementation satisfies #2795. cte_rewriter.py builds alias maps per sqlglot scope and uses scoped bindings for model-column collection, case-sensitive collection, identifier normalization, c…
Out of Scope Changes check Passed The whole-PR diff changes only core/wren/src/wren/mdl/cte_rewriter.py and core/wren/tests/unit/test_cte_rewriter.py. The implementation and regression tests directly support the scoped-alias requi…
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 2 files.


✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks each query’s scope,
And maps each alias to its home.
Through unions, stars, and CTEs,
It follows columns through the trees.
Case-distinct names now keep their place,
As hopping tests confirm the trace.

Comment @coderabbitai help to get the list of available commands.

@dafyy321-pixel

Copy link
Copy Markdown
Contributor Author

Added docstrings for scoped column collection and the new regression fixtures and cases in 6775299. They describe how correlated columns retain their outer binding and what each alias-reuse test checks. This update only adds documentation; the scope-resolution behavior is unchanged.

The CTE rewriter, case-sensitivity, and engine suites still pass: 97 tests. Ruff check, format check, and the diff whitespace check also pass.

@goldmedal goldmedal left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @dafyy321-pixel 👍

@goldmedal
goldmedal merged commit b04ac03 into Canner:main Oct 20, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reused table aliases across SELECT scopes break SDK query planning

2 participants