Commit 919f080
committed
[SPARK-59434][SQL] Non-deterministic predicates should not be pushed down to CTE definitions
### What changes were proposed in this pull request?
`PushdownPredicatesAndPruneColumnsForCTEDef` collects the predicates at each CTE reference and pushes their OR-merged combination into the shared CTE definition. Each reference keeps its own predicates, so this only works for deterministic predicates -- a non-deterministic one is then evaluated a second time in the definition.
This PR filters the collected predicates to deterministic ones before push-down. When that leaves a reference with nothing pushable, the combined predicate becomes `TRUE` and the definition gets no push-down at all, including for its other references; the scaladoc is updated to say so.
### Why are the changes needed?
A non-deterministic predicate evaluated a second time in the CTE definition can drop rows, which is a wrong-results bug:
```sql
create or replace temp view t as select * from values (0), (1), (2) as t(c1);
with v as (select c1, rand(1) r from t)
select c1 from v where rand(2) < 0.5
union all
select c1 from v where rand(3) < 0.5;
```
The definition is non-deterministic and referenced twice, so it survives `InlineCTE`. `(rand(2) < 0.5) OR (rand(3) < 0.5)` is pushed into the definition while both references keep their own filter, so the optimized plan holds four `rand` filters instead of two and rows are filtered twice.
The rule has had this behavior since it was added in SPARK-37670 (3.2.2). Its scaladoc claimed determinism was taken care of by `ScanOperation`, but that was never true -- the filter-collection guard admits the first filter whatever it is:
```scala
filters.isEmpty || (filters.forall(_.deterministic) && condition.deterministic)
```
`PhysicalOperation`, which SPARK-39764 (3.4.0) later swapped in, behaves the same way here -- it returns a single filter even when it is non-deterministic, since its `filters.length > 1` assert only binds when more than one filter was collected. The scaladoc is corrected along with the fix.
### Does this PR introduce _any_ user-facing change?
Yes, it fixes wrong results for the query shape above. Affects 3.2.2 and later.
### How was this patch tested?
Four new tests in `CTEInlineSuite`, all failing without the fix:
- non-deterministic predicates stay at the references, two `rand` filters instead of four;
- deterministic conjuncts are still pushed while the non-deterministic one stays up;
- a reference with only non-deterministic predicates blocks push-down for its deterministic sibling, which must not be pushed alone;
- a row-count assertion using `monotonically_increasing_id()`, 10 rows instead of 8.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Opus 5
Closes #58735 from pan3793/cte-nondeterministic-pushdown.
Authored-by: Cheng Pan <pan3793@gmail.com>
Signed-off-by: Cheng Pan <chengpan@apache.org>1 parent ddfcc17 commit 919f080
2 files changed
Lines changed: 116 additions & 6 deletions
File tree
- sql
- catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer
- core/src/test/scala/org/apache/spark/sql
Lines changed: 11 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
64 | 66 | | |
65 | 67 | | |
66 | 68 | | |
| |||
77 | 79 | | |
78 | 80 | | |
79 | 81 | | |
80 | | - | |
81 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
82 | 87 | | |
83 | 88 | | |
84 | 89 | | |
| |||
Lines changed: 105 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
451 | 451 | | |
452 | 452 | | |
453 | 453 | | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
454 | 559 | | |
455 | 560 | | |
456 | 561 | | |
| |||
0 commit comments