aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/nodeHashjoin.c77
-rw-r--r--src/backend/executor/nodeMergejoin.c11
-rw-r--r--src/backend/executor/nodeNestloop.c60
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)));