1 /*-------------------------------------------------------------------------
4 * routines to support nest-loop joins
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.13 1999/07/16 04:58:51 momjian Exp $
12 *-------------------------------------------------------------------------
16 * ExecNestLoop - process a nestloop join of two plans
17 * ExecInitNestLoop - initialize the join
18 * ExecEndNestLoop - shut down the join
22 #include "executor/execdebug.h"
23 #include "executor/executor.h"
24 #include "executor/nodeNestloop.h"
26 /* ----------------------------------------------------------------
30 * Returns the tuple joined from inner and outer tuples which
31 * satisfies the qualification clause.
33 * It scans the inner relation to join with current outer tuple.
35 * If none is found, next tuple form the outer relation is retrieved
36 * and the inner relation is scanned from the beginning again to join
37 * with the outer tuple.
39 * Nil is returned if all the remaining outer tuples are tried and
40 * all fail to join with the inner tuples.
42 * Nil is also returned if there is no tuple from inner realtion.
45 * -- outerTuple contains current tuple from outer relation and
46 * the right son(inner realtion) maintains "cursor" at the tuple
47 * returned previously.
48 * This is achieved by maintaining a scan position on the outer
52 * -- the outer child and the inner child
53 * are prepared to return the first tuple.
54 * ----------------------------------------------------------------
57 ExecNestLoop(NestLoop *node, Plan *parent)
59 NestLoopState *nlstate;
62 bool needNewOuterTuple;
64 TupleTableSlot *outerTupleSlot;
65 TupleTableSlot *innerTupleSlot;
69 ExprContext *econtext;
72 * get information from the node
75 ENL1_printf("getting info from node");
77 nlstate = node->nlstate;
78 qual = node->join.qual;
79 outerPlan = outerPlan(&node->join);
80 innerPlan = innerPlan(&node->join);
83 * initialize expression context
86 econtext = nlstate->jstate.cs_ExprContext;
89 * get the current outer tuple
92 outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
93 econtext->ecxt_outertuple = outerTupleSlot;
96 * Ok, everything is setup for the join so now loop until
97 * we return a qualifying join tuple..
101 if (nlstate->jstate.cs_TupFromTlist)
103 TupleTableSlot *result;
106 result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
111 ENL1_printf("entering main loop");
115 * The essential idea now is to get the next inner tuple
116 * and join it with the current outer tuple.
119 needNewOuterTuple = false;
121 if (!TupIsNull(outerTupleSlot))
122 ENL1_printf("have outer tuple, deal with it");
125 ENL1_printf("outer tuple is nil, need new outer tuple");
126 needNewOuterTuple = true;
130 * if we have an outerTuple, try to get the next inner tuple.
133 if (!needNewOuterTuple)
135 ENL1_printf("getting new inner tuple");
137 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
138 econtext->ecxt_innertuple = innerTupleSlot;
140 if (TupIsNull(innerTupleSlot))
142 ENL1_printf("no inner tuple, need new outer tuple");
143 needNewOuterTuple = true;
148 * loop until we have a new outer tuple and a new
152 while (needNewOuterTuple)
155 * now try to get the next outer tuple
158 ENL1_printf("getting new outer tuple");
159 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
160 econtext->ecxt_outertuple = outerTupleSlot;
163 * if there are no more outer tuples, then the join
167 if (TupIsNull(outerTupleSlot))
169 ENL1_printf("no outer tuple, ending join");
173 ENL1_printf("saving new outer tuple information");
174 nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
177 * now rescan the inner plan and get a new inner tuple
181 ENL1_printf("rescanning inner plan");
184 * The scan key of the inner plan might depend on the current
185 * outer tuple (e.g. in index scans), that's why we pass our
188 ExecReScan(innerPlan, econtext, parent);
190 ENL1_printf("getting new inner tuple");
192 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
193 econtext->ecxt_innertuple = innerTupleSlot;
195 if (TupIsNull(innerTupleSlot))
196 ENL1_printf("couldn't get inner tuple - need new outer tuple");
199 ENL1_printf("got inner and outer tuples");
200 needNewOuterTuple = false;
202 } /* while (needNewOuterTuple) */
205 * at this point we have a new pair of inner and outer
206 * tuples so we test the inner and outer tuples to see
207 * if they satisify the node's qualification.
210 ENL1_printf("testing qualification");
211 qualResult = ExecQual((List *) qual, econtext);
216 * qualification was satisified so we project and
217 * return the slot containing the result tuple
218 * using ExecProject().
221 ProjectionInfo *projInfo;
222 TupleTableSlot *result;
225 ENL1_printf("qualification succeeded, projecting tuple");
227 projInfo = nlstate->jstate.cs_ProjInfo;
228 result = ExecProject(projInfo, &isDone);
229 nlstate->jstate.cs_TupFromTlist = !isDone;
234 * qualification failed so we have to try again..
237 ENL1_printf("qualification failed, looping");
241 /* ----------------------------------------------------------------
244 * Creates the run-time state information for the nestloop node
245 * produced by the planner and initailizes inner and outer relations
247 * ----------------------------------------------------------------
250 ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
252 NestLoopState *nlstate;
254 NL1_printf("ExecInitNestLoop: %s\n",
255 "initializing node");
258 * assign execution state to node
261 node->join.state = estate;
264 * create new nest loop state
267 nlstate = makeNode(NestLoopState);
268 nlstate->nl_PortalFlag = false;
269 node->nlstate = nlstate;
272 * Miscellanious initialization
274 * + assign node's base_id
275 * + assign debugging hooks and
276 * + create expression context for node
279 ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
280 ExecAssignExprContext(estate, &nlstate->jstate);
282 #define NESTLOOP_NSLOTS 1
284 * tuple table initialization
287 ExecInitResultTupleSlot(estate, &nlstate->jstate);
290 * now initialize children
293 ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
294 ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
297 * initialize tuple type and projection info
300 ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
301 ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
304 * finally, wipe the current outer tuple clean.
307 nlstate->jstate.cs_OuterTupleSlot = NULL;
308 nlstate->jstate.cs_TupFromTlist = false;
310 NL1_printf("ExecInitNestLoop: %s\n",
316 ExecCountSlotsNestLoop(NestLoop *node)
318 return ExecCountSlotsNode(outerPlan(node)) +
319 ExecCountSlotsNode(innerPlan(node)) +
323 /* ----------------------------------------------------------------
326 * closes down scans and frees allocated storage
327 * ----------------------------------------------------------------
330 ExecEndNestLoop(NestLoop *node)
332 NestLoopState *nlstate;
334 NL1_printf("ExecEndNestLoop: %s\n",
335 "ending node processing");
338 * get info from the node
341 nlstate = node->nlstate;
344 * Free the projection info
346 * Note: we don't ExecFreeResultType(nlstate)
347 * because the rule manager depends on the tupType
348 * returned by ExecMain(). So for now, this
349 * is freed at end-transaction time. -cim 6/2/91
352 ExecFreeProjectionInfo(&nlstate->jstate);
355 * close down subplans
358 ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
359 ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
362 * clean out the tuple table
365 ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
367 NL1_printf("ExecEndNestLoop: %s\n",
368 "node processing ended");
371 /* ----------------------------------------------------------------
373 * ----------------------------------------------------------------
376 ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
378 NestLoopState *nlstate = node->nlstate;
379 Plan *outerPlan = outerPlan((Plan *) node);
382 * If outerPlan->chgParam is not null then plan will be automatically
383 * re-scanned by first ExecProcNode. innerPlan is re-scanned for each
384 * new outer tuple and MUST NOT be re-scanned from here or you'll get
385 * troubles from inner index scans when outer Vars are used as
388 if (outerPlan->chgParam == NULL)
389 ExecReScan(outerPlan, exprCtxt, (Plan *) node);
391 /* let outerPlan to free its result typle ... */
392 nlstate->jstate.cs_OuterTupleSlot = NULL;
393 nlstate->jstate.cs_TupFromTlist = false;