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.48 2008/08/15 19:20:42 tgl 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 * Reset per-tuple memory context to free any expression evaluation
106 * storage allocated in the previous tuple cycle. Note this can't happen
107 * until we're done projecting out tuples from a join tuple.
109 ResetExprContext(econtext);
112 * Ok, everything is setup for the join so now loop until we return a
113 * qualifying join tuple.
115 ENL1_printf("entering main loop");
120 * If we don't have an outer tuple, get the next one and reset the
123 if (node->nl_NeedNewOuter)
125 ENL1_printf("getting new outer tuple");
126 outerTupleSlot = ExecProcNode(outerPlan);
129 * if there are no more outer tuples, then the join is complete..
131 if (TupIsNull(outerTupleSlot))
133 ENL1_printf("no outer tuple, ending join");
137 ENL1_printf("saving new outer tuple information");
138 node->js.ps.ps_OuterTupleSlot = outerTupleSlot;
139 econtext->ecxt_outertuple = outerTupleSlot;
140 node->nl_NeedNewOuter = false;
141 node->nl_MatchedOuter = false;
144 * now rescan the inner plan
146 ENL1_printf("rescanning inner plan");
149 * The scan key of the inner plan might depend on the current
150 * outer tuple (e.g. in index scans), that's why we pass our expr
153 ExecReScan(innerPlan, econtext);
157 * we have an outerTuple, try to get the next inner tuple.
159 ENL1_printf("getting new inner tuple");
161 innerTupleSlot = ExecProcNode(innerPlan);
162 econtext->ecxt_innertuple = innerTupleSlot;
164 if (TupIsNull(innerTupleSlot))
166 ENL1_printf("no inner tuple, need new outer tuple");
168 node->nl_NeedNewOuter = true;
170 if (!node->nl_MatchedOuter &&
171 (node->js.jointype == JOIN_LEFT ||
172 node->js.jointype == JOIN_ANTI))
175 * We are doing an outer join and there were no join matches
176 * for this outer tuple. Generate a fake join tuple with
177 * nulls for the inner tuple, and return it if it passes the
180 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
182 ENL1_printf("testing qualification for outer-join tuple");
184 if (otherqual == NIL || ExecQual(otherqual, econtext, false))
187 * qualification was satisfied so we project and return
188 * the slot containing the result tuple using
191 TupleTableSlot *result;
194 ENL1_printf("qualification succeeded, projecting tuple");
196 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
198 if (isDone != ExprEndResult)
200 node->js.ps.ps_TupFromTlist =
201 (isDone == ExprMultipleResult);
208 * Otherwise just return to top of loop for a new outer tuple.
214 * at this point we have a new pair of inner and outer tuples so we
215 * test the inner and outer tuples to see if they satisfy the node's
218 * Only the joinquals determine MatchedOuter status, but all quals
219 * must pass to actually return the tuple.
221 ENL1_printf("testing qualification");
223 if (ExecQual(joinqual, econtext, false))
225 node->nl_MatchedOuter = true;
227 /* In an antijoin, we never return a matched tuple */
228 if (node->js.jointype == JOIN_ANTI)
230 node->nl_NeedNewOuter = true;
231 continue; /* return to top of loop */
235 * In a semijoin, we'll consider returning the first match,
236 * but after that we're done with this outer tuple.
238 if (node->js.jointype == JOIN_SEMI)
239 node->nl_NeedNewOuter = true;
241 if (otherqual == NIL || ExecQual(otherqual, econtext, false))
244 * qualification was satisfied so we project and return the
245 * slot containing the result tuple using ExecProject().
247 TupleTableSlot *result;
250 ENL1_printf("qualification succeeded, projecting tuple");
252 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
254 if (isDone != ExprEndResult)
256 node->js.ps.ps_TupFromTlist =
257 (isDone == ExprMultipleResult);
264 * Tuple fails qual, so free per-tuple memory and try again.
266 ResetExprContext(econtext);
268 ENL1_printf("qualification failed, looping");
272 /* ----------------------------------------------------------------
274 * ----------------------------------------------------------------
277 ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
279 NestLoopState *nlstate;
281 /* check for unsupported flags */
282 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
284 NL1_printf("ExecInitNestLoop: %s\n",
285 "initializing node");
288 * create state structure
290 nlstate = makeNode(NestLoopState);
291 nlstate->js.ps.plan = (Plan *) node;
292 nlstate->js.ps.state = estate;
295 * Miscellaneous initialization
297 * create expression context for node
299 ExecAssignExprContext(estate, &nlstate->js.ps);
302 * initialize child expressions
304 nlstate->js.ps.targetlist = (List *)
305 ExecInitExpr((Expr *) node->join.plan.targetlist,
306 (PlanState *) nlstate);
307 nlstate->js.ps.qual = (List *)
308 ExecInitExpr((Expr *) node->join.plan.qual,
309 (PlanState *) nlstate);
310 nlstate->js.jointype = node->join.jointype;
311 nlstate->js.joinqual = (List *)
312 ExecInitExpr((Expr *) node->join.joinqual,
313 (PlanState *) nlstate);
316 * initialize child nodes
318 * Tell the inner child that cheap rescans would be good. (This is
319 * unnecessary if we are doing nestloop with inner indexscan, because the
320 * rescan will always be with a fresh parameter --- but since
321 * nodeIndexscan doesn't actually care about REWIND, there's no point in
322 * dealing with that refinement.)
324 outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
325 innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate,
326 eflags | EXEC_FLAG_REWIND);
328 #define NESTLOOP_NSLOTS 2
331 * tuple table initialization
333 ExecInitResultTupleSlot(estate, &nlstate->js.ps);
335 switch (node->join.jointype)
342 nlstate->nl_NullInnerTupleSlot =
343 ExecInitNullTupleSlot(estate,
344 ExecGetResultType(innerPlanState(nlstate)));
347 elog(ERROR, "unrecognized join type: %d",
348 (int) node->join.jointype);
352 * initialize tuple type and projection info
354 ExecAssignResultTypeFromTL(&nlstate->js.ps);
355 ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
358 * finally, wipe the current outer tuple clean.
360 nlstate->js.ps.ps_OuterTupleSlot = NULL;
361 nlstate->js.ps.ps_TupFromTlist = false;
362 nlstate->nl_NeedNewOuter = true;
363 nlstate->nl_MatchedOuter = false;
365 NL1_printf("ExecInitNestLoop: %s\n",
372 ExecCountSlotsNestLoop(NestLoop *node)
374 return ExecCountSlotsNode(outerPlan(node)) +
375 ExecCountSlotsNode(innerPlan(node)) +
379 /* ----------------------------------------------------------------
382 * closes down scans and frees allocated storage
383 * ----------------------------------------------------------------
386 ExecEndNestLoop(NestLoopState *node)
388 NL1_printf("ExecEndNestLoop: %s\n",
389 "ending node processing");
392 * Free the exprcontext
394 ExecFreeExprContext(&node->js.ps);
397 * clean out the tuple table
399 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
402 * close down subplans
404 ExecEndNode(outerPlanState(node));
405 ExecEndNode(innerPlanState(node));
407 NL1_printf("ExecEndNestLoop: %s\n",
408 "node processing ended");
411 /* ----------------------------------------------------------------
413 * ----------------------------------------------------------------
416 ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt)
418 PlanState *outerPlan = outerPlanState(node);
421 * If outerPlan->chgParam is not null then plan will be automatically
422 * re-scanned by first ExecProcNode. innerPlan is re-scanned for each new
423 * outer tuple and MUST NOT be re-scanned from here or you'll get troubles
424 * from inner index scans when outer Vars are used as run-time keys...
426 if (outerPlan->chgParam == NULL)
427 ExecReScan(outerPlan, exprCtxt);
429 /* let outerPlan to free its result tuple ... */
430 node->js.ps.ps_OuterTupleSlot = NULL;
431 node->js.ps.ps_TupFromTlist = false;
432 node->nl_NeedNewOuter = true;
433 node->nl_MatchedOuter = false;