shinzoxD opened a new pull request, #24402:
URL: https://github.com/apache/datafusion/pull/24402
## Which issue does this PR close?
- Closes #11570
## Rationale for this change
A common TPC-DS pattern protects against divide-by-zero with:
```sql
CASE WHEN y > 0 THEN x / y ELSE NULL END
```
The general `CaseExpr` path (filter + scatter) is expensive for this. #19994
tried replacing it with a fully vectorized always-divide (`eq` / `zip` / `div`
/ `nullif`, dummy-dividing zeros by 1). That wins when few denominators are
zero, but loses when many are.
After #20097 (`ExpressionOrExpression` for `CASE WHEN x THEN y [ELSE NULL]`)
and #20498 (type-specific scatter), the general CASE path is in better shape.
This PR specializes the remaining divide-by-zero protection pattern without the
#19994 always-divide tradeoff.
## What changes are included in this PR?
New `EvalMethod::DivideByZeroProtection` that detects:
```sql
CASE WHEN y {>, !=, <} 0 THEN x / y [ELSE NULL]
```
including swapped comparisons (`0 < y`, `0 != y`, `0 > y`) and `Cast` /
`TryCast` wrappers around the checked operand / divisor.
Evaluation keeps the original WHEN predicate so `>`, `!=`, and `<` stay
correct, then:
1. Evaluates WHEN (NULL WHEN treated as false ? do not divide)
2. Nulls the divisor on excluded rows (`nullif`)
3. Divides ? skipped rows become NULL without a dummy divide-by-one
4. Fast paths for all-true (plain divide) and all-false / no-true (scalar
NULL)
Only applied when:
- Single WHEN / THEN and implicit or explicit `ELSE NULL`
- The checked operand is the same as the divisor (including through `Cast` /
`TryCast`)
- Both divide operands are cheap (column, literal, or cast of those) so
evaluating them on the full batch cannot introduce errors on skipped rows
Unlike #19994, this does not dummy-divide by 1 and does not collapse `>` /
`<` / `!=` into a single "is zero" mask.
## Are these changes tested?
Yes.
Unit tests in `datafusion/physical-expr/src/expressions/case.rs`:
- `test_divide_by_zero_protection_specialization` ? pattern detection +
results with `Cast`
- `test_divide_by_zero_protection_predicates` ? `!=`, `>`, `<`, and swapped
`0 != d`
- `test_divide_by_zero_protection_all_true_all_false_and_nulls`
- `test_divide_by_zero_protection_specialization_not_applied` ? WHEN `a` /
divisor `b` must not specialize
- `test_divide_by_zero_protection_not_applied_with_else` ? `ELSE 0` must not
specialize
SLTs in `datafusion/sqllogictest/test_files/case.slt` (existing `>`, `!=`,
`<` cases from the #19994 review, plus):
- Swapped form `WHEN 0 < d`
- Float divide-by-zero protection
- Implicit ELSE (no ELSE clause)
- NULL divisor treated as not matching WHEN
Bench comment in `datafusion/physical-expr/benches/case_when.rs` updated to
match the specialization trigger (`!= 0`).
## Are there any user-facing changes?
No. Internal optimization; results are unchanged.
## Testing
- [x] Existing `case.slt` `>`, `!=`, `<` divide-by-zero rows still correct
- [x] New SLTs for swapped comparison, float, implicit ELSE, and NULL divisor
- [x] Unit tests for specialization, predicates, all-true / all-false /
nulls, and negative cases
- [ ] `cargo fmt --all` / `cargo clippy --all-targets --all-features -- -D
warnings` (CI)
--
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]