Commit 8941106
committed
[SPARK-56660][SQL][FOLLOWUP] Preserve NULL semantics; gate behind SQLConf; rework tests
Addresses review feedback on PR #56244:
1. Correctness fix for NULL handling. The original decomposition rewrote
EqualTo(struct, struct) into a plain conjunction of per-field EqualTo
comparisons, which silently changed semantics for non-null structs that
contained NULL fields:
- Before this PR: struct(1, null) = struct(1, null) returned TRUE
(Spark's whole-struct EqualTo evaluates ordering.equiv on the row,
which treats per-field NULL == NULL as equal).
- With original PR #56244: returned NULL.
The fix wraps the conjunction with a null-check that mirrors the
original outer null behavior:
- EqualTo(L, R) over nullable structs: IF (L IS NULL OR R IS NULL)
THEN NULL ELSE And(EqualNullSafe(L.fi, R.fi)).
- EqualNullSafe(L, R): IF (L IS NULL AND R IS NULL) THEN TRUE
ELSE IF (L IS NULL OR R IS NULL) THEN FALSE
ELSE And(EqualNullSafe(L.fi, R.fi)).
The wrappers fold out cleanly when either operand is non-nullable,
leaving the simple conjunction in the common
`CreateNamedStruct = column` pushdown case.
2. SQLConf gate. Add `spark.sql.optimizer.decomposeStructComparison.enabled`
(default false) so users opt in once the behavior has soaked. Add
`spark.sql.optimizer.decomposeStructComparison.maxFields` (default 1000)
that bounds total decomposed predicates including recursively nested
struct fields, replacing the unprincipled per-level field cap of 100.
3. Scaladoc explaining Filter scope. Document why join conditions and
aggregate grouping keys are deliberately not rewritten.
4. Tests reworked as oracle tests. The original suite asserted post-rewrite
NULL behavior directly, which codified the regression as expected. The
rewritten suite uses two patterns:
- Catalyst-level: build expressions and assert eval result of original
expression equals eval result of rewritten expression on representative
inputs (struct(1, null), whole-struct null, Not wrapper, etc.).
- End-to-end: run each query with the rule enabled and with the conf
disabled; assert row sets are identical.
Added tests for: Not(struct = struct) with NULL fields, whole-struct null
on one side, conf gating. Removed: 3 wrong-oracle NULL tests, structural-
only "nullable fields decomposes" test, duplicate LessThan, duplicate
3-level nested, single-field, duplicate join test in catalyst suite.1 parent 707a859 commit 8941106
4 files changed
Lines changed: 451 additions & 264 deletions
File tree
- sql
- catalyst/src
- main/scala/org/apache/spark/sql
- catalyst/optimizer
- internal
- test/scala/org/apache/spark/sql/catalyst/optimizer
- core/src/test/scala/org/apache/spark/sql
Lines changed: 123 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
565 | 565 | | |
566 | 566 | | |
567 | 567 | | |
568 | | - | |
569 | | - | |
570 | | - | |
571 | | - | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
572 | 593 | | |
573 | 594 | | |
574 | | - | |
575 | | - | |
576 | | - | |
577 | | - | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
578 | 603 | | |
579 | 604 | | |
580 | 605 | | |
581 | 606 | | |
582 | 607 | | |
583 | | - | |
| 608 | + | |
584 | 609 | | |
585 | | - | |
| 610 | + | |
586 | 611 | | |
587 | 612 | | |
588 | 613 | | |
589 | 614 | | |
590 | 615 | | |
591 | | - | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
592 | 622 | | |
593 | 623 | | |
594 | 624 | | |
595 | 625 | | |
596 | 626 | | |
597 | | - | |
598 | | - | |
599 | | - | |
600 | | - | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
601 | 650 | | |
602 | 651 | | |
603 | | - | |
604 | | - | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
605 | 711 | | |
606 | 712 | | |
607 | 713 | | |
| |||
Lines changed: 28 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
522 | 522 | | |
523 | 523 | | |
524 | 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 | + | |
525 | 549 | | |
526 | 550 | | |
527 | 551 | | |
| |||
8581 | 8605 | | |
8582 | 8606 | | |
8583 | 8607 | | |
| 8608 | + | |
| 8609 | + | |
| 8610 | + | |
| 8611 | + | |
8584 | 8612 | | |
8585 | 8613 | | |
8586 | 8614 | | |
| |||
0 commit comments