]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeNestloop.c
78216212ff58437fa421ffa9b870f12537843d86
[postgresql] / src / backend / executor / nodeNestloop.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeNestloop.c
4  *        routines to support nest-loop joins
5  *
6  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.46 2008/01/01 19:45:49 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 /*
16  *       INTERFACE ROUTINES
17  *              ExecNestLoop     - process a nestloop join of two plans
18  *              ExecInitNestLoop - initialize the join
19  *              ExecEndNestLoop  - shut down the join
20  */
21
22 #include "postgres.h"
23
24 #include "executor/execdebug.h"
25 #include "executor/nodeNestloop.h"
26 #include "utils/memutils.h"
27
28
29 /* ----------------------------------------------------------------
30  *              ExecNestLoop(node)
31  *
32  * old comments
33  *              Returns the tuple joined from inner and outer tuples which
34  *              satisfies the qualification clause.
35  *
36  *              It scans the inner relation to join with current outer tuple.
37  *
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.
41  *
42  *              NULL is returned if all the remaining outer tuples are tried and
43  *              all fail to join with the inner tuples.
44  *
45  *              NULL is also returned if there is no tuple from inner relation.
46  *
47  *              Conditions:
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
52  *                              relation.
53  *
54  *              Initial States:
55  *                -- the outer child and the inner child
56  *                         are prepared to return the first tuple.
57  * ----------------------------------------------------------------
58  */
59 TupleTableSlot *
60 ExecNestLoop(NestLoopState *node)
61 {
62         PlanState  *innerPlan;
63         PlanState  *outerPlan;
64         TupleTableSlot *outerTupleSlot;
65         TupleTableSlot *innerTupleSlot;
66         List       *joinqual;
67         List       *otherqual;
68         ExprContext *econtext;
69
70         /*
71          * get information from the node
72          */
73         ENL1_printf("getting info from node");
74
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;
80
81         /*
82          * get the current outer tuple
83          */
84         outerTupleSlot = node->js.ps.ps_OuterTupleSlot;
85         econtext->ecxt_outertuple = outerTupleSlot;
86
87         /*
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.
91          */
92         if (node->js.ps.ps_TupFromTlist)
93         {
94                 TupleTableSlot *result;
95                 ExprDoneCond isDone;
96
97                 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
98                 if (isDone == ExprMultipleResult)
99                         return result;
100                 /* Done with that source tuple... */
101                 node->js.ps.ps_TupFromTlist = false;
102         }
103
104         /*
105          * If we're doing an IN join, we want to return at most one row per outer
106          * tuple; so we can stop scanning the inner scan if we matched on the
107          * previous try.
108          */
109         if (node->js.jointype == JOIN_IN &&
110                 node->nl_MatchedOuter)
111                 node->nl_NeedNewOuter = true;
112
113         /*
114          * Reset per-tuple memory context to free any expression evaluation
115          * storage allocated in the previous tuple cycle.  Note this can't happen
116          * until we're done projecting out tuples from a join tuple.
117          */
118         ResetExprContext(econtext);
119
120         /*
121          * Ok, everything is setup for the join so now loop until we return a
122          * qualifying join tuple.
123          */
124         ENL1_printf("entering main loop");
125
126         for (;;)
127         {
128                 /*
129                  * If we don't have an outer tuple, get the next one and reset the
130                  * inner scan.
131                  */
132                 if (node->nl_NeedNewOuter)
133                 {
134                         ENL1_printf("getting new outer tuple");
135                         outerTupleSlot = ExecProcNode(outerPlan);
136
137                         /*
138                          * if there are no more outer tuples, then the join is complete..
139                          */
140                         if (TupIsNull(outerTupleSlot))
141                         {
142                                 ENL1_printf("no outer tuple, ending join");
143                                 return NULL;
144                         }
145
146                         ENL1_printf("saving new outer tuple information");
147                         node->js.ps.ps_OuterTupleSlot = outerTupleSlot;
148                         econtext->ecxt_outertuple = outerTupleSlot;
149                         node->nl_NeedNewOuter = false;
150                         node->nl_MatchedOuter = false;
151
152                         /*
153                          * now rescan the inner plan
154                          */
155                         ENL1_printf("rescanning inner plan");
156
157                         /*
158                          * The scan key of the inner plan might depend on the current
159                          * outer tuple (e.g. in index scans), that's why we pass our expr
160                          * context.
161                          */
162                         ExecReScan(innerPlan, econtext);
163                 }
164
165                 /*
166                  * we have an outerTuple, try to get the next inner tuple.
167                  */
168                 ENL1_printf("getting new inner tuple");
169
170                 innerTupleSlot = ExecProcNode(innerPlan);
171                 econtext->ecxt_innertuple = innerTupleSlot;
172
173                 if (TupIsNull(innerTupleSlot))
174                 {
175                         ENL1_printf("no inner tuple, need new outer tuple");
176
177                         node->nl_NeedNewOuter = true;
178
179                         if (!node->nl_MatchedOuter &&
180                                 node->js.jointype == JOIN_LEFT)
181                         {
182                                 /*
183                                  * We are doing an outer join and there were no join matches
184                                  * for this outer tuple.  Generate a fake join tuple with
185                                  * nulls for the inner tuple, and return it if it passes the
186                                  * non-join quals.
187                                  */
188                                 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
189
190                                 ENL1_printf("testing qualification for outer-join tuple");
191
192                                 if (ExecQual(otherqual, econtext, false))
193                                 {
194                                         /*
195                                          * qualification was satisfied so we project and return
196                                          * the slot containing the result tuple using
197                                          * ExecProject().
198                                          */
199                                         TupleTableSlot *result;
200                                         ExprDoneCond isDone;
201
202                                         ENL1_printf("qualification succeeded, projecting tuple");
203
204                                         result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
205
206                                         if (isDone != ExprEndResult)
207                                         {
208                                                 node->js.ps.ps_TupFromTlist =
209                                                         (isDone == ExprMultipleResult);
210                                                 return result;
211                                         }
212                                 }
213                         }
214
215                         /*
216                          * Otherwise just return to top of loop for a new outer tuple.
217                          */
218                         continue;
219                 }
220
221                 /*
222                  * at this point we have a new pair of inner and outer tuples so we
223                  * test the inner and outer tuples to see if they satisfy the node's
224                  * qualification.
225                  *
226                  * Only the joinquals determine MatchedOuter status, but all quals
227                  * must pass to actually return the tuple.
228                  */
229                 ENL1_printf("testing qualification");
230
231                 if (ExecQual(joinqual, econtext, false))
232                 {
233                         node->nl_MatchedOuter = true;
234
235                         if (otherqual == NIL || ExecQual(otherqual, econtext, false))
236                         {
237                                 /*
238                                  * qualification was satisfied so we project and return the
239                                  * slot containing the result tuple using ExecProject().
240                                  */
241                                 TupleTableSlot *result;
242                                 ExprDoneCond isDone;
243
244                                 ENL1_printf("qualification succeeded, projecting tuple");
245
246                                 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
247
248                                 if (isDone != ExprEndResult)
249                                 {
250                                         node->js.ps.ps_TupFromTlist =
251                                                 (isDone == ExprMultipleResult);
252                                         return result;
253                                 }
254                         }
255
256                         /* If we didn't return a tuple, may need to set NeedNewOuter */
257                         if (node->js.jointype == JOIN_IN)
258                                 node->nl_NeedNewOuter = true;
259                 }
260
261                 /*
262                  * Tuple fails qual, so free per-tuple memory and try again.
263                  */
264                 ResetExprContext(econtext);
265
266                 ENL1_printf("qualification failed, looping");
267         }
268 }
269
270 /* ----------------------------------------------------------------
271  *              ExecInitNestLoop
272  * ----------------------------------------------------------------
273  */
274 NestLoopState *
275 ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
276 {
277         NestLoopState *nlstate;
278
279         /* check for unsupported flags */
280         Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
281
282         NL1_printf("ExecInitNestLoop: %s\n",
283                            "initializing node");
284
285         /*
286          * create state structure
287          */
288         nlstate = makeNode(NestLoopState);
289         nlstate->js.ps.plan = (Plan *) node;
290         nlstate->js.ps.state = estate;
291
292         /*
293          * Miscellaneous initialization
294          *
295          * create expression context for node
296          */
297         ExecAssignExprContext(estate, &nlstate->js.ps);
298
299         /*
300          * initialize child expressions
301          */
302         nlstate->js.ps.targetlist = (List *)
303                 ExecInitExpr((Expr *) node->join.plan.targetlist,
304                                          (PlanState *) nlstate);
305         nlstate->js.ps.qual = (List *)
306                 ExecInitExpr((Expr *) node->join.plan.qual,
307                                          (PlanState *) nlstate);
308         nlstate->js.jointype = node->join.jointype;
309         nlstate->js.joinqual = (List *)
310                 ExecInitExpr((Expr *) node->join.joinqual,
311                                          (PlanState *) nlstate);
312
313         /*
314          * initialize child nodes
315          *
316          * Tell the inner child that cheap rescans would be good.  (This is
317          * unnecessary if we are doing nestloop with inner indexscan, because the
318          * rescan will always be with a fresh parameter --- but since
319          * nodeIndexscan doesn't actually care about REWIND, there's no point in
320          * dealing with that refinement.)
321          */
322         outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
323         innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate,
324                                                                                    eflags | EXEC_FLAG_REWIND);
325
326 #define NESTLOOP_NSLOTS 2
327
328         /*
329          * tuple table initialization
330          */
331         ExecInitResultTupleSlot(estate, &nlstate->js.ps);
332
333         switch (node->join.jointype)
334         {
335                 case JOIN_INNER:
336                 case JOIN_IN:
337                         break;
338                 case JOIN_LEFT:
339                         nlstate->nl_NullInnerTupleSlot =
340                                 ExecInitNullTupleSlot(estate,
341                                                                  ExecGetResultType(innerPlanState(nlstate)));
342                         break;
343                 default:
344                         elog(ERROR, "unrecognized join type: %d",
345                                  (int) node->join.jointype);
346         }
347
348         /*
349          * initialize tuple type and projection info
350          */
351         ExecAssignResultTypeFromTL(&nlstate->js.ps);
352         ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
353
354         /*
355          * finally, wipe the current outer tuple clean.
356          */
357         nlstate->js.ps.ps_OuterTupleSlot = NULL;
358         nlstate->js.ps.ps_TupFromTlist = false;
359         nlstate->nl_NeedNewOuter = true;
360         nlstate->nl_MatchedOuter = false;
361
362         NL1_printf("ExecInitNestLoop: %s\n",
363                            "node initialized");
364
365         return nlstate;
366 }
367
368 int
369 ExecCountSlotsNestLoop(NestLoop *node)
370 {
371         return ExecCountSlotsNode(outerPlan(node)) +
372                 ExecCountSlotsNode(innerPlan(node)) +
373                 NESTLOOP_NSLOTS;
374 }
375
376 /* ----------------------------------------------------------------
377  *              ExecEndNestLoop
378  *
379  *              closes down scans and frees allocated storage
380  * ----------------------------------------------------------------
381  */
382 void
383 ExecEndNestLoop(NestLoopState *node)
384 {
385         NL1_printf("ExecEndNestLoop: %s\n",
386                            "ending node processing");
387
388         /*
389          * Free the exprcontext
390          */
391         ExecFreeExprContext(&node->js.ps);
392
393         /*
394          * clean out the tuple table
395          */
396         ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
397
398         /*
399          * close down subplans
400          */
401         ExecEndNode(outerPlanState(node));
402         ExecEndNode(innerPlanState(node));
403
404         NL1_printf("ExecEndNestLoop: %s\n",
405                            "node processing ended");
406 }
407
408 /* ----------------------------------------------------------------
409  *              ExecReScanNestLoop
410  * ----------------------------------------------------------------
411  */
412 void
413 ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt)
414 {
415         PlanState  *outerPlan = outerPlanState(node);
416
417         /*
418          * If outerPlan->chgParam is not null then plan will be automatically
419          * re-scanned by first ExecProcNode. innerPlan is re-scanned for each new
420          * outer tuple and MUST NOT be re-scanned from here or you'll get troubles
421          * from inner index scans when outer Vars are used as run-time keys...
422          */
423         if (outerPlan->chgParam == NULL)
424                 ExecReScan(outerPlan, exprCtxt);
425
426         /* let outerPlan to free its result tuple ... */
427         node->js.ps.ps_OuterTupleSlot = NULL;
428         node->js.ps.ps_TupFromTlist = false;
429         node->nl_NeedNewOuter = true;
430         node->nl_MatchedOuter = false;
431 }