1 /*-------------------------------------------------------------------------
4 * routines to support nest-loop joins
6 * Portions Copyright (c) 1996-2003, 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.36 2003/11/29 19:51:48 pgsql 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
89 * join tuple (because there is a function-returning-set in the
90 * projection 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
106 * outer tuple; so we can stop scanning the inner scan if we matched
107 * on the previous try.
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
116 * happen 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
141 if (TupIsNull(outerTupleSlot))
143 ENL1_printf("no outer tuple, ending join");
147 ENL1_printf("saving new outer tuple information");
148 node->js.ps.ps_OuterTupleSlot = outerTupleSlot;
149 econtext->ecxt_outertuple = outerTupleSlot;
150 node->nl_NeedNewOuter = false;
151 node->nl_MatchedOuter = false;
154 * now rescan the inner plan
156 ENL1_printf("rescanning inner plan");
159 * The scan key of the inner plan might depend on the current
160 * outer tuple (e.g. in index scans), that's why we pass our
163 ExecReScan(innerPlan, econtext);
167 * we have an outerTuple, try to get the next inner tuple.
169 ENL1_printf("getting new inner tuple");
171 innerTupleSlot = ExecProcNode(innerPlan);
172 econtext->ecxt_innertuple = innerTupleSlot;
174 if (TupIsNull(innerTupleSlot))
176 ENL1_printf("no inner tuple, need new outer tuple");
178 node->nl_NeedNewOuter = true;
180 if (!node->nl_MatchedOuter &&
181 node->js.jointype == JOIN_LEFT)
184 * We are doing an outer join and there were no join
185 * matches for this outer tuple. Generate a fake join
186 * tuple with nulls for the inner tuple, and return it if
187 * it passes the non-join quals.
189 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
191 ENL1_printf("testing qualification for outer-join tuple");
193 if (ExecQual(otherqual, econtext, false))
196 * qualification was satisfied so we project and
197 * return the slot containing the result tuple using
200 TupleTableSlot *result;
203 ENL1_printf("qualification succeeded, projecting tuple");
205 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
207 if (isDone != ExprEndResult)
209 node->js.ps.ps_TupFromTlist =
210 (isDone == ExprMultipleResult);
217 * Otherwise just return to top of loop for a new outer tuple.
223 * at this point we have a new pair of inner and outer tuples so
224 * we test the inner and outer tuples to see if they satisfy the
225 * node's qualification.
227 * Only the joinquals determine MatchedOuter status, but all quals
228 * must pass to actually return the tuple.
230 ENL1_printf("testing qualification");
232 if (ExecQual(joinqual, econtext, false))
234 node->nl_MatchedOuter = true;
236 if (otherqual == NIL || ExecQual(otherqual, econtext, false))
239 * qualification was satisfied so we project and return
240 * the slot containing the result tuple using
243 TupleTableSlot *result;
246 ENL1_printf("qualification succeeded, projecting tuple");
248 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
250 if (isDone != ExprEndResult)
252 node->js.ps.ps_TupFromTlist =
253 (isDone == ExprMultipleResult);
258 /* If we didn't return a tuple, may need to set NeedNewOuter */
259 if (node->js.jointype == JOIN_IN)
260 node->nl_NeedNewOuter = true;
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)
279 NestLoopState *nlstate;
281 NL1_printf("ExecInitNestLoop: %s\n",
282 "initializing node");
285 * create state structure
287 nlstate = makeNode(NestLoopState);
288 nlstate->js.ps.plan = (Plan *) node;
289 nlstate->js.ps.state = estate;
292 * Miscellaneous initialization
294 * create expression context for node
296 ExecAssignExprContext(estate, &nlstate->js.ps);
299 * initialize child expressions
301 nlstate->js.ps.targetlist = (List *)
302 ExecInitExpr((Expr *) node->join.plan.targetlist,
303 (PlanState *) nlstate);
304 nlstate->js.ps.qual = (List *)
305 ExecInitExpr((Expr *) node->join.plan.qual,
306 (PlanState *) nlstate);
307 nlstate->js.jointype = node->join.jointype;
308 nlstate->js.joinqual = (List *)
309 ExecInitExpr((Expr *) node->join.joinqual,
310 (PlanState *) nlstate);
313 * initialize child nodes
315 outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate);
316 innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate);
318 #define NESTLOOP_NSLOTS 2
321 * tuple table initialization
323 ExecInitResultTupleSlot(estate, &nlstate->js.ps);
325 switch (node->join.jointype)
331 nlstate->nl_NullInnerTupleSlot =
332 ExecInitNullTupleSlot(estate,
333 ExecGetResultType(innerPlanState(nlstate)));
336 elog(ERROR, "unrecognized join type: %d",
337 (int) node->join.jointype);
341 * initialize tuple type and projection info
343 ExecAssignResultTypeFromTL(&nlstate->js.ps);
344 ExecAssignProjectionInfo(&nlstate->js.ps);
347 * finally, wipe the current outer tuple clean.
349 nlstate->js.ps.ps_OuterTupleSlot = NULL;
350 nlstate->js.ps.ps_TupFromTlist = false;
351 nlstate->nl_NeedNewOuter = true;
352 nlstate->nl_MatchedOuter = false;
354 NL1_printf("ExecInitNestLoop: %s\n",
361 ExecCountSlotsNestLoop(NestLoop *node)
363 return ExecCountSlotsNode(outerPlan(node)) +
364 ExecCountSlotsNode(innerPlan(node)) +
368 /* ----------------------------------------------------------------
371 * closes down scans and frees allocated storage
372 * ----------------------------------------------------------------
375 ExecEndNestLoop(NestLoopState *node)
377 NL1_printf("ExecEndNestLoop: %s\n",
378 "ending node processing");
381 * Free the exprcontext
383 ExecFreeExprContext(&node->js.ps);
386 * clean out the tuple table
388 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
391 * close down subplans
393 ExecEndNode(outerPlanState(node));
394 ExecEndNode(innerPlanState(node));
396 NL1_printf("ExecEndNestLoop: %s\n",
397 "node processing ended");
400 /* ----------------------------------------------------------------
402 * ----------------------------------------------------------------
405 ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt)
407 PlanState *outerPlan = outerPlanState(node);
410 * If outerPlan->chgParam is not null then plan will be automatically
411 * re-scanned by first ExecProcNode. innerPlan is re-scanned for each
412 * new outer tuple and MUST NOT be re-scanned from here or you'll get
413 * troubles from inner index scans when outer Vars are used as
416 if (outerPlan->chgParam == NULL)
417 ExecReScan(outerPlan, exprCtxt);
419 /* let outerPlan to free its result tuple ... */
420 node->js.ps.ps_OuterTupleSlot = NULL;
421 node->js.ps.ps_TupFromTlist = false;
422 node->nl_NeedNewOuter = true;
423 node->nl_MatchedOuter = false;