diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/nodeHashjoin.c | 35 | ||||
-rw-r--r-- | src/backend/executor/nodeMergejoin.c | 33 |
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)) |