Repository navigation
Conversation
This was referenced Oct 6, 2026
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #26085 +/- ##
==========================================
- Coverage 82.72% 82.72% -0.01%
==========================================
Files 1147 1147
Lines 448179 448163 -16
Branches 448179 448163 -16
==========================================
- Hits 370751 370724 -27
- Misses 54898 54900 +2
- Partials 22530 22539 +9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
A projection of a `MoveTowardsLeafNodes` expression directly over a node that the extraction cannot go through (for example a `TableScan` or an `Aggregate`) was split into a recovery projection over an in-place extraction projection. `OptimizeProjections` then merged the two back into the original projection. The two rules undid each other in every optimizer pass, and the loop stopped only because the plan signature repeated. Now the rule pushes the in-place extraction projection immediately. If it cannot move below the input, the rule leaves the original projection unchanged. The final plans do not change, and the source still absorbs the leaf expressions (for example `DataSourceExec projection=[get_field(...)]`). `filter_and_extraction_projection_reach_fixed_point` now checks that no rule changes the plan in the last pass, and covers a projection over a scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adriangb
force-pushed
the
leaf-churn-fix
branch
from
October 6, 2026 14:33
4461e73 to
38a7f6d
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.
Which issue does this PR close?
Rationale for this change
PushDownLeafProjectionsandOptimizeProjectionsundo each other in every optimizer pass for a common plan shape: a projection of a struct field (or anotherMoveTowardsLeafNodesexpression) directly over a node that the extraction cannot go through, such as aTableScanor anAggregate.In each pass,
push_down_leaf_projectionssplits this into a recovery projection over an in-place extraction projection:Then
optimize_projectionsmerges the two back into the original plan. The loop stops only because the plan at the end of a pass is the same as at the end of the previous pass. The final plan is correct, but each pass does the work of both rules again.The test that #25455 added found this shape. That test records which rules change the plan in each pass. On
mainit prints, for the plan above:With this PR:
Which rule yields
PushDownLeafProjectionsyields. The split has no use when the extraction projection cannot move: a plainProjection(get_field(...))directly over a Parquet scan already reachesDataSourceExec projection=[..., get_field(s@1, value) ...]. The existingEXPLAINchecks inprojection_pushdown.sltshow this and do not change. IfOptimizeProjectionsyielded instead, the plan would keep a projection node that does no work.What changes are included in this PR?
In
split_and_push_projection(extract_leaf_expressions.rs), the rule still builds the in-place extraction projection, because a mixed projection over aFiltermust become a pure extraction projection before it can go through the filter. The rule now immediately tries to push that extraction projection into its input. If it cannot move, the rule returns the original projection unchanged. The early return for a projection without new extractions stays: it also ends the recursion of that push attempt.extract_leaf_expressions.rsandoptimize_projections, and as a second entry in the "Rule Precedence" section ofdocs/source/library-user-guide/query-optimizer.md.What is the testing strategy for this PR?
filter_and_extraction_projection_reach_fixed_pointinpush_down_filter.rsnow checks that no rule at all changes the plan in the last optimizer pass (before, it checked onlyPushDownFilterandPushDownLeafProjections). It also covers a projection over a scan with no filter. Without the fix it fails with[["push_down_leaf_projections","optimize_projections"],["push_down_leaf_projections","optimize_projections"]].extract_leaf_expressions.rschange. In each one, only the "After Pushdown" stage changes, to "(same as after extraction)". The "Optimized" plan is the same in all six.unnest.sltchanges (logical plan line 03). A nested alias is now kept:get_field(__unnest_placeholder(...,depth=1) AS UNNEST(recursive_unnest_table.column3), Utf8("c1")). The split and merge used to remove that alias as a side effect. The physical plan is the same. This is the same display-only nested alias that fix: decide the precedence between PushDownFilter and PushDownLeafProjections #25455 showed forlength(...).Commands run:
cargo test --profile ci -p datafusion-optimizer --lib: 932 passed.cargo test --profile ci -p datafusion-sqllogictest --test sqllogictests: 526 of 526 files pass.cargo fmt --allandcargo clippy --profile ci -p datafusion-optimizer --all-targets -- -D warnings: clean.Are there any user-facing changes?
No. Final plans and results do not change, except for the alias display in the
unnestplan above. Planning does less work for queries that read struct fields.🤖 Generated with Claude Code