Repository navigation
fix: preserve nested projection expressions - #24686
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #24686 +/- ##
==========================================
- Coverage 81.58% 81.58% -0.01%
==========================================
Files 1123 1123
Lines 406743 406778 +35
Branches 406743 406778 +35
==========================================
+ Hits 331840 331862 +22
+ Misses 55464 55462 -2
- Partials 19439 19454 +15 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
abd01b2 to
dfb44a1
Compare
kosiew
left a comment
There was a problem hiding this comment.
Thanks for working on this. I dug into the nested projection behavior and tested the change across different nesting depths and optimizer pass counts.
I think the root cause is slightly different from what this PR assumes. Removing the iterative merge improves the specific depth-3 case in the regression test, but deeper chains can still return wrong results. The issue appears to be the structural-equality fast path in merge_consecutive_projections, which can elide a projection that is structurally equal but not idempotent.
I have a few blocking comments below. The main suggestion is to keep the iterative merge and guard the equality fast path so it only applies to projections that are safe to elide. I tested that combination locally and it preserves the expected results across the depths and optimizer pass counts I checked.
Signed-off-by: discord9 <[email protected]>
dfb44a1 to
27b6eb0
Compare
Signed-off-by: discord9 <[email protected]>
kosiew
left a comment
There was a problem hiding this comment.
Thanks for working on this. I took another look at the latest version and re-verified the behavior against the current head. I think there are still a few issues we need to address before merging.
The main concern is that removing the iterative merge avoids the reported failure for the shallow case, but it does not address the underlying structural-equality fast path. That path can still remove a projection that needs to be evaluated. Removing the loop also introduces a projection-collapse regression for deeper chains.
I left the specific changes I think are needed inline. In short, I suggest keeping the iterative merge, guarding the equality fast path so it only elides projections that are safe to elide, and extending the regression coverage to deeper chains and the default optimizer pass count.
Thanks again for digging into this.
Thanks for the review, I think fa440be should address most of the review comment? basically just remove fast path and add some tests now
|
Signed-off-by: discord9 <[email protected]>
kosiew
left a comment
There was a problem hiding this comment.
Thanks for the updates. I took another look and the issues from my previous review have been addressed.
The iterative projection merge is restored, and removing the structurally-equal fast path fixes the non-idempotent projection case without giving up the one-pass collapse behavior. The new focused tests also cover the non-idempotent case, deep projection chains, and alias metadata preservation.
The expanded SQL regression now exercises six nested projections with both max_passes = 1 and the default optimizer configuration, and the configuration is reset afterward. The tighter optimizer integration plan expectation has also been restored.
I don't have any remaining findings. Looks good to me. Thanks for working through the feedback!
|
🚀 |
## Which issue does this PR close? - No issue has been filed. ## Rationale for this change Anonymous nested projections that reuse the same output name can return wrong results. For example, each `i + 1 AS i` layer must be evaluated independently, but the projection optimizer could drop one layer when two consecutive projection expression vectors were structurally equal. Structural equality does not imply that a projection is safe to elide: repeated computations such as `i + 1 AS i` have the same expression shape but must still be evaluated twice. ## What changes are included in this PR? - Remove the structural-equality fast path that directly elided one of two consecutive projections. - Keep the existing iterative whole-chain merge, so deep projection chains still collapse within one optimizer rule invocation. - Continue using the normal projection rewrite path, which composes repeated expressions and preserves aliases and field metadata. - Add focused regressions for repeated non-idempotent projections, one-pass collapse of a 12-level chain, and metadata-bearing aliases. - Add an execution-level SQLLogicTest with six anonymous `i + 1 AS i` layers under both `max_passes = 1` and the default optimizer configuration. ## What is the testing strategy for this PR? The focused unit tests verify that: - two structurally equal `i + 1 AS i` projections retain both additions; - a 12-level chain preserves all 12 additions and collapses to one `Projection` with `max_passes = 1`; - a metadata-bearing `Alias(Column)` is merged without losing field metadata. The SQLLogicTest executes a six-level anonymous projection chain against a temporary table with both one optimizer pass and the default pass count. Verified with: ```text cargo fmt --all --check cargo test -p datafusion-optimizer optimize_projections # 58 passed; 0 failed cargo test -p datafusion-optimizer --test optimizer_integration # 26 passed; 0 failed cargo test --profile ci -p datafusion-sqllogictest --test sqllogictests -- projection.slt # 1/1 files completed; 0 failures cargo clippy -p datafusion-optimizer --all-targets --all-features -- -D warnings # passed git diff --check # passed ``` ## Are there any user-facing changes? Yes. Deep anonymous nested projections that reuse an output name now preserve every projection expression and return the correct result. There are no public API or configuration changes. --------- Signed-off-by: discord9 <[email protected]> Signed-off-by: discord9 <[email protected]> (cherry picked from commit 7a1f468)
…ion_chain_in_one_pass The backport of apache#24686 (cbc04dd) added `merge_deep_projection_chain_in_one_pass`, which optimizes a chain of twelve `i + 1 AS i` projections with `max_passes = 1` and asserts that 1. all twelve `Int32(1)` additions survive, which is what apache#24686 fixes, and 2. the chain collapses into a single `Projection`. The second assertion checks the whole-chain merge from upstream apache#22389 (0da8961), which this branch does not have; apache#24686 keeps that merge rather than adding it. On this branch the test fails on the second assertion only (`left: 1`, `right: 6`), after the first one has passed. The first assertion is kept. With the apache#24686 change to `merge_consecutive_projections_one_level` reverted, it fails with `left: 12`, `right: 7`, so the test still fails without the fix and passes with it.
Which issue does this PR close?
Rationale for this change
Anonymous nested projections that reuse the same output name can return wrong results. For example, each
i + 1 AS ilayer must be evaluated independently, but the projection optimizer could drop one layer when two consecutive projection expression vectors were structurally equal.Structural equality does not imply that a projection is safe to elide: repeated computations such as
i + 1 AS ihave the same expression shape but must still be evaluated twice.What changes are included in this PR?
i + 1 AS ilayers under bothmax_passes = 1and the default optimizer configuration.What is the testing strategy for this PR?
The focused unit tests verify that:
i + 1 AS iprojections retain both additions;Projectionwithmax_passes = 1;Alias(Column)is merged without losing field metadata.The SQLLogicTest executes a six-level anonymous projection chain against a temporary table with both one optimizer pass and the default pass count.
Verified with:
Are there any user-facing changes?
Yes. Deep anonymous nested projections that reuse an output name now preserve every projection expression and return the correct result. There are no public API or configuration changes.