1
/*-------------------------------------------------------------------------
4
* routines to support nest-loop joins
6
* Portions Copyright (c) 1996-2005, 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.38 2004-12-31 21:59:45 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;