From 27cdb17454507604f87c8362c4d4e48f5d879f35 Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Wed, 30 Jun 2021 10:39:36 +0800 Subject: [PATCH] Using each rel as both outer and inner for anti joins --- src/backend/commands/explain.c | 3 +++ src/backend/executor/nodeHashjoin.c | 9 +++++++++ src/backend/optimizer/path/joinpath.c | 7 +++++++ src/backend/optimizer/path/joinrels.c | 3 +++ src/backend/optimizer/plan/setrefs.c | 1 + src/include/nodes/nodes.h | 4 +++- 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index e81b990092..a9f6ebf1f3 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1517,6 +1517,9 @@ ExplainNode(PlanState *planstate, List *ancestors, case JOIN_ANTI: jointype = "Anti"; break; + case JOIN_RIGHT_ANTI: + jointype = "Right Anti"; + break; default: jointype = "???"; break; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 510bdd39ad..6592a81de3 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -482,6 +482,14 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel) continue; } + /* + * In a right-antijoin, we never return a matched tuple. + * And we need to use current outer tuple to scan the + * bucket for matches + */ + if (node->js.jointype == JOIN_RIGHT_ANTI) + continue; + /* * If we only need to join to the first matching inner * tuple, then consider returning this one, but after that @@ -694,6 +702,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual); break; case JOIN_RIGHT: + case JOIN_RIGHT_ANTI: hjstate->hj_NullOuterTupleSlot = ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual); break; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index b67b517770..e91293ff54 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -1110,6 +1110,9 @@ sort_inner_and_outer(PlannerInfo *root, List *all_pathkeys; ListCell *l; + if (jointype == JOIN_RIGHT_ANTI) + return; + /* * We only consider the cheapest-total-cost input paths, since we are * assuming here that a sort is required. We will consider @@ -1559,6 +1562,9 @@ match_unsorted_outer(PlannerInfo *root, Path *matpath = NULL; ListCell *lc1; + if (jointype == JOIN_RIGHT_ANTI) + return; + /* * Nestloop only supports inner, left, semi, and anti joins. Also, if we * are doing a right or full mergejoin, we must use *all* the mergeclauses @@ -2100,6 +2106,7 @@ hash_inner_and_outer(PlannerInfo *root, save_jointype != JOIN_UNIQUE_OUTER && save_jointype != JOIN_FULL && save_jointype != JOIN_RIGHT && + save_jointype != JOIN_RIGHT_ANTI && outerrel->partial_pathlist != NIL && bms_is_empty(joinrel->lateral_relids)) { diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 0dbe2ac726..194f820c2f 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -915,6 +915,9 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_ANTI, sjinfo, restrictlist); + add_paths_to_joinrel(root, joinrel, rel2, rel1, + JOIN_RIGHT_ANTI, sjinfo, + restrictlist); break; default: /* other values not expected here */ diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 61ccfd300b..56fc238b0e 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -2069,6 +2069,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) inner_itlist->has_non_vars = false; break; case JOIN_RIGHT: + case JOIN_RIGHT_ANTI: outer_itlist->has_non_vars = false; break; case JOIN_FULL: diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index d9e417bcd7..7c07792e56 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -725,6 +725,7 @@ typedef enum JoinType */ JOIN_SEMI, /* 1 copy of each LHS row that has match(es) */ JOIN_ANTI, /* 1 copy of each LHS row that has no match */ + JOIN_RIGHT_ANTI, /* 1 copy of each RHS row that has no match */ /* * These codes are used internally in the planner, but are not supported @@ -757,7 +758,8 @@ typedef enum JoinType ((1 << JOIN_LEFT) | \ (1 << JOIN_FULL) | \ (1 << JOIN_RIGHT) | \ - (1 << JOIN_ANTI))) != 0) + (1 << JOIN_ANTI) | \ + (1 << JOIN_RIGHT_ANTI))) != 0) /* * AggStrategy - -- 2.31.0