diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/nodeHashjoin.c | 77 | ||||
-rw-r--r-- | src/backend/executor/nodeMergejoin.c | 11 | ||||
-rw-r--r-- | src/backend/executor/nodeNestloop.c | 60 |
3 files changed, 83 insertions, 65 deletions
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index bfe5be7c33c..837837bece0 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.93 2008/01/01 19:45:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.94 2008/08/14 18:47:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,9 @@ #include "utils/memutils.h" +/* Returns true for JOIN_LEFT and JOIN_ANTI jointypes */ +#define HASHJOIN_IS_OUTER(hjstate) ((hjstate)->hj_NullInnerTupleSlot != NULL) + static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *outerNode, HashJoinState *hjstate, uint32 *hashvalue); @@ -90,14 +93,6 @@ ExecHashJoin(HashJoinState *node) } /* - * If we're doing an IN join, we want to return at most one row per outer - * tuple; so we can stop scanning the inner scan if we matched on the - * previous try. - */ - if (node->js.jointype == JOIN_IN && node->hj_MatchedOuter) - node->hj_NeedNewOuter = true; - - /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a join tuple. @@ -129,7 +124,7 @@ ExecHashJoin(HashJoinState *node) * outer plan node. If we succeed, we have to stash it away for later * consumption by ExecHashJoinOuterGetTuple. */ - if (node->js.jointype == JOIN_LEFT || + if (HASHJOIN_IS_OUTER(node) || (outerNode->plan->startup_cost < hashNode->ps.plan->total_cost && !node->hj_OuterNotEmpty)) { @@ -162,7 +157,7 @@ ExecHashJoin(HashJoinState *node) * If the inner relation is completely empty, and we're not doing an * outer join, we can quit without scanning the outer relation. */ - if (hashtable->totalTuples == 0 && node->js.jointype != JOIN_LEFT) + if (hashtable->totalTuples == 0 && !HASHJOIN_IS_OUTER(node)) return NULL; /* @@ -263,27 +258,41 @@ ExecHashJoin(HashJoinState *node) { node->hj_MatchedOuter = true; - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + /* In an antijoin, we never return a matched tuple */ + if (node->js.jointype == JOIN_ANTI) + { + node->hj_NeedNewOuter = true; + break; /* out of loop over hash bucket */ + } + else { - TupleTableSlot *result; + /* + * In a semijoin, we'll consider returning the first match, + * but after that we're done with this outer tuple. + */ + if (node->js.jointype == JOIN_SEMI) + node->hj_NeedNewOuter = true; + + if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + { + TupleTableSlot *result; - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; + if (isDone != ExprEndResult) + { + node->js.ps.ps_TupFromTlist = + (isDone == ExprMultipleResult); + return result; + } } - } - /* - * If we didn't return a tuple, may need to set NeedNewOuter - */ - if (node->js.jointype == JOIN_IN) - { - node->hj_NeedNewOuter = true; - break; /* out of loop over hash bucket */ + /* + * If semijoin and we didn't return the tuple, we're still + * done with this outer tuple. + */ + if (node->js.jointype == JOIN_SEMI) + break; /* out of loop over hash bucket */ } } } @@ -296,7 +305,7 @@ ExecHashJoin(HashJoinState *node) node->hj_NeedNewOuter = true; if (!node->hj_MatchedOuter && - node->js.jointype == JOIN_LEFT) + HASHJOIN_IS_OUTER(node)) { /* * We are doing an outer join and there were no join matches for @@ -305,7 +314,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (ExecQual(otherqual, econtext, false)) + if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { /* * qualification was satisfied so we project and return the @@ -398,12 +407,14 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) ExecInitResultTupleSlot(estate, &hjstate->js.ps); hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate); + /* note: HASHJOIN_IS_OUTER macro depends on this initialization */ switch (node->join.jointype) { case JOIN_INNER: - case JOIN_IN: + case JOIN_SEMI: break; case JOIN_LEFT: + case JOIN_ANTI: hjstate->hj_NullInnerTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(innerPlanState(hjstate))); @@ -570,7 +581,7 @@ ExecHashJoinOuterGetTuple(PlanState *outerNode, if (ExecHashGetHashValue(hashtable, econtext, hjstate->hj_OuterHashKeys, true, /* outer tuple */ - (hjstate->js.jointype == JOIN_LEFT), + HASHJOIN_IS_OUTER(hjstate), hashvalue)) { /* remember outer relation is not empty for possible rescan */ @@ -650,7 +661,7 @@ start_over: * sides. We can sometimes skip over batches that are empty on only one * side, but there are exceptions: * - * 1. In a LEFT JOIN, we have to process outer batches even if the inner + * 1. In an outer join, we have to process outer batches even if the inner * batch is empty. * * 2. If we have increased nbatch since the initial estimate, we have to @@ -667,7 +678,7 @@ start_over: hashtable->innerBatchFile[curbatch] == NULL)) { if (hashtable->outerBatchFile[curbatch] && - hjstate->js.jointype == JOIN_LEFT) + HASHJOIN_IS_OUTER(hjstate)) break; /* must process due to rule 1 */ if (hashtable->innerBatchFile[curbatch] && nbatch != hashtable->nbatch_original) diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index cf1bb7d0a69..e9deb5c8da7 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.91 2008/04/13 20:51:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.92 2008/08/14 18:47:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -757,7 +757,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - if (node->js.jointype == JOIN_IN && + if (node->js.jointype == JOIN_SEMI && node->mj_MatchedOuter) qualResult = false; else @@ -772,6 +772,10 @@ ExecMergeJoin(MergeJoinState *node) node->mj_MatchedOuter = true; node->mj_MatchedInner = true; + /* In an antijoin, we never return a matched tuple */ + if (node->js.jointype == JOIN_ANTI) + break; + qualResult = (otherqual == NIL || ExecQual(otherqual, econtext, false)); MJ_DEBUG_QUAL(otherqual, qualResult); @@ -1472,11 +1476,12 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) switch (node->join.jointype) { case JOIN_INNER: - case JOIN_IN: + case JOIN_SEMI: mergestate->mj_FillOuter = false; mergestate->mj_FillInner = false; break; case JOIN_LEFT: + case JOIN_ANTI: mergestate->mj_FillOuter = true; mergestate->mj_FillInner = false; mergestate->mj_NullInnerTupleSlot = diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 78216212ff5..c6a33228582 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.46 2008/01/01 19:45:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.47 2008/08/14 18:47:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -102,15 +102,6 @@ ExecNestLoop(NestLoopState *node) } /* - * If we're doing an IN join, we want to return at most one row per outer - * tuple; so we can stop scanning the inner scan if we matched on the - * previous try. - */ - if (node->js.jointype == JOIN_IN && - node->nl_MatchedOuter) - node->nl_NeedNewOuter = true; - - /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a join tuple. @@ -177,7 +168,8 @@ ExecNestLoop(NestLoopState *node) node->nl_NeedNewOuter = true; if (!node->nl_MatchedOuter && - node->js.jointype == JOIN_LEFT) + (node->js.jointype == JOIN_LEFT || + node->js.jointype == JOIN_ANTI)) { /* * We are doing an outer join and there were no join matches @@ -189,7 +181,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (ExecQual(otherqual, econtext, false)) + if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { /* * qualification was satisfied so we project and return @@ -232,30 +224,39 @@ ExecNestLoop(NestLoopState *node) { node->nl_MatchedOuter = true; - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + /* In an antijoin, we never return a matched tuple */ + if (node->js.jointype == JOIN_ANTI) + node->nl_NeedNewOuter = true; + else { /* - * qualification was satisfied so we project and return the - * slot containing the result tuple using ExecProject(). + * In a semijoin, we'll consider returning the first match, + * but after that we're done with this outer tuple. */ - TupleTableSlot *result; - ExprDoneCond isDone; + if (node->js.jointype == JOIN_SEMI) + node->nl_NeedNewOuter = true; + if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + { + /* + * qualification was satisfied so we project and return + * the slot containing the result tuple using + * ExecProject(). + */ + TupleTableSlot *result; + ExprDoneCond isDone; - ENL1_printf("qualification succeeded, projecting tuple"); + ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; + if (isDone != ExprEndResult) + { + node->js.ps.ps_TupFromTlist = + (isDone == ExprMultipleResult); + return result; + } } } - - /* If we didn't return a tuple, may need to set NeedNewOuter */ - if (node->js.jointype == JOIN_IN) - node->nl_NeedNewOuter = true; } /* @@ -333,9 +334,10 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) switch (node->join.jointype) { case JOIN_INNER: - case JOIN_IN: + case JOIN_SEMI: break; case JOIN_LEFT: + case JOIN_ANTI: nlstate->nl_NullInnerTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(innerPlanState(nlstate))); |