1 /*-------------------------------------------------------------------------
4 * routines to support nest-loop joins
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.30 2003/01/20 18:54:46 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
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 on
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);
260 * Tuple fails qual, so free per-tuple memory and try again.
262 ResetExprContext(econtext);
264 ENL1_printf("qualification failed, looping");
268 /* ----------------------------------------------------------------
270 * ----------------------------------------------------------------
273 ExecInitNestLoop(NestLoop *node, EState *estate)
275 NestLoopState *nlstate;
277 NL1_printf("ExecInitNestLoop: %s\n",
278 "initializing node");
281 * create state structure
283 nlstate = makeNode(NestLoopState);
284 nlstate->js.ps.plan = (Plan *) node;
285 nlstate->js.ps.state = estate;
288 * Miscellaneous initialization
290 * create expression context for node
292 ExecAssignExprContext(estate, &nlstate->js.ps);
295 * initialize child expressions
297 nlstate->js.ps.targetlist = (List *)
298 ExecInitExpr((Expr *) node->join.plan.targetlist,
299 (PlanState *) nlstate);
300 nlstate->js.ps.qual = (List *)
301 ExecInitExpr((Expr *) node->join.plan.qual,
302 (PlanState *) nlstate);
303 nlstate->js.jointype = node->join.jointype;
304 nlstate->js.joinqual = (List *)
305 ExecInitExpr((Expr *) node->join.joinqual,
306 (PlanState *) nlstate);
309 * initialize child nodes
311 outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate);
312 innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate);
314 #define NESTLOOP_NSLOTS 2
317 * tuple table initialization
319 ExecInitResultTupleSlot(estate, &nlstate->js.ps);
321 switch (node->join.jointype)
327 nlstate->nl_NullInnerTupleSlot =
328 ExecInitNullTupleSlot(estate,
329 ExecGetTupType(innerPlanState(nlstate)));
332 elog(ERROR, "ExecInitNestLoop: unsupported join type %d",
333 (int) node->join.jointype);
337 * initialize tuple type and projection info
339 ExecAssignResultTypeFromTL(&nlstate->js.ps);
340 ExecAssignProjectionInfo(&nlstate->js.ps);
343 * finally, wipe the current outer tuple clean.
345 nlstate->js.ps.ps_OuterTupleSlot = NULL;
346 nlstate->js.ps.ps_TupFromTlist = false;
347 nlstate->nl_NeedNewOuter = true;
348 nlstate->nl_MatchedOuter = false;
350 NL1_printf("ExecInitNestLoop: %s\n",
357 ExecCountSlotsNestLoop(NestLoop *node)
359 return ExecCountSlotsNode(outerPlan(node)) +
360 ExecCountSlotsNode(innerPlan(node)) +
364 /* ----------------------------------------------------------------
367 * closes down scans and frees allocated storage
368 * ----------------------------------------------------------------
371 ExecEndNestLoop(NestLoopState *node)
373 NL1_printf("ExecEndNestLoop: %s\n",
374 "ending node processing");
377 * Free the exprcontext
379 ExecFreeExprContext(&node->js.ps);
382 * clean out the tuple table
384 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
387 * close down subplans
389 ExecEndNode(outerPlanState(node));
390 ExecEndNode(innerPlanState(node));
392 NL1_printf("ExecEndNestLoop: %s\n",
393 "node processing ended");
396 /* ----------------------------------------------------------------
398 * ----------------------------------------------------------------
401 ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt)
403 PlanState *outerPlan = outerPlanState(node);
406 * If outerPlan->chgParam is not null then plan will be automatically
407 * re-scanned by first ExecProcNode. innerPlan is re-scanned for each
408 * new outer tuple and MUST NOT be re-scanned from here or you'll get
409 * troubles from inner index scans when outer Vars are used as
412 if (outerPlan->chgParam == NULL)
413 ExecReScan(outerPlan, exprCtxt);
415 /* let outerPlan to free its result tuple ... */
416 node->js.ps.ps_OuterTupleSlot = NULL;
417 node->js.ps.ps_TupFromTlist = false;
418 node->nl_NeedNewOuter = true;
419 node->nl_MatchedOuter = false;