)]}'
{
  "commit": "837f80823a33f42a646ede95c7e20d890837fff6",
  "tree": "9e5fb9f8c858f6c06fb04088a915b9f1b954c312",
  "parents": [
    "d32715185e961bae45460c2ec7a7bba892592ae0"
  ],
  "author": {
    "name": "Jianghua.yjh",
    "email": "yjhjstz@gmail.com",
    "time": "Tue Jul 21 16:39:29 2026 +0800"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Tue Jul 21 16:39:29 2026 +0800"
  },
  "message": "ORCA: don\u0027t push LOJ ON-pred onto its own outer in PushThruOuterChild (#1836)\n\n* ORCA: don\u0027t emit NIJ ON-pred edges into the Select above DPv2 join trees\n\nWhen two non-inner joins in an NAry join have structurally identical ON\npredicates, ORCA could silently drop or misplace predicates, producing\nwrong results:\n\n  select x.c1, y2.c1 from x left join y1 on x.c1\n                            left join y2 on x.c1\n   where y2.c1 is null;\n\nreturned 0 rows instead of the two null-padded FALSE rows, because a\ncopy of the ON pred ended up as a scan filter on x.\n\nRoot cause: CJoinOrderDPv2\u0027s m_expression_to_edge_map is keyed on\nstructural equality (CExpression::HashValue / CUtils::Equals). With two\nstructurally identical ON preds, RecursivelyMarkEdgesAsUsed can only\never mark one of the duplicate edges as used, so\nAddSelectNodeForRemainingEdges treated the other edge as a leftover\nWHERE predicate and emitted it into a Select on top of the join tree.\nThe normalizer then legitimately pushed that Select onto the LOJ\u0027s own\nouter child, filtering out rows that outer-join semantics require to be\nnull-padded. (The map is only populated when a WHERE predicate\nreferences an NIJ right child, which is why the WHERE clause is needed\nto trigger the bug.)\n\nFix at the source: skip ON-pred edges (m_loj_num \u003e 0) when collecting\nremaining edges. An NIJ\u0027s ON predicate is always applied by the join\nitself when its right child is placed (IsRightChildOfNIJ), so an\n\"unused\" ON-pred edge can only be a bookkeeping artifact of the\nstructural-equality map and must never be duplicated above the join.\n\nAn earlier attempt fixed this downstream, by stripping conjuncts that\nstructurally match the LOJ\u0027s ON pred in CNormalizer::PushThruOuterChild.\nThat layer cannot distinguish the leaked ON-pred copy from legitimate,\nstructurally identical conjuncts arriving from above, and silently\ndeleted user predicates:\n\n  select * from x left join y on x.c1 where x.c1;       -- 3 rows, not 1\n  select 1 from a t1\n    left join (a t2 left join a t3 on t2.id \u003d 1)\n    on t2.id \u003d 1;                                        -- lost the\n                                                         -- Index Cond on\n                                                         -- t2 and the ON\n                                                         -- pred entirely\n\nWith this fix, the original repro returns the correct 2 rows with no\nscan filter on x, the queries above return planner-identical results,\nand the nested-LOJ query regains Index Cond: (id \u003d 1) on t2.\n\nAdd the repro as a regression test in bfv_joins.",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "3151568025669b9289b7b144bb973d352a53dc8d",
      "old_mode": 33188,
      "old_path": "contrib/pax_storage/src/test/regress/expected/bfv_joins.out",
      "new_id": "e82e9e158c7c198dac56f127b93955ceb4b77a10",
      "new_mode": 33188,
      "new_path": "contrib/pax_storage/src/test/regress/expected/bfv_joins.out"
    },
    {
      "type": "modify",
      "old_id": "af48a5dd8d923f80dfd7f2d8c094722ccf3703cd",
      "old_mode": 33188,
      "old_path": "contrib/pax_storage/src/test/regress/expected/bfv_joins_optimizer.out",
      "new_id": "6426bf4f8fb67c6f88a5e081cbd6e3553ee949c7",
      "new_mode": 33188,
      "new_path": "contrib/pax_storage/src/test/regress/expected/bfv_joins_optimizer.out"
    },
    {
      "type": "modify",
      "old_id": "edc39f58a7dcedf8118ca7901668f6ef0cfb68c6",
      "old_mode": 33188,
      "old_path": "contrib/pax_storage/src/test/regress/sql/bfv_joins.sql",
      "new_id": "cb4acd0a9c6e8dd261373e52a1a6b4b3fa2af30f",
      "new_mode": 33188,
      "new_path": "contrib/pax_storage/src/test/regress/sql/bfv_joins.sql"
    },
    {
      "type": "modify",
      "old_id": "c6c5a12500bf183c8499c788b64594ce3bb56731",
      "old_mode": 33188,
      "old_path": "src/backend/gporca/libgpopt/src/xforms/CJoinOrderDPv2.cpp",
      "new_id": "3f9445abaf3a28db2eef3414aa967705641439b9",
      "new_mode": 33188,
      "new_path": "src/backend/gporca/libgpopt/src/xforms/CJoinOrderDPv2.cpp"
    },
    {
      "type": "modify",
      "old_id": "da6e74813182eff91e413837285eec45fb25086f",
      "old_mode": 33188,
      "old_path": "src/test/regress/expected/bfv_joins.out",
      "new_id": "ff7947488c838bcd344bd00ffe8cdf452eceb920",
      "new_mode": 33188,
      "new_path": "src/test/regress/expected/bfv_joins.out"
    },
    {
      "type": "modify",
      "old_id": "934b682492b70d6c4e5874b5d7cb3eb500855e2b",
      "old_mode": 33188,
      "old_path": "src/test/regress/expected/bfv_joins_optimizer.out",
      "new_id": "d8cb7b7a425d0fb5618cc99534f2bfd857f6736b",
      "new_mode": 33188,
      "new_path": "src/test/regress/expected/bfv_joins_optimizer.out"
    },
    {
      "type": "modify",
      "old_id": "3a0fca09fc79bb4c33dcc0ad918c388cf824905e",
      "old_mode": 33188,
      "old_path": "src/test/regress/sql/bfv_joins.sql",
      "new_id": "1dca58051c8a7ec1c5d91dad847dcb77024b1ac1",
      "new_mode": 33188,
      "new_path": "src/test/regress/sql/bfv_joins.sql"
    }
  ]
}
