Disable prune_by_limit when output ordering is required#48
Merged
zhuqi-lucas merged 1 commit intobranch-52from Apr 14, 2026
Merged
Disable prune_by_limit when output ordering is required#48zhuqi-lucas merged 1 commit intobranch-52from
zhuqi-lucas merged 1 commit intobranch-52from
Conversation
xudong963
approved these changes
Apr 14, 2026
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect results for Parquet scans where ORDER BY + LIMIT is planned as an order-preserving scan and row-group pruning uses prune_by_limit, which can incorrectly drop partially-matched row groups that may contain earlier-sorting rows.
Changes:
- Add a
preserve_orderflag toParquetOpenerand set it based onFileScanConfig.output_orderinginParquetSource::create_file_opener. - Guard
prune_by_limitso it is only applied whenpreserve_orderisfalse. - Add a regression test ensuring
preserve_order=trueprevents limit-based pruning from skipping partially-matched row groups.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
datafusion/datasource-parquet/src/source.rs |
Propagates ordering requirements from FileScanConfig into the Parquet file opener via preserve_order. |
datafusion/datasource-parquet/src/opener.rs |
Disables limit pruning when output ordering must be preserved; adds a regression test covering the correctness bug. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
When a parquet scan has output ordering constraints (e.g. ORDER BY + LIMIT), prune_by_limit must not discard partially-matched row groups. A partially- matched group may contain rows that sort before any fully-matched group, so skipping it returns incorrect results. This matches the fix in upstream DataFusion (apache#21190) where prune_by_limit is guarded by preserve_order. Add preserve_order: bool to ParquetOpener. Set to true when output_ordering is non-empty. Guard prune_by_limit with !preserve_order.
4fe6b27 to
fa4144d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix incorrect query results when ORDER BY + LIMIT is combined with partially-matched row groups.
prune_by_limitdiscards partially-matched row groups when enough fully-matched groups can satisfy the limit. This is only safe for unordered scans — with output ordering, a partially-matched group may contain rows that sort before any fully-matched group.Root cause
ParquetOpenerhad no awareness of output ordering. It calledprune_by_limitunconditionally whenever a limit was set.Fix
Add
preserve_order: booltoParquetOpener:truewhenoutput_orderingis non-empty (inParquetSource::create_opener)prune_by_limit:if let (Some(limit), false) = (limit, preserve_order)Example
Parquet sorted by
[a ASC], 3 rows per RG:a=[1,2,3]— partially matched fora > 2a=[4,5,6]— fully matchedQuery:
WHERE a > 2 ORDER BY a ASC LIMIT 1a=4from RG1a=3from RG0Test
test_preserve_order_prevents_limit_pruning— creates a real parquet with 2 RGs, verifies:preserve_order=false: prune_by_limit skips RG0, returnsa=4preserve_order=true: RG0 is kept, returnsa=3