)]}'
{
  "commit": "0a429a37dbdf735c4d91efbce68a0fdcb271ea46",
  "tree": "bb488978d8700094a160c99a2a9cac44f4ece6d5",
  "parents": [
    "e76f1af0932a81d82d6645d43d027eed417d2f03"
  ],
  "author": {
    "name": "Liang-Chi Hsieh",
    "email": "viirya@gmail.com",
    "time": "Thu Aug 13 21:59:48 2026 +0000"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Thu Aug 13 21:59:48 2026 +0000"
  },
  "message": "fix: emit unmatched NULL-key right rows in RIGHT/FULL PiecewiseMergeJoin (#24336)\n\n## Which issue does this PR close?\n\n- Closes #24335.\n\n## Rationale for this change\n\nA `RIGHT`/`FULL` `PiecewiseMergeJoin` with a range predicate drops an\nunmatched right-side row whose join key is `NULL`. A `NULL` key never\nmatches (`NULL \u003c x` is UNKNOWN), so in a `RIGHT`/`FULL` join the row is\nunmatched and must still be emitted with NULLs on the left — but\n`PiecewiseMergeJoinExec` omits it, diverging from `NestedLoopJoin`.\n\n```sql\ncreate table l(v int) as values (5);\ncreate table r(v int) as values (10), (NULL);\nselect l.v, r.v from l right join r on l.v \u003c r.v;   -- drops (NULL, NULL)\n```\n\nRoot cause: `resolve_classic_join` starts the match scan past the\nstreamed side\u0027s `NULL`-keyed rows (they sort to the front under\n`nulls_first`). Those rows are never revisited, so for `Right`/`Full`\nthey were never added to `unmatched_indices` and got dropped.\n\n## What changes are included in this PR?\n\n- In `resolve_classic_join`, when skipping the streamed side\u0027s leading\n`NULL`-key rows, record them as unmatched for `Right`/`Full` joins so\nthey are emitted (with NULLs on the buffered side).\n\n## Are these changes tested?\n\nYes.\n\n- Regression test in `pwmj.slt`: a `RIGHT JOIN` over the existing\n`null_join_*` tables now emits the `(NULL, NULL)` row. The test fails on\n`main` (the row is dropped) and passes with this change.\n- Verified more broadly with a differential fuzz against\n`NestedLoopJoin` (same SQL, `enable_piecewise_merge_join` on vs off):\n1200 checks over random `RIGHT JOIN` inputs with `\u003c`/`\u003c\u003d`/`\u003e`/`\u003e\u003d` and\nhigh right-side NULL density, 0 mismatches.\n\n## Are there any user-facing changes?\n\n`RIGHT`/`FULL` range joins via `PiecewiseMergeJoin` (behind\n`enable_piecewise_merge_join`, default off) now return unmatched right\nrows with `NULL` keys, matching `NestedLoopJoin`. No API changes.",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "50ef78f18bf659d5f616e2f31d9d9e2e9104fe48",
      "old_mode": 33188,
      "old_path": "datafusion/physical-plan/src/joins/piecewise_merge_join/classic_join.rs",
      "new_id": "1c89927087fdb44bfcd6b8a711d718ffcd0dfa03",
      "new_mode": 33188,
      "new_path": "datafusion/physical-plan/src/joins/piecewise_merge_join/classic_join.rs"
    },
    {
      "type": "modify",
      "old_id": "9789c0e4e5392d415e1751b2e27316510f8a40fa",
      "old_mode": 33188,
      "old_path": "datafusion/sqllogictest/test_files/pwmj.slt",
      "new_id": "25c275f4b7e93f2ec1c8353a32d348be82016df2",
      "new_mode": 33188,
      "new_path": "datafusion/sqllogictest/test_files/pwmj.slt"
    }
  ]
}
