Repository navigation
ORDER BY in subqueries: keep the sorted order above the Sort; no compaction after ORDER BY - #152
Merged
Merged
Conversation
…action after ORDER BY A filter over an `ORDER BY … LIMIT` subquery failed to verify: - The Sort's hint dropped __row_id__, and DataFusion drops a sort no operator above depends on, so LIMIT and expression hints above the Sort saw rows in another order than the committed columns. __row_id__ is now the output position, numbered by a window that requires the sort order (not committed). - Execution reran DataFusion's default optimizer, whose PushDownFilter (DataFusion 46) moves a filter below a Sort with a fetch: the claimed result kept the filtered rows' top k instead of filtering the top k. It now runs without that rule; the proof planner has placed the filters. - LIMIT asserted (debug builds) that its payload already had an activator, which is false below a subquery alias. The paper (PIOP 20) forbids Compaction after an ORDER BY, since it proves only that it keeps its input's rows: the planner never places one above a Sort, and replaying such a hint is rejected. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Member
|
wait I don't understand the pushdownfilter bug; if it's a bug in DF then we should upgrade to a version which fixes it. Seems like a serious issue. |
Collaborator
Author
It's a real DataFusion bug, and it's fixed upstream, but only in 54.0.0 (apache/datafusion#21057, March 2026). We're on 46. We can backport the upstream fix here, then upgrade to the new version in another PR. |
…on#21057) DataFusion 46's PushDownFilter moves a filter below a Sort with a fetch (a top-k). FetchSafePushDownFilter keeps a filter above any node with a fetch or skip, as DataFusion does from 54.0.0, and replaces the stock rule both in the proof planner and in execution (which dropped the rule until now). The proof planner no longer relies on NormalizeSortFetch running every pass to avoid it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
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.
Implements the paper's new rule (Pratyush/dbsnark-paper e048932, PIOP 20): no Compaction downstream of an ORDER BY. Compaction proves only that its output has its input's rows, not their order. While testing the one shape where this can arise, a filter over an
ORDER BY … LIMITsubquery, I found that such queries failed to verify at all, for two separate reasons.Compaction after ORDER BY
The planner no longer collects a compaction hint for a node with a Sort below it, and replaying such a hint is rejected (
follows_order_byinensure_compaction_target). This is the narrow form of what #94 attempted. #94 banned compaction in any plan with a Sort or Limit; this only forbids it above a Sort, which costs nothing on TPC-H. The #94 closing comment noted that the safety argument rested only on planner behaviour; now the verifier enforces it.Bug 1: the Sort's hint lost its row order (completeness)
sort_dfsorted the hint and then dropped__row_id__. DataFusion only keeps an ordering where an operator requires it, and it drops a sort that nothing above depends on. LIMIT's hint and the expression hints above it (here the filter predicate) therefore saw the rows in a different order from the committed columns, with the padding first. The comparison's predicate witness came out allfalse, and the Sign check failed.Fix: the Sort hint's
__row_id__is now the output position, numbered byrow_number() OVER (ORDER BY <the sort's keys>). A window requires that order, so DataFusion keeps it. The column is not committed. Nodes above already order their hints by__row_id__.Bug 2: execution moved the filter below the top-k (completeness; a DataFusion 46 bug)
execute()ranDataFrame::collect, which re-runs DataFusion's default optimizer. In DataFusion 46,PushDownFilterpushes a filter below anySort, even one with afetch(a top-k), which changes the result. ForSELECT n_name FROM (SELECT * FROM nation ORDER BY n_nationkey LIMIT 20) s WHERE n_regionkey = 1, the claimed result had 5 rows, including UNITED STATES (key 24). The proof had the correct 4, so the verifier rejected the proof.Fix:
FetchSafePushDownFilterbackports the upstream guard from apache/datafusion#21057 (released in 54.0.0): a filter stays above any node with a fetch or skip. It replaces the stockPushDownFilterboth in the proof planner and in execution's default pipeline. Before this, the proof planner avoided the bug only becauseNormalizeSortFetchturnsSort(fetch)back intoLimit(Sort)in every pass. Two unit tests show stock DataFusion 46 pushing the filter below a top-k and the wrapper keeping it above. The DataFusion upgrade itself is #153.Also
LIMIT had a
debug_assert!that its payload already contained an activator. That is false for a LIMIT inside a subquery, so debug builds panicked. Removed: the code after it already handles an empty payload.Tests
ordered_limit_in_subquery,filter_over_ordered_limit,limit_over_filter_over_ordered_limit,ordered_filter_over_ordered_limit. The three with a filter fail on main.Sort/Limitis rejected.cargo test --release -p tt-exec --features test-utils: all 98 pass, including 17 TPC-H and 6 Poneglyph queries. Workspace unit tests, fmt and clippy are clean.🤖 Generated with Claude Code