entity-0-0-2 opened a new pull request, #26146:
URL: https://github.com/apache/datafusion/pull/26146

   ## Pain
   
   An equi-join or range join whose key is a nested type containing float 
leaves can silently return fewer rows than it should. No error is raised: the 
query plans, runs, and hands back a table that is missing matched pairs. With 
`prefer_hash_join = false` (the sort-merge path):
   
   ```sql
   CREATE TABLE fa AS SELECT * FROM (VALUES
     (1, make_array(arrow_cast(-0.0, 'Float64'), 1.0)),
     (2, make_array(0.0, 0.0)),
     (3, make_array(0.0, 1.0))) t(id, k);
   CREATE TABLE fb AS SELECT * FROM (VALUES
     (10, make_array(0.0, 1.0)),
     (11, make_array(0.0, 0.0))) t(id, k);
   SET datafusion.optimizer.prefer_hash_join = false;
   SELECT fa.id, fb.id FROM fa JOIN fb ON fa.k = fb.k;
   ```
   
   DataFusion returns rows `(1,10)` and `(3,10)`; the match `(2,11)` is 
dropped. Hash join returns the correct three rows.
   
   ## Root cause
   
   `JoinKeyComparator::build` 
(datafusion/physical-plan/src/joins/utils.rs:2568) normalizes `-0.0` to `+0.0` 
on both key arrays before `make_comparator`, so merge-join key comparison 
honors SQL equality. The sort that feeds the merge orders keys with Arrow's 
IEEE total order, where `-0.0 < +0.0`. Since #24827 (d42cd854d) 
`normalize_float_zero` recurses into nested children, so the two orders diverge 
exactly when a key type is nested with float leaves: sorted input ranks `[-0.0, 
1.0] < [0.0, 0.0]` while the normalized comparator ranks it `>`, and the merge 
stream skips valid pairs. Flat float keys are unaffected because nothing sorts 
between the two zeros.
   
   ## The fix
   
   The planner routes joins with such keys away from the sort-based execs:
   
   - datafusion/core/src/physical_planner.rs: equi-joins whose keys are nested 
with float leaves now plan `HashJoinExec` instead of `SortMergeJoinExec`, and 
single range predicates on such keys plan `NestedLoopJoinExec` instead of 
`PiecewiseMergeJoinExec` (same fallback the planner already uses when a range 
predicate has no clean side split).
   - Key types where both orders provably agree, including flat floats, keep 
the sort-based fast paths.
   
   ## Test
   
   Added the issue query to 
`datafusion/sqllogictest/test_files/negative_zero.slt` asserting the correct 
three rows.
   
   Before, on main (`a23b89ff9`):
   
   ```
   query result mismatch:
   [SQL] SELECT fa.id, fb.id FROM fa JOIN fb ON fa.k = fb.k ORDER BY fa.id, 
fb.id;
   [Diff] (-expected|+actual)
       1 10
   -   2 11
       3 10
   ```
   
   After: `cargo test -p datafusion-sqllogictest --test sqllogictests -- 
negative_zero` passes, as do the `sort_merge_join*` and `piecewise_merge_join*` 
suites and `cargo test -p datafusion --lib physical_planner` (56 passed).
   
   ## How this was found
   
   Vestiges Causal Walk (https://github.com/samvallad33/vestige). There was no 
error to search for, only wrong bytes and a walk: the symptom record (the 
missing `2 11` row) was bridged over recorded causal edges to the introducing 
commit `d42cd854d`.
   
   Fixes #26115


-- 
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]

Reply via email to