Weijun-H opened a new pull request, #27281: URL: https://github.com/apache/datafusion/pull/27281
## Which issue does this PR close? - Closes #27280. ## Rationale for this change In `CASE WHEN f(x) = 'a' THEN 'b' ELSE f(x) END`, `f(x)` runs for all rows in the first WHEN and again for the ELSE rows. Logical CSE can remove it (for example for an immutable UDF) only for plans that pass through the logical optimizer, and does so by moving `f(x)` into a lower projection. This API lets a producer that builds physical plans directly reuse the result while still evaluating `f(x)` at its original position in the first WHEN. ## What changes are included in this PR? - `RequiredOperandReusePolicy`: the caller says which child an expression evaluates first (`None` when unknown) and whether a rewritten call is still the authorized one. - `CaseExpr::with_required_operand_reuse(&operand, policy)`: takes effect only for `CASE WHEN … ELSE operand` without a base expression, when the policy leads from the first WHEN to a node equal to ELSE. Otherwise the CASE is unchanged. - The operand is evaluated once at its first-WHEN position and reused for ELSE. All existing CASE fast paths still apply. The operand must produce an array. - `with_new_children` re-checks the permission against the previous ELSE and drops it if either use changes to a different call or different arguments. - The permission is part of `Eq`/`Hash`; `try_to_proto` returns `Ok(None)` for a CASE carrying one. ## What is the testing strategy for this PR? The API is only reachable from physical plans, so the tests are Rust unit tests rather than `sqllogictest`: - `expressions::case::required_operand_tests` (5 tests, counting stand-in expression): single evaluation and correct values across all-true/all-false/null/mixed batches; skipped THEN on uniform predicates; permission kept across column rewrites and revoked when the call or its arguments change; nested CASE and ineligible operand positions; `Eq`/`Hash`. - `projection::case_operand_reuse_tests`: on one batch where an earlier column and the operand fail on different rows, the plain and the reusing CASE surface the same error, while a lower projection surfaces the other one. This documents the producer's requirement, not a general DataFusion error-order guarantee. Local checks: on `c8265b68` (this branch's base), the two test modules above, `cargo clippy -p datafusion-physical-expr -p datafusion-physical-plan --all-targets --all-features -- -D warnings` and `cargo fmt --all -- --check`. On the earlier base `14b3edd9` (same patch): workspace `cargo clippy --all-targets --all-features -- -D warnings`, the AGENTS.md extended `cargo test --profile ci … --workspace` command, and `./dev/rust_lint.sh`. Benchmark: the existing `case_when` benchmark does not opt in, so it only shows the patch's cost on unchanged CASE paths. For its 9 single-condition cases (8192 rows × 3/50/100 columns), 3 alternating rounds of `14b3edd9` versus the same base plus this patch gave per-case median changes of −1.05% to +1.64%. Cases that moved in the same direction in all three rounds: slower — `8192x3 … THEN 1 ELSE 0` (+0.40%, +1.65%, +1.64%) and `8192x3 … THEN c2 ELSE c3` (+0.53%, +0.01%, +0.79%); faster — `8192x50 … THEN c2 ELSE c3` (−0.71%, −1.05%, −1.88%) and `8192x100 … THEN c2 ELSE c3` (−0.11%, −0.81%, −0.59%). These are observed values from one short run, not a significance test. No benchmark of the opt-in path is included. ## Are there any user-facing changes? New public API: `RequiredOperandReusePolicy` and `CaseExpr::with_required_operand_reuse`. No behaviour change unless a caller opts in. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
