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.5 1997/09/08 02:22:48 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/executor.h"
23 #include "executor/execdebug.h"
24 #include "executor/nodeNestloop.h"
25 #include "executor/nodeIndexscan.h"
27 /* ----------------------------------------------------------------
31 * Returns the tuple joined from inner and outer tuples which
32 * satisfies the qualification clause.
34 * It scans the inner relation to join with current outer tuple.
36 * If none is found, next tuple form the outer relation is retrieved
37 * and the inner relation is scanned from the beginning again to join
38 * with the outer tuple.
40 * Nil is returned if all the remaining outer tuples are tried and
41 * all fail to join with the inner tuples.
43 * Nil is also returned if there is no tuple from inner realtion.
46 * -- outerTuple contains current tuple from outer relation and
47 * the right son(inner realtion) maintains "cursor" at the tuple
48 * returned previously.
49 * This is achieved by maintaining a scan position on the outer
53 * -- the outer child and the inner child
54 * are prepared to return the first tuple.
55 * ----------------------------------------------------------------
58 ExecNestLoop(NestLoop * node, Plan * parent)
60 NestLoopState *nlstate;
63 bool needNewOuterTuple;
65 TupleTableSlot *outerTupleSlot;
66 TupleTableSlot *innerTupleSlot;
70 ExprContext *econtext;
73 * get information from the node
76 ENL1_printf("getting info from node");
78 nlstate = node->nlstate;
79 qual = node->join.qual;
80 outerPlan = outerPlan(&node->join);
81 innerPlan = innerPlan(&node->join);
84 * initialize expression context
87 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;
122 * If outer tuple is not null then that means
123 * we are in the middle of a scan and we should
124 * restore our previously saved scan position.
127 if (!TupIsNull(outerTupleSlot))
129 ENL1_printf("have outer tuple, restoring outer plan");
130 ExecRestrPos(outerPlan);
134 ENL1_printf("outer tuple is nil, need new outer tuple");
135 needNewOuterTuple = true;
139 * if we have an outerTuple, try to get the next inner tuple.
142 if (!needNewOuterTuple)
144 ENL1_printf("getting new inner tuple");
146 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
147 econtext->ecxt_innertuple = innerTupleSlot;
149 if (TupIsNull(innerTupleSlot))
151 ENL1_printf("no inner tuple, need new outer tuple");
152 needNewOuterTuple = true;
157 * loop until we have a new outer tuple and a new
161 while (needNewOuterTuple)
164 * now try to get the next outer tuple
167 ENL1_printf("getting new outer tuple");
168 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
169 econtext->ecxt_outertuple = outerTupleSlot;
172 * if there are no more outer tuples, then the join
176 if (TupIsNull(outerTupleSlot))
178 ENL1_printf("no outer tuple, ending join");
183 * we have a new outer tuple so we mark our position
184 * in the outer scan and save the outer tuple in the
188 ENL1_printf("saving new outer tuple information");
189 ExecMarkPos(outerPlan);
190 nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
193 * now rescan the inner plan and get a new inner tuple
197 ENL1_printf("rescanning inner plan");
200 * The scan key of the inner plan might depend on the current
201 * outer tuple (e.g. in index scans), that's why we pass our
204 ExecReScan(innerPlan, econtext, parent);
206 ENL1_printf("getting new inner tuple");
208 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
209 econtext->ecxt_innertuple = innerTupleSlot;
211 if (TupIsNull(innerTupleSlot))
213 ENL1_printf("couldn't get inner tuple - need new outer tuple");
217 ENL1_printf("got inner and outer tuples");
218 needNewOuterTuple = false;
220 } /* while (needNewOuterTuple) */
223 * at this point we have a new pair of inner and outer
224 * tuples so we test the inner and outer tuples to see
225 * if they satisify the node's qualification.
228 ENL1_printf("testing qualification");
229 qualResult = ExecQual((List *) qual, econtext);
234 * qualification was satisified so we project and
235 * return the slot containing the result tuple
236 * using ExecProject().
239 ProjectionInfo *projInfo;
240 TupleTableSlot *result;
243 ENL1_printf("qualification succeeded, projecting tuple");
245 projInfo = nlstate->jstate.cs_ProjInfo;
246 result = ExecProject(projInfo, &isDone);
247 nlstate->jstate.cs_TupFromTlist = !isDone;
252 * qualification failed so we have to try again..
255 ENL1_printf("qualification failed, looping");
259 /* ----------------------------------------------------------------
262 * Creates the run-time state information for the nestloop node
263 * produced by the planner and initailizes inner and outer relations
265 * ----------------------------------------------------------------
268 ExecInitNestLoop(NestLoop * node, EState * estate, Plan * parent)
270 NestLoopState *nlstate;
272 NL1_printf("ExecInitNestLoop: %s\n",
273 "initializing node");
276 * assign execution state to node
279 node->join.state = estate;
282 * create new nest loop state
285 nlstate = makeNode(NestLoopState);
286 nlstate->nl_PortalFlag = false;
287 node->nlstate = nlstate;
290 * Miscellanious initialization
292 * + assign node's base_id
293 * + assign debugging hooks and
294 * + create expression context for node
297 ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
298 ExecAssignExprContext(estate, &nlstate->jstate);
300 #define NESTLOOP_NSLOTS 1
302 * tuple table initialization
305 ExecInitResultTupleSlot(estate, &nlstate->jstate);
308 * now initialize children
311 ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
312 ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
315 * initialize tuple type and projection info
318 ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
319 ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
322 * finally, wipe the current outer tuple clean.
325 nlstate->jstate.cs_OuterTupleSlot = NULL;
326 nlstate->jstate.cs_TupFromTlist = false;
328 NL1_printf("ExecInitNestLoop: %s\n",
334 ExecCountSlotsNestLoop(NestLoop * node)
336 return ExecCountSlotsNode(outerPlan(node)) +
337 ExecCountSlotsNode(innerPlan(node)) +
341 /* ----------------------------------------------------------------
344 * closes down scans and frees allocated storage
345 * ----------------------------------------------------------------
348 ExecEndNestLoop(NestLoop * node)
350 NestLoopState *nlstate;
352 NL1_printf("ExecEndNestLoop: %s\n",
353 "ending node processing");
356 * get info from the node
359 nlstate = node->nlstate;
362 * Free the projection info
364 * Note: we don't ExecFreeResultType(nlstate)
365 * because the rule manager depends on the tupType
366 * returned by ExecMain(). So for now, this
367 * is freed at end-transaction time. -cim 6/2/91
370 ExecFreeProjectionInfo(&nlstate->jstate);
373 * close down subplans
376 ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
377 ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
380 * clean out the tuple table
383 ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
385 NL1_printf("ExecEndNestLoop: %s\n",
386 "node processing ended");