summaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/nodeHashjoin.c35
-rw-r--r--src/backend/executor/nodeMergejoin.c33
2 files changed, 44 insertions, 24 deletions
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 52ed05c6f5a..0a3f32f731d 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -86,7 +86,7 @@
* PHJ_BATCH_ALLOCATE* -- one allocates buckets
* PHJ_BATCH_LOAD -- all load the hash table from disk
* PHJ_BATCH_PROBE -- all probe
- * PHJ_BATCH_SCAN* -- one does full/right unmatched scan
+ * PHJ_BATCH_SCAN* -- one does right/right-anti/full unmatched scan
* PHJ_BATCH_FREE* -- one frees memory
*
* Batch 0 is a special case, because it starts out in phase
@@ -228,10 +228,10 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel)
/*
* If the outer relation is completely empty, and it's not
- * right/full join, we can quit without building the hash
- * table. However, for an inner join it is only a win to
- * check this when the outer relation's startup cost is less
- * than the projected cost of building the hash table.
+ * right/right-anti/full join, we can quit without building
+ * the hash table. However, for an inner join it is only a
+ * win to check this when the outer relation's startup cost is
+ * less than the projected cost of building the hash table.
* Otherwise it's best to build the hash table first and see
* if the inner relation is empty. (When it's a left join, we
* should always make this check, since we aren't going to be
@@ -520,6 +520,14 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel)
}
/*
+ * In a right-antijoin, we never return a matched tuple.
+ * And we need to stay on the current outer tuple to
+ * continue scanning the inner side 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
* continue with next outer tuple.
@@ -564,9 +572,10 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel)
case HJ_FILL_INNER_TUPLES:
/*
- * We have finished a batch, but we are doing right/full join,
- * so any unmatched inner tuples in the hashtable have to be
- * emitted before we continue to the next batch.
+ * We have finished a batch, but we are doing
+ * right/right-anti/full join, so any unmatched inner tuples
+ * in the hashtable have to be emitted before we continue to
+ * the next batch.
*/
if (!(parallel ? ExecParallelScanHashTableForUnmatched(node, econtext)
: ExecScanHashTableForUnmatched(node, econtext)))
@@ -732,6 +741,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;
@@ -1027,8 +1037,9 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
* side, but there are exceptions:
*
* 1. In a left/full outer join, we have to process outer batches even if
- * the inner batch is empty. Similarly, in a right/full outer join, we
- * have to process inner batches even if the outer batch is empty.
+ * the inner batch is empty. Similarly, in a right/right-anti/full outer
+ * join, we have to process inner batches even if the outer batch is
+ * empty.
*
* 2. If we have increased nbatch since the initial estimate, we have to
* scan inner batches since they might contain tuples that need to be
@@ -1349,8 +1360,8 @@ ExecReScanHashJoin(HashJoinState *node)
/*
* Okay to reuse the hash table; needn't rescan inner, either.
*
- * However, if it's a right/full join, we'd better reset the
- * inner-tuple match flags contained in the table.
+ * However, if it's a right/right-anti/full join, we'd better
+ * reset the inner-tuple match flags contained in the table.
*/
if (HJ_FILL_INNER(node))
ExecHashTableResetMatchFlags(node->hj_HashTable);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 809aa215c67..00f96d045e0 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -806,6 +806,14 @@ ExecMergeJoin(PlanState *pstate)
}
/*
+ * In a right-antijoin, we never return a matched tuple.
+ * And we need to stay on the current outer tuple to
+ * continue scanning the inner side for matches.
+ */
+ if (node->js.jointype == JOIN_RIGHT_ANTI)
+ break;
+
+ /*
* If we only need to join to the first matching inner
* tuple, then consider returning this one, but after that
* continue with next outer tuple.
@@ -1063,12 +1071,12 @@ ExecMergeJoin(PlanState *pstate)
* them will match this new outer tuple and therefore
* won't be emitted as fill tuples. This works *only*
* because we require the extra joinquals to be constant
- * when doing a right or full join --- otherwise some of
- * the rescanned tuples might fail the extra joinquals.
- * This obviously won't happen for a constant-true extra
- * joinqual, while the constant-false case is handled by
- * forcing the merge clause to never match, so we never
- * get here.
+ * when doing a right, right-anti or full join ---
+ * otherwise some of the rescanned tuples might fail the
+ * extra joinquals. This obviously won't happen for a
+ * constant-true extra joinqual, while the constant-false
+ * case is handled by forcing the merge clause to never
+ * match, so we never get here.
*/
if (!node->mj_SkipMarkRestore)
{
@@ -1332,8 +1340,8 @@ ExecMergeJoin(PlanState *pstate)
/*
* EXEC_MJ_ENDOUTER means we have run out of outer tuples, but
- * are doing a right/full join and therefore must null-fill
- * any remaining unmatched inner tuples.
+ * are doing a right/right-anti/full join and therefore must
+ * null-fill any remaining unmatched inner tuples.
*/
case EXEC_MJ_ENDOUTER:
MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
@@ -1554,14 +1562,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual);
break;
case JOIN_RIGHT:
+ case JOIN_RIGHT_ANTI:
mergestate->mj_FillOuter = false;
mergestate->mj_FillInner = true;
mergestate->mj_NullOuterTupleSlot =
ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual);
/*
- * Can't handle right or full join with non-constant extra
- * joinclauses. This should have been caught by planner.
+ * Can't handle right, right-anti or full join with non-constant
+ * extra joinclauses. This should have been caught by planner.
*/
if (!check_constant_qual(node->join.joinqual,
&mergestate->mj_ConstFalseJoin))
@@ -1578,8 +1587,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual);
/*
- * Can't handle right or full join with non-constant extra
- * joinclauses. This should have been caught by planner.
+ * Can't handle right, right-anti or full join with non-constant
+ * extra joinclauses. This should have been caught by planner.
*/
if (!check_constant_qual(node->join.joinqual,
&mergestate->mj_ConstFalseJoin))