Skip to content

fix: preserve per-node UNION vs UNION ALL when unparsing - #24498

Open
shinzoxD wants to merge 1 commit into
apache:mainfrom
shinzoxD:fix/union-unparse-quantifier
Open

fix: preserve per-node UNION vs UNION ALL when unparsing#24498
shinzoxD wants to merge 1 commit into
apache:mainfrom
shinzoxD:fix/union-unparse-quantifier

Conversation

@shinzoxD

Copy link
Copy Markdown

Which issue does this PR close?

Rationale for this change

Unparsing mixed UNION / UNION ALL trees was incorrect. a UNION ALL (b UNION a) was emitted as a flat, all-distinct a UNION b UNION a. QueryBuilder::distinct_union was a single query-level boolean: walking the inner Distinct(Union) set that flag, and every later UNION in the same statement then unparsed as distinct. Set quantifiers are a per-node property, so a sticky statement-wide flag cannot represent mixed ALL vs DISTINCT.

What changes are included in this PR?

  • Consume distinct_union on the UNION node that it belongs to (take_distinct_union), so the flag cannot leak to a parent or sibling union.
  • Unparse each UNION operand in its own QueryBuilder. Nested distinct unions (and operand-scoped ORDER BY / LIMIT) stay isolated from the enclosing set operation.
  • Wrap non-SELECT or clause-bearing operands as parenthesized subqueries. sqlparser does not parenthesize nested SetOperations, so this is required to keep ALL vs distinct precedence.

Are these changes tested?

Yes. The issue reproduction is in datafusion/sql/tests/cases/plan_to_sql.rs as both a plan round-trip and a snapshot of the unparsed SQL. The snapshot asserts the outer UNION ALL and the parenthesized inner UNION. Without this change the snapshot fails because the unparser emits a flat distinct UNION.

Are there any user-facing changes?

Unparsed SQL for mixed UNION / UNION ALL now preserves each node's set quantifier and the parentheses needed for that meaning. This is a bug fix for plan_to_sql; there is no public API break.

QueryBuilder::distinct_union was a sticky query-level flag, so a nested Distinct(Union) rewrote every UNION in the statement as DISTINCT. Consume the flag per UNION node and unparse each operand in its own QueryBuilder so mixed ALL vs DISTINCT (and parentheses) are preserved.
Copilot AI lite review requested due to automatic review settings August 19, 2026 19:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Jefffrey

Copy link
Copy Markdown
Contributor

we seem to already have a PR for this

@Jefffrey

Copy link
Copy Markdown
Contributor

for anyone looking at this PR please see my comment here:

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

Labels

sql SQL Planner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Correctness issue with unparsing UNION & UNION ALL

3 participants