Skip to content

fix: plan UNION of untyped aliased placeholders - #25675

Open
adriangb wants to merge 1 commit into
apache:mainfrom
adriangb:fix/aliased-placeholder-union-get-type
Open

adriangb wants to merge 1 commit into
apache:mainfrom
adriangb:fix/aliased-placeholder-union-get-type

Conversation

@adriangb

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

PREPARE p AS SELECT $1 AS a UNION ALL SELECT $2 AS a fails with Schema error: No field named a. The same happens when a caller optimizes such a plan before binding its parameters.

Expr::get_type had a special case for an alias of an untyped placeholder: it looked up the alias name as a column in the schema it receives. The UNION coercion code calls get_type on each projection expression with the projection's input schema, which does not contain the alias, so the lookup fails. Expr::to_field has no such special case and returns a Null field for the same expression.

The special case came from #4701. At that time get_type returned an error for a bare untyped placeholder. Today a bare untyped placeholder has type Null, so the special case is no longer needed.

What changes are included in this PR?

  • Remove the Alias(Placeholder { field: None }) special case from Expr::get_type. An alias now has the type of the expression it wraps, the same as in to_field.

What is the testing strategy for this PR?

Tests added:

  • sqllogictest cases in prepare.slt for PREPARE and EXECUTE of a UNION ALL of aliased placeholders, including a CTE joined to a table.
  • A test in core/tests/sql/select.rs that optimizes the plan with unbound placeholders, then binds and executes it.
  • A unit test in expr_schema.rs that get_type and to_field agree for an aliased untyped placeholder.

The existing prepared statement and placeholder type inference tests pass without changes.

Are there any user-facing changes?

The queries above now plan and run. No API changes.

Expr::get_type looked up the alias of an untyped placeholder as a column
in the schema it receives. UNION coercion passes the projection's input
schema, which does not contain the alias, so planning failed with
"No field named <alias>". Remove the special case so get_type agrees
with to_field and returns Null, like a bare untyped placeholder.

Closes apache#25669
@github-actions github-actions Bot added logical-expr Logical plan and expressions core Core DataFusion crate sqllogictest SQL Logic Tests (.slt) labels Sep 24, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.48%. Comparing base (95bb0a0) to head (fd981db).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #25675      +/-   ##
==========================================
- Coverage   82.48%   82.48%   -0.01%     
==========================================
  Files        1140     1140              
  Lines      437614   437622       +8     
  Branches   437614   437622       +8     
==========================================
+ Hits       360986   360987       +1     
- Misses      54834    54840       +6     
- Partials    21794    21795       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@adriangb
adriangb requested a review from neilconway September 24, 2026 04:28
@adriangb

Copy link
Copy Markdown
Contributor Author

@neilconway could I ping you for review?

@dustinCodes84600

Copy link
Copy Markdown

This is useful for a Rust service where we cache optimized DataFusion logical plans before binding request params. Hit the same schema lookup failure when a parameterized two-row key set used UNION ALL before joining an Arrow-backed table.

fwiw, while reading I made a visual walkthrough covering alias type resolution: https://flyovers.dev/flyover/85a1f93d-380b-43e5-9401-274f9f7e6e81

@adriangb

adriangb commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor Author

This is useful for a Rust service where we cache optimized DataFusion logical plans before binding request params. Hit the same schema lookup failure when a parameterized two-row key set used UNION ALL before joining an Arrow-backed table.

fwiw, while reading I made a visual walkthrough covering alias type resolution: https://flyovers.dev/flyover/85a1f93d-380b-43e5-9401-274f9f7e6e81

I was not expecting what I got when I clicked that link 🤣. If you care about this PR maybe a GitHub review would be helpful in helping get it across the line? Or confirmation that it fixes your problem?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate logical-expr Logical plan and expressions sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Untyped aliased placeholder in a UNION arm fails with "Schema error: No field named <alias>" when the plan is analyzed or optimized

3 participants