aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeNestloop.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2000-09-12 21:07:18 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2000-09-12 21:07:18 +0000
commited5003c58401e5727fcdd970505972394c95febb (patch)
tree53c25d5c65d6f7275f110503f51ab370e55af6ea /src/backend/executor/nodeNestloop.c
parentb5c0ab278bc67bc7f363da7d828a08ce7c4d28c2 (diff)
downloadpostgresql-ed5003c58401e5727fcdd970505972394c95febb.tar.gz
postgresql-ed5003c58401e5727fcdd970505972394c95febb.zip
First cut at full support for OUTER JOINs. There are still a few loose
ends to clean up (see my message of same date to pghackers), but mostly it works. INITDB REQUIRED!
Diffstat (limited to 'src/backend/executor/nodeNestloop.c')
-rw-r--r--src/backend/executor/nodeNestloop.c194
1 files changed, 119 insertions, 75 deletions
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 3685232c7e4..5abd4ffc3a1 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.20 2000/08/24 03:29:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.21 2000/09/12 21:06:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -62,10 +62,10 @@ ExecNestLoop(NestLoop *node)
NestLoopState *nlstate;
Plan *innerPlan;
Plan *outerPlan;
- bool needNewOuterTuple;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
- List *qual;
+ List *joinqual;
+ List *otherqual;
ExprContext *econtext;
/* ----------------
@@ -75,9 +75,10 @@ ExecNestLoop(NestLoop *node)
ENL1_printf("getting info from node");
nlstate = node->nlstate;
- qual = node->join.qual;
- outerPlan = outerPlan(&node->join);
- innerPlan = innerPlan(&node->join);
+ joinqual = node->join.joinqual;
+ otherqual = node->join.plan.qual;
+ outerPlan = outerPlan((Plan *) node);
+ innerPlan = innerPlan((Plan *) node);
econtext = nlstate->jstate.cs_ExprContext;
/* ----------------
@@ -115,7 +116,7 @@ ExecNestLoop(NestLoop *node)
/* ----------------
* Ok, everything is setup for the join so now loop until
- * we return a qualifying join tuple..
+ * we return a qualifying join tuple.
* ----------------
*/
ENL1_printf("entering main loop");
@@ -123,44 +124,14 @@ ExecNestLoop(NestLoop *node)
for (;;)
{
/* ----------------
- * The essential idea now is to get the next inner tuple
- * and join it with the current outer tuple.
+ * If we don't have an outer tuple, get the next one and
+ * reset the inner scan.
* ----------------
*/
- needNewOuterTuple = TupIsNull(outerTupleSlot);
-
- /* ----------------
- * if we have an outerTuple, try to get the next inner tuple.
- * ----------------
- */
- if (!needNewOuterTuple)
- {
- ENL1_printf("getting new inner tuple");
-
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (TupIsNull(innerTupleSlot))
- {
- ENL1_printf("no inner tuple, need new outer tuple");
- needNewOuterTuple = true;
- }
- }
-
- /* ----------------
- * loop until we have a new outer tuple and a new
- * inner tuple.
- * ----------------
- */
- while (needNewOuterTuple)
+ if (nlstate->nl_NeedNewOuter)
{
- /* ----------------
- * now try to get the next outer tuple
- * ----------------
- */
ENL1_printf("getting new outer tuple");
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- econtext->ecxt_outertuple = outerTupleSlot;
/* ----------------
* if there are no more outer tuples, then the join
@@ -175,12 +146,14 @@ ExecNestLoop(NestLoop *node)
ENL1_printf("saving new outer tuple information");
nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+ econtext->ecxt_outertuple = outerTupleSlot;
+ nlstate->nl_NeedNewOuter = false;
+ nlstate->nl_MatchedOuter = false;
/* ----------------
- * now rescan the inner plan and get a new inner tuple
+ * now rescan the inner plan
* ----------------
*/
-
ENL1_printf("rescanning inner plan");
/*
@@ -189,48 +162,101 @@ ExecNestLoop(NestLoop *node)
* expr context.
*/
ExecReScan(innerPlan, econtext, (Plan *) node);
+ }
+
+ /* ----------------
+ * we have an outerTuple, try to get the next inner tuple.
+ * ----------------
+ */
+ ENL1_printf("getting new inner tuple");
+
+ innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+ econtext->ecxt_innertuple = innerTupleSlot;
- ENL1_printf("getting new inner tuple");
+ if (TupIsNull(innerTupleSlot))
+ {
+ ENL1_printf("no inner tuple, need new outer tuple");
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- econtext->ecxt_innertuple = innerTupleSlot;
+ nlstate->nl_NeedNewOuter = true;
- if (TupIsNull(innerTupleSlot))
- ENL1_printf("couldn't get inner tuple - need new outer tuple");
- else
+ if (! nlstate->nl_MatchedOuter &&
+ node->join.jointype == JOIN_LEFT)
{
- ENL1_printf("got inner and outer tuples");
- needNewOuterTuple = false;
+ /*
+ * We are doing an outer join and there were no join matches
+ * for this outer tuple. Generate a fake join tuple with
+ * nulls for the inner tuple, and return it if it passes
+ * the non-join quals.
+ */
+ econtext->ecxt_innertuple = nlstate->nl_NullInnerTupleSlot;
+
+ ENL1_printf("testing qualification for outer-join tuple");
+
+ if (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");
+
+ result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+
+ if (isDone != ExprEndResult)
+ {
+ nlstate->jstate.cs_TupFromTlist =
+ (isDone == ExprMultipleResult);
+ return result;
+ }
+ }
}
- } /* while (needNewOuterTuple) */
+ /*
+ * Otherwise just return to top of loop for a new outer tuple.
+ */
+ continue;
+ }
/* ----------------
* at this point we have a new pair of inner and outer
* tuples so we test the inner and outer tuples to see
- * if they satisify the node's qualification.
+ * if they satisfy the node's qualification.
+ *
+ * Only the joinquals determine MatchedOuter status,
+ * but all quals must pass to actually return the tuple.
* ----------------
*/
ENL1_printf("testing qualification");
- if (ExecQual((List *) qual, econtext, false))
+ if (ExecQual(joinqual, econtext, false))
{
- /* ----------------
- * qualification was satisified so we project and
- * return the slot containing the result tuple
- * using ExecProject().
- * ----------------
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
+ nlstate->nl_MatchedOuter = true;
- ENL1_printf("qualification succeeded, projecting tuple");
-
- result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
-
- if (isDone != ExprEndResult)
+ if (otherqual == NIL || ExecQual(otherqual, econtext, false))
{
- nlstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
- return result;
+ /* ----------------
+ * 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");
+
+ result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+
+ if (isDone != ExprEndResult)
+ {
+ nlstate->jstate.cs_TupFromTlist =
+ (isDone == ExprMultipleResult);
+ return result;
+ }
}
}
@@ -264,7 +290,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
* assign execution state to node
* ----------------
*/
- node->join.state = estate;
+ node->join.plan.state = estate;
/* ----------------
* create new nest loop state
@@ -281,19 +307,33 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
*/
ExecAssignExprContext(estate, &nlstate->jstate);
-#define NESTLOOP_NSLOTS 1
/* ----------------
- * tuple table initialization
+ * now initialize children
* ----------------
*/
- ExecInitResultTupleSlot(estate, &nlstate->jstate);
+ ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
+ ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+#define NESTLOOP_NSLOTS 2
/* ----------------
- * now initialize children
+ * tuple table initialization
* ----------------
*/
- ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
- ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+ ExecInitResultTupleSlot(estate, &nlstate->jstate);
+
+ switch (node->join.jointype)
+ {
+ case JOIN_INNER:
+ break;
+ case JOIN_LEFT:
+ nlstate->nl_NullInnerTupleSlot =
+ ExecInitNullTupleSlot(estate,
+ ExecGetTupType(innerPlan((Plan*) node)));
+ break;
+ default:
+ elog(ERROR, "ExecInitNestLoop: unsupported join type %d",
+ (int) node->join.jointype);
+ }
/* ----------------
* initialize tuple type and projection info
@@ -308,6 +348,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
*/
nlstate->jstate.cs_OuterTupleSlot = NULL;
nlstate->jstate.cs_TupFromTlist = false;
+ nlstate->nl_NeedNewOuter = true;
+ nlstate->nl_MatchedOuter = false;
NL1_printf("ExecInitNestLoop: %s\n",
"node initialized");
@@ -394,4 +436,6 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
/* let outerPlan to free its result tuple ... */
nlstate->jstate.cs_OuterTupleSlot = NULL;
nlstate->jstate.cs_TupFromTlist = false;
+ nlstate->nl_NeedNewOuter = true;
+ nlstate->nl_MatchedOuter = false;
}