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
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 * Check to see if we're still projecting out tuples from a previous join
83 * tuple (because there is a function-returning-set in the projection
84 * expressions). If so, try to project another one.
86 if (node
->js
.ps
.ps_TupFromTlist
)
88 TupleTableSlot
*result
;
91 result
= ExecProject(node
->js
.ps
.ps_ProjInfo
, &isDone
);
92 if (isDone
== ExprMultipleResult
)
94 /* Done with that source tuple... */
95 node
->js
.ps
.ps_TupFromTlist
= false;
99 * Reset per-tuple memory context to free any expression evaluation
100 * storage allocated in the previous tuple cycle. Note this can't happen
101 * until we're done projecting out tuples from a join tuple.
103 ResetExprContext(econtext
);
106 * Ok, everything is setup for the join so now loop until we return a
107 * qualifying join tuple.
109 ENL1_printf("entering main loop");
114 * If we don't have an outer tuple, get the next one and reset the
117 if (node
->nl_NeedNewOuter
)
119 ENL1_printf("getting new outer tuple");
120 outerTupleSlot
= ExecProcNode(outerPlan
);
123 * if there are no more outer tuples, then the join is complete..
125 if (TupIsNull(outerTupleSlot
))
127 ENL1_printf("no outer tuple, ending join");
131 ENL1_printf("saving new outer tuple information");
132 econtext
->ecxt_outertuple
= outerTupleSlot
;
133 node
->nl_NeedNewOuter
= false;
134 node
->nl_MatchedOuter
= false;
137 * now rescan the inner plan
139 ENL1_printf("rescanning inner plan");
142 * The scan key of the inner plan might depend on the current
143 * outer tuple (e.g. in index scans), that's why we pass our expr
146 ExecReScan(innerPlan
, econtext
);
150 * we have an outerTuple, try to get the next inner tuple.
152 ENL1_printf("getting new inner tuple");
154 innerTupleSlot
= ExecProcNode(innerPlan
);
155 econtext
->ecxt_innertuple
= innerTupleSlot
;
157 if (TupIsNull(innerTupleSlot
))
159 ENL1_printf("no inner tuple, need new outer tuple");
161 node
->nl_NeedNewOuter
= true;
163 if (!node
->nl_MatchedOuter
&&
164 (node
->js
.jointype
== JOIN_LEFT
||
165 node
->js
.jointype
== JOIN_ANTI
))
168 * We are doing an outer join and there were no join matches
169 * for this outer tuple. Generate a fake join tuple with
170 * nulls for the inner tuple, and return it if it passes the
173 econtext
->ecxt_innertuple
= node
->nl_NullInnerTupleSlot
;
175 ENL1_printf("testing qualification for outer-join tuple");
177 if (otherqual
== NIL
|| ExecQual(otherqual
, econtext
, false))
180 * qualification was satisfied so we project and return
181 * the slot containing the result tuple using
184 TupleTableSlot
*result
;
187 ENL1_printf("qualification succeeded, projecting tuple");
189 result
= ExecProject(node
->js
.ps
.ps_ProjInfo
, &isDone
);
191 if (isDone
!= ExprEndResult
)
193 node
->js
.ps
.ps_TupFromTlist
=
194 (isDone
== ExprMultipleResult
);
201 * Otherwise just return to top of loop for a new outer tuple.
207 * at this point we have a new pair of inner and outer tuples so we
208 * test the inner and outer tuples to see if they satisfy the node's
211 * Only the joinquals determine MatchedOuter status, but all quals
212 * must pass to actually return the tuple.
214 ENL1_printf("testing qualification");
216 if (ExecQual(joinqual
, econtext
, false))
218 node
->nl_MatchedOuter
= true;
220 /* In an antijoin, we never return a matched tuple */
221 if (node
->js
.jointype
== JOIN_ANTI
)
223 node
->nl_NeedNewOuter
= true;
224 continue; /* return to top of loop */
228 * In a semijoin, we'll consider returning the first match,
229 * but after that we're done with this outer tuple.
231 if (node
->js
.jointype
== JOIN_SEMI
)
232 node
->nl_NeedNewOuter
= true;
234 if (otherqual
== NIL
|| ExecQual(otherqual
, econtext
, false))
237 * qualification was satisfied so we project and return the
238 * slot containing the result tuple using ExecProject().
240 TupleTableSlot
*result
;
243 ENL1_printf("qualification succeeded, projecting tuple");
245 result
= ExecProject(node
->js
.ps
.ps_ProjInfo
, &isDone
);
247 if (isDone
!= ExprEndResult
)
249 node
->js
.ps
.ps_TupFromTlist
=
250 (isDone
== ExprMultipleResult
);
257 * Tuple fails qual, so free per-tuple memory and try again.
259 ResetExprContext(econtext
);
261 ENL1_printf("qualification failed, looping");
265 /* ----------------------------------------------------------------
267 * ----------------------------------------------------------------
270 ExecInitNestLoop(NestLoop
*node
, EState
*estate
, int eflags
)
272 NestLoopState
*nlstate
;
274 /* check for unsupported flags */
275 Assert(!(eflags
& (EXEC_FLAG_BACKWARD
| EXEC_FLAG_MARK
)));
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 * Tell the inner child that cheap rescans would be good. (This is
312 * unnecessary if we are doing nestloop with inner indexscan, because the
313 * rescan will always be with a fresh parameter --- but since
314 * nodeIndexscan doesn't actually care about REWIND, there's no point in
315 * dealing with that refinement.)
317 outerPlanState(nlstate
) = ExecInitNode(outerPlan(node
), estate
, eflags
);
318 innerPlanState(nlstate
) = ExecInitNode(innerPlan(node
), estate
,
319 eflags
| EXEC_FLAG_REWIND
);
321 #define NESTLOOP_NSLOTS 2
324 * tuple table initialization
326 ExecInitResultTupleSlot(estate
, &nlstate
->js
.ps
);
328 switch (node
->join
.jointype
)
335 nlstate
->nl_NullInnerTupleSlot
=
336 ExecInitNullTupleSlot(estate
,
337 ExecGetResultType(innerPlanState(nlstate
)));
340 elog(ERROR
, "unrecognized join type: %d",
341 (int) node
->join
.jointype
);
345 * initialize tuple type and projection info
347 ExecAssignResultTypeFromTL(&nlstate
->js
.ps
);
348 ExecAssignProjectionInfo(&nlstate
->js
.ps
, NULL
);
351 * finally, wipe the current outer tuple clean.
353 nlstate
->js
.ps
.ps_TupFromTlist
= false;
354 nlstate
->nl_NeedNewOuter
= true;
355 nlstate
->nl_MatchedOuter
= false;
357 NL1_printf("ExecInitNestLoop: %s\n",
364 ExecCountSlotsNestLoop(NestLoop
*node
)
366 return ExecCountSlotsNode(outerPlan(node
)) +
367 ExecCountSlotsNode(innerPlan(node
)) +
371 /* ----------------------------------------------------------------
374 * closes down scans and frees allocated storage
375 * ----------------------------------------------------------------
378 ExecEndNestLoop(NestLoopState
*node
)
380 NL1_printf("ExecEndNestLoop: %s\n",
381 "ending node processing");
384 * Free the exprcontext
386 ExecFreeExprContext(&node
->js
.ps
);
389 * clean out the tuple table
391 ExecClearTuple(node
->js
.ps
.ps_ResultTupleSlot
);
394 * close down subplans
396 ExecEndNode(outerPlanState(node
));
397 ExecEndNode(innerPlanState(node
));
399 NL1_printf("ExecEndNestLoop: %s\n",
400 "node processing ended");
403 /* ----------------------------------------------------------------
405 * ----------------------------------------------------------------
408 ExecReScanNestLoop(NestLoopState
*node
, ExprContext
*exprCtxt
)
410 PlanState
*outerPlan
= outerPlanState(node
);
413 * If outerPlan->chgParam is not null then plan will be automatically
414 * re-scanned by first ExecProcNode. innerPlan is re-scanned for each new
415 * outer tuple and MUST NOT be re-scanned from here or you'll get troubles
416 * from inner index scans when outer Vars are used as run-time keys...
418 if (outerPlan
->chgParam
== NULL
)
419 ExecReScan(outerPlan
, exprCtxt
);
421 node
->js
.ps
.ps_TupFromTlist
= false;
422 node
->nl_NeedNewOuter
= true;
423 node
->nl_MatchedOuter
= false;