1 /*-------------------------------------------------------------------------
4 * routines to support nest-loop joins
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.46 2008/01/01 19:45:49 momjian Exp $
13 *-------------------------------------------------------------------------
17 * ExecNestLoop - process a nestloop join of two plans
18 * ExecInitNestLoop - initialize the join
19 * ExecEndNestLoop - shut down the join
24 #include "executor/execdebug.h"
25 #include "executor/nodeNestloop.h"
26 #include "utils/memutils.h"
29 /* ----------------------------------------------------------------
33 * Returns the tuple joined from inner and outer tuples which
34 * satisfies the qualification clause.
36 * It scans the inner relation to join with current outer tuple.
38 * If none is found, next tuple from the outer relation is retrieved
39 * and the inner relation is scanned from the beginning again to join
40 * with the outer tuple.
42 * NULL is returned if all the remaining outer tuples are tried and
43 * all fail to join with the inner tuples.
45 * NULL is also returned if there is no tuple from inner relation.
48 * -- outerTuple contains current tuple from outer relation and
49 * the right son(inner relation) maintains "cursor" at the tuple
50 * returned previously.
51 * This is achieved by maintaining a scan position on the outer
55 * -- the outer child and the inner child
56 * are prepared to return the first tuple.
57 * ----------------------------------------------------------------
60 ExecNestLoop(NestLoopState *node)
64 TupleTableSlot *outerTupleSlot;
65 TupleTableSlot *innerTupleSlot;
68 ExprContext *econtext;
71 * get information from the node
73 ENL1_printf("getting info from node");
75 joinqual = node->js.joinqual;
76 otherqual = node->js.ps.qual;
77 outerPlan = outerPlanState(node);
78 innerPlan = innerPlanState(node);
79 econtext = node->js.ps.ps_ExprContext;
82 * get the current outer tuple
84 outerTupleSlot = node->js.ps.ps_OuterTupleSlot;
85 econtext->ecxt_outertuple = outerTupleSlot;
88 * Check to see if we're still projecting out tuples from a previous join
89 * tuple (because there is a function-returning-set in the projection
90 * expressions). If so, try to project another one.
92 if (node->js.ps.ps_TupFromTlist)
94 TupleTableSlot *result;
97 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
98 if (isDone == ExprMultipleResult)
100 /* Done with that source tuple... */
101 node->js.ps.ps_TupFromTlist = false;
105 * If we're doing an IN join, we want to return at most one row per outer
106 * tuple; so we can stop scanning the inner scan if we matched on the
109 if (node->js.jointype == JOIN_IN &&
110 node->nl_MatchedOuter)
111 node->nl_NeedNewOuter = true;
114 * Reset per-tuple memory context to free any expression evaluation
115 * storage allocated in the previous tuple cycle. Note this can't happen
116 * until we're done projecting out tuples from a join tuple.
118 ResetExprContext(econtext);
121 * Ok, everything is setup for the join so now loop until we return a
122 * qualifying join tuple.
124 ENL1_printf("entering main loop");
129 * If we don't have an outer tuple, get the next one and reset the
132 if (node->nl_NeedNewOuter)
134 ENL1_printf("getting new outer tuple");
135 outerTupleSlot = ExecProcNode(outerPlan);
138 * if there are no more outer tuples, then the join is complete..
140 if (TupIsNull(outerTupleSlot))
142 ENL1_printf("no outer tuple, ending join");
146 ENL1_printf("saving new outer tuple information");
147 node->js.ps.ps_OuterTupleSlot = outerTupleSlot;
148 econtext->ecxt_outertuple = outerTupleSlot;
149 node->nl_NeedNewOuter = false;
150 node->nl_MatchedOuter = false;
153 * now rescan the inner plan
155 ENL1_printf("rescanning inner plan");
158 * The scan key of the inner plan might depend on the current
159 * outer tuple (e.g. in index scans), that's why we pass our expr
162 ExecReScan(innerPlan, econtext);
166 * we have an outerTuple, try to get the next inner tuple.
168 ENL1_printf("getting new inner tuple");
170 innerTupleSlot = ExecProcNode(innerPlan);
171 econtext->ecxt_innertuple = innerTupleSlot;
173 if (TupIsNull(innerTupleSlot))
175 ENL1_printf("no inner tuple, need new outer tuple");
177 node->nl_NeedNewOuter = true;
179 if (!node->nl_MatchedOuter &&
180 node->js.jointype == JOIN_LEFT)
183 * We are doing an outer join and there were no join matches
184 * for this outer tuple. Generate a fake join tuple with
185 * nulls for the inner tuple, and return it if it passes the
188 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
190 ENL1_printf("testing qualification for outer-join tuple");
192 if (ExecQual(otherqual, econtext, false))
195 * qualification was satisfied so we project and return
196 * the slot containing the result tuple using
199 TupleTableSlot *result;
202 ENL1_printf("qualification succeeded, projecting tuple");
204 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
206 if (isDone != ExprEndResult)
208 node->js.ps.ps_TupFromTlist =
209 (isDone == ExprMultipleResult);
216 * Otherwise just return to top of loop for a new outer tuple.
222 * at this point we have a new pair of inner and outer tuples so we
223 * test the inner and outer tuples to see if they satisfy the node's
226 * Only the joinquals determine MatchedOuter status, but all quals
227 * must pass to actually return the tuple.
229 ENL1_printf("testing qualification");
231 if (ExecQual(joinqual, econtext, false))
233 node->nl_MatchedOuter = true;
235 if (otherqual == NIL || ExecQual(otherqual, econtext, false))
238 * qualification was satisfied so we project and return the
239 * slot containing the result tuple using ExecProject().
241 TupleTableSlot *result;
244 ENL1_printf("qualification succeeded, projecting tuple");
246 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
248 if (isDone != ExprEndResult)
250 node->js.ps.ps_TupFromTlist =
251 (isDone == ExprMultipleResult);
256 /* If we didn't return a tuple, may need to set NeedNewOuter */
257 if (node->js.jointype == JOIN_IN)
258 node->nl_NeedNewOuter = true;
262 * Tuple fails qual, so free per-tuple memory and try again.
264 ResetExprContext(econtext);
266 ENL1_printf("qualification failed, looping");
270 /* ----------------------------------------------------------------
272 * ----------------------------------------------------------------
275 ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
277 NestLoopState *nlstate;
279 /* check for unsupported flags */
280 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
282 NL1_printf("ExecInitNestLoop: %s\n",
283 "initializing node");
286 * create state structure
288 nlstate = makeNode(NestLoopState);
289 nlstate->js.ps.plan = (Plan *) node;
290 nlstate->js.ps.state = estate;
293 * Miscellaneous initialization
295 * create expression context for node
297 ExecAssignExprContext(estate, &nlstate->js.ps);
300 * initialize child expressions
302 nlstate->js.ps.targetlist = (List *)
303 ExecInitExpr((Expr *) node->join.plan.targetlist,
304 (PlanState *) nlstate);
305 nlstate->js.ps.qual = (List *)
306 ExecInitExpr((Expr *) node->join.plan.qual,
307 (PlanState *) nlstate);
308 nlstate->js.jointype = node->join.jointype;
309 nlstate->js.joinqual = (List *)
310 ExecInitExpr((Expr *) node->join.joinqual,
311 (PlanState *) nlstate);
314 * initialize child nodes
316 * Tell the inner child that cheap rescans would be good. (This is
317 * unnecessary if we are doing nestloop with inner indexscan, because the
318 * rescan will always be with a fresh parameter --- but since
319 * nodeIndexscan doesn't actually care about REWIND, there's no point in
320 * dealing with that refinement.)
322 outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
323 innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate,
324 eflags | EXEC_FLAG_REWIND);
326 #define NESTLOOP_NSLOTS 2
329 * tuple table initialization
331 ExecInitResultTupleSlot(estate, &nlstate->js.ps);
333 switch (node->join.jointype)
339 nlstate->nl_NullInnerTupleSlot =
340 ExecInitNullTupleSlot(estate,
341 ExecGetResultType(innerPlanState(nlstate)));
344 elog(ERROR, "unrecognized join type: %d",
345 (int) node->join.jointype);
349 * initialize tuple type and projection info
351 ExecAssignResultTypeFromTL(&nlstate->js.ps);
352 ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
355 * finally, wipe the current outer tuple clean.
357 nlstate->js.ps.ps_OuterTupleSlot = NULL;
358 nlstate->js.ps.ps_TupFromTlist = false;
359 nlstate->nl_NeedNewOuter = true;
360 nlstate->nl_MatchedOuter = false;
362 NL1_printf("ExecInitNestLoop: %s\n",
369 ExecCountSlotsNestLoop(NestLoop *node)
371 return ExecCountSlotsNode(outerPlan(node)) +
372 ExecCountSlotsNode(innerPlan(node)) +
376 /* ----------------------------------------------------------------
379 * closes down scans and frees allocated storage
380 * ----------------------------------------------------------------
383 ExecEndNestLoop(NestLoopState *node)
385 NL1_printf("ExecEndNestLoop: %s\n",
386 "ending node processing");
389 * Free the exprcontext
391 ExecFreeExprContext(&node->js.ps);
394 * clean out the tuple table
396 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
399 * close down subplans
401 ExecEndNode(outerPlanState(node));
402 ExecEndNode(innerPlanState(node));
404 NL1_printf("ExecEndNestLoop: %s\n",
405 "node processing ended");
408 /* ----------------------------------------------------------------
410 * ----------------------------------------------------------------
413 ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt)
415 PlanState *outerPlan = outerPlanState(node);
418 * If outerPlan->chgParam is not null then plan will be automatically
419 * re-scanned by first ExecProcNode. innerPlan is re-scanned for each new
420 * outer tuple and MUST NOT be re-scanned from here or you'll get troubles
421 * from inner index scans when outer Vars are used as run-time keys...
423 if (outerPlan->chgParam == NULL)
424 ExecReScan(outerPlan, exprCtxt);
426 /* let outerPlan to free its result tuple ... */
427 node->js.ps.ps_OuterTupleSlot = NULL;
428 node->js.ps.ps_TupFromTlist = false;
429 node->nl_NeedNewOuter = true;
430 node->nl_MatchedOuter = false;