Skip to content

Fix ordering for UNION ALL over heterogeneous constants#23528

Merged
alamb merged 4 commits into
apache:mainfrom
vadimpiven:main
Jul 17, 2026
Merged

Fix ordering for UNION ALL over heterogeneous constants#23528
alamb merged 4 commits into
apache:mainfrom
vadimpiven:main

Conversation

@vadimpiven

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

  • Closes #.

Rationale for this change

add_sort_above and add_sort_above_with_distribution in physical-optimizer prune every expression that EquivalenceProperties::is_expr_constant reports as constant.
This treats AcrossPartitions::Heterogeneous the same as AcrossPartitions::Uniform, which is incorrect.

Check an example:

SELECT 2 AS sample, c FROM t
UNION ALL
SELECT 1 AS sample, c FROM t
ORDER BY sample, c

The union reports sample as a Heterogeneous constant and so it gets pruned, which breaks ORDER BY.

What changes are included in this PR?

Helper that activates pruning only for AcrossPartitions::Uniform, use it at both prune sites (add_sort_above and add_sort_above_with_distribution) so Heterogeneous constants are retained.

Are these changes tested?

Yes, a new unit-test added.

Are there any user-facing changes?

No API changes, just an internal logic bug-fix.

`add_sort_above` and `add_sort_above_with_distribution` pruned every
expression that equivalence properties reported as constant, via
`is_expr_constant(..).is_none()`. That treats `AcrossPartitions::Heterogeneous`
the same as `AcrossPartitions::Uniform`, which is wrong.

A `Heterogeneous` constant is constant *within* each partition but varies
*across* partitions (e.g. `sample` in
`SELECT 1 AS sample, c FROM t UNION ALL SELECT 2 AS sample, c FROM t`).
Both helpers build a partition-preserving `SortExec` whose output is merged
across partitions downstream, where such a column is still the leading
ordering discriminator. Dropping it silently discards the global ordering,
producing non-deterministic output order for `ORDER BY sample, c` once the
plan is rooted at a sink / output requirement.

Only `AcrossPartitions::Uniform` is globally constant and safe to drop.
Introduce `is_uniform_constant` and use it at both prune sites.

Adds a regression test that builds a union with a heterogeneous-constant
column and asserts the key survives `add_sort_above`.
@github-actions github-actions Bot added the optimizer Optimizer rules label Jul 13, 2026
@vadimpiven

Copy link
Copy Markdown
Contributor Author

@alamb Hi! May I ask you to have a look at this tiny bugfix?

plan.is::<GlobalLimitExec>() || plan.is::<LocalLimitExec>()
}

#[cfg(test)]

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.

Can you please write these tests as .slt rather than rust unit tests? That will make it easier to validate that it is hooked up into the right place

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@alamb Changed test to be .slt, thank you for the suggestion, turned out that fix belonged to a different place.

The previous fix touched `add_sort_above`/`add_sort_above_with_distribution`
in physical-optimizer, but that path is not where real SQL loses the key. A
global sort feeding a sink (`CopyTo`) still dropped its leading ordering key
when that key is an `AcrossPartitions::Heterogeneous` constant, producing
non-deterministically ordered sink output.

The actual prune happens in `EquivalenceGroup::normalize_sort_exprs` and
`normalize_sort_requirements`, which filtered out *every* constant via
`is_expr_constant(..).is_none()`. That drops `Heterogeneous` constants too,
even though such a column is constant only *within* a partition and still
discriminates the global order once partitions are merged (e.g. a
partition-preserving `SortExec` feeding a `SortPreservingMergeExec` under a
sink). Only `AcrossPartitions::Uniform` is globally constant and safe to drop.

Revert the physical-optimizer change and instead keep heterogeneous constants
at both normalize sites via a new `is_uniform_constant` helper.

Replace the Rust unit test with an `.slt` regression test that plans
`EXPLAIN COPY (<union> ORDER BY a, b) TO parquet` and asserts the sink-rooted
plan keeps `[a@0 ASC, b@1 ASC]` in the merge above the union. This exercises
the real planning path end to end rather than calling `add_sort_above`
directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added physical-expr Changes to the physical-expr crates sqllogictest SQL Logic Tests (.slt) and removed optimizer Optimizer rules labels Jul 16, 2026
@codecov-commenter

codecov-commenter commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@95de385). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #23528   +/-   ##
=======================================
  Coverage        ?   80.65%           
=======================================
  Files           ?     1086           
  Lines           ?   366102           
  Branches        ?   366102           
=======================================
  Hits            ?   295273           
  Misses          ?    53234           
  Partials        ?    17595           

☔ 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.

@alamb alamb 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.

Thank you @vadimpiven

I took the liberty of pushing a few more tests to your branch and merging up from main

@vadimpiven

Copy link
Copy Markdown
Contributor Author

@alamb Can you please merge the branch if everything looks good? This bug produces sporadic errors in our pipeline.

@alamb
alamb added this pull request to the merge queue Jul 17, 2026
Merged via the queue into apache:main with commit d185ed5 Jul 17, 2026
40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

physical-expr Changes to the physical-expr crates sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants