Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeNestloop.c
4 : : * routines to support nest-loop joins
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/executor/nodeNestloop.c
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/instrument.h"
26 : : #include "executor/nodeNestloop.h"
27 : : #include "miscadmin.h"
28 : :
29 : :
30 : : /* ----------------------------------------------------------------
31 : : * ExecNestLoop(node)
32 : : *
33 : : * old comments
34 : : * Returns the tuple joined from inner and outer tuples which
35 : : * satisfies the qualification clause.
36 : : *
37 : : * It scans the inner relation to join with current outer tuple.
38 : : *
39 : : * If none is found, next tuple from the outer relation is retrieved
40 : : * and the inner relation is scanned from the beginning again to join
41 : : * with the outer tuple.
42 : : *
43 : : * NULL is returned if all the remaining outer tuples are tried and
44 : : * all fail to join with the inner tuples.
45 : : *
46 : : * NULL is also returned if there is no tuple from inner relation.
47 : : *
48 : : * Conditions:
49 : : * -- outerTuple contains current tuple from outer relation and
50 : : * the right son(inner relation) maintains "cursor" at the tuple
51 : : * returned previously.
52 : : * This is achieved by maintaining a scan position on the outer
53 : : * relation.
54 : : *
55 : : * Initial States:
56 : : * -- the outer child and the inner child
57 : : * are prepared to return the first tuple.
58 : : * ----------------------------------------------------------------
59 : : */
60 : : static TupleTableSlot *
3214 andres@anarazel.de 61 :CBC 1982025 : ExecNestLoop(PlanState *pstate)
62 : : {
63 : 1982025 : NestLoopState *node = castNode(NestLoopState, pstate);
64 : : NestLoop *nl;
65 : : PlanState *innerPlan;
66 : : PlanState *outerPlan;
67 : : TupleTableSlot *outerTupleSlot;
68 : : TupleTableSlot *innerTupleSlot;
69 : : ExprState *joinqual;
70 : : ExprState *otherqual;
71 : : ExprContext *econtext;
72 : : ListCell *lc;
73 : :
3206 74 [ + + ]: 1982025 : CHECK_FOR_INTERRUPTS();
75 : :
76 : : /*
77 : : * get information from the node
78 : : */
79 : : ENL1_printf("getting info from node");
80 : :
5776 tgl@sss.pgh.pa.us 81 : 1982024 : nl = (NestLoop *) node->js.ps.plan;
8552 82 : 1982024 : joinqual = node->js.joinqual;
83 : 1982024 : otherqual = node->js.ps.qual;
84 : 1982024 : outerPlan = outerPlanState(node);
85 : 1982024 : innerPlan = innerPlanState(node);
86 : 1982024 : econtext = node->js.ps.ps_ExprContext;
87 : :
88 : : /*
89 : : * Reset per-tuple memory context to free any expression evaluation
90 : : * storage allocated in the previous tuple cycle.
91 : : */
9385 92 : 1982024 : ResetExprContext(econtext);
93 : :
94 : : /*
95 : : * Ok, everything is setup for the join so now loop until we return a
96 : : * qualifying join tuple.
97 : : */
98 : : ENL1_printf("entering main loop");
99 : :
100 : : for (;;)
101 : : {
102 : : /*
103 : : * If we don't have an outer tuple, get the next one and reset the
104 : : * inner scan.
105 : : */
8552 106 [ + + ]: 7184915 : if (node->nl_NeedNewOuter)
107 : : {
108 : : ENL1_printf("getting new outer tuple");
109 : 1013483 : outerTupleSlot = ExecProcNode(outerPlan);
110 : :
111 : : /*
112 : : * if there are no more outer tuples, then the join is complete..
113 : : */
10467 bruce@momjian.us 114 [ + + + + ]: 1013479 : if (TupIsNull(outerTupleSlot))
115 : : {
116 : : ENL1_printf("no outer tuple, ending join");
117 : 71260 : return NULL;
118 : : }
119 : :
120 : : ENL1_printf("saving new outer tuple information");
9366 tgl@sss.pgh.pa.us 121 : 942219 : econtext->ecxt_outertuple = outerTupleSlot;
8552 122 : 942219 : node->nl_NeedNewOuter = false;
123 : 942219 : node->nl_MatchedOuter = false;
124 : :
125 : : /*
126 : : * fetch the values of any outer Vars that must be passed to the
127 : : * inner scan, and store them in the appropriate PARAM_EXEC slots.
128 : : */
5776 129 [ + + + + : 1930462 : foreach(lc, nl->nestParams)
+ + ]
130 : : {
131 : 988243 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
132 : 988243 : int paramno = nlp->paramno;
133 : : ParamExecData *prm;
134 : :
135 : 988243 : prm = &(econtext->ecxt_param_exec_vals[paramno]);
136 : : /* Param value should be an OUTER_VAR var */
5297 137 [ - + ]: 988243 : Assert(IsA(nlp->paramval, Var));
5320 138 [ - + ]: 988243 : Assert(nlp->paramval->varno == OUTER_VAR);
5776 139 [ - + ]: 988243 : Assert(nlp->paramval->varattno > 0);
140 : 1976486 : prm->value = slot_getattr(outerTupleSlot,
141 : 988243 : nlp->paramval->varattno,
142 : : &(prm->isnull));
143 : : /* Flag parameter value as changed */
144 : 988243 : innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
145 : : paramno);
146 : : }
147 : :
148 : : /*
149 : : * now rescan the inner plan
150 : : */
151 : : ENL1_printf("rescanning inner plan");
152 : 942219 : ExecReScan(innerPlan);
153 : : }
154 : :
155 : : /*
156 : : * we have an outerTuple, try to get the next inner tuple.
157 : : */
158 : : ENL1_printf("getting new inner tuple");
159 : :
8552 160 : 7113651 : innerTupleSlot = ExecProcNode(innerPlan);
9366 161 : 7113617 : econtext->ecxt_innertuple = innerTupleSlot;
162 : :
163 [ + + + + ]: 7113617 : if (TupIsNull(innerTupleSlot))
164 : : {
165 : : ENL1_printf("no inner tuple, need new outer tuple");
166 : :
8552 167 : 553543 : node->nl_NeedNewOuter = true;
168 : :
169 [ + + ]: 553543 : if (!node->nl_MatchedOuter &&
6473 170 [ + + ]: 349808 : (node->js.jointype == JOIN_LEFT ||
171 [ + + ]: 335314 : node->js.jointype == JOIN_ANTI))
172 : : {
173 : : /*
174 : : * We are doing an outer join and there were no join matches
175 : : * for this outer tuple. Generate a fake join tuple with
176 : : * nulls for the inner tuple, and return it if it passes the
177 : : * non-join quals.
178 : : */
8552 179 : 58279 : econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
180 : :
181 : : ENL1_printf("testing qualification for outer-join tuple");
182 : :
3339 andres@anarazel.de 183 [ + + + + ]: 58279 : if (otherqual == NULL || ExecQual(otherqual, econtext))
184 : : {
185 : : /*
186 : : * qualification was satisfied so we project and return
187 : : * the slot containing the result tuple using
188 : : * ExecProject().
189 : : */
190 : : ENL1_printf("qualification succeeded, projecting tuple");
191 : :
3393 192 : 57305 : return ExecProject(node->js.ps.ps_ProjInfo);
193 : : }
194 : : else
5339 tgl@sss.pgh.pa.us 195 [ - + ]: 974 : InstrCountFiltered2(node, 1);
196 : : }
197 : :
198 : : /*
199 : : * Otherwise just return to top of loop for a new outer tuple.
200 : : */
9366 201 : 496238 : continue;
202 : : }
203 : :
204 : : /*
205 : : * at this point we have a new pair of inner and outer tuples so we
206 : : * test the inner and outer tuples to see if they satisfy the node's
207 : : * qualification.
208 : : *
209 : : * Only the joinquals determine MatchedOuter status, but all quals
210 : : * must pass to actually return the tuple.
211 : : */
212 : : ENL1_printf("testing qualification");
213 : :
3339 andres@anarazel.de 214 [ + + ]: 6560074 : if (ExecQual(joinqual, econtext))
215 : : {
8552 tgl@sss.pgh.pa.us 216 : 2070202 : node->nl_MatchedOuter = true;
217 : :
218 : : /* In an antijoin, we never return a matched tuple */
6473 219 [ + + ]: 2070202 : if (node->js.jointype == JOIN_ANTI)
220 : : {
221 : 209708 : node->nl_NeedNewOuter = true;
6472 222 : 209708 : continue; /* return to top of loop */
223 : : }
224 : :
225 : : /*
226 : : * If we only need to join to the first matching inner tuple, then
227 : : * consider returning this one, but after that continue with next
228 : : * outer tuple.
229 : : */
3315 230 [ + + ]: 1860494 : if (node->js.single_match)
6472 231 : 178833 : node->nl_NeedNewOuter = true;
232 : :
3339 andres@anarazel.de 233 [ + + + + ]: 1860494 : if (otherqual == NULL || ExecQual(otherqual, econtext))
234 : : {
235 : : /*
236 : : * qualification was satisfied so we project and return the
237 : : * slot containing the result tuple using ExecProject().
238 : : */
239 : : ENL1_printf("qualification succeeded, projecting tuple");
240 : :
3393 241 : 1853421 : return ExecProject(node->js.ps.ps_ProjInfo);
242 : : }
243 : : else
5339 tgl@sss.pgh.pa.us 244 [ - + ]: 7073 : InstrCountFiltered2(node, 1);
245 : : }
246 : : else
247 [ + + ]: 4489872 : InstrCountFiltered1(node, 1);
248 : :
249 : : /*
250 : : * Tuple fails qual, so free per-tuple memory and try again.
251 : : */
9428 252 : 4496945 : ResetExprContext(econtext);
253 : :
254 : : ENL1_printf("qualification failed, looping");
255 : : }
256 : : }
257 : :
258 : : /* ----------------------------------------------------------------
259 : : * ExecInitNestLoop
260 : : * ----------------------------------------------------------------
261 : : */
262 : : NestLoopState *
7371 263 : 69052 : ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
264 : : {
265 : : NestLoopState *nlstate;
266 : :
267 : : /* check for unsupported flags */
268 [ - + ]: 69052 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
269 : :
270 : : NL1_printf("ExecInitNestLoop: %s\n",
271 : : "initializing node");
272 : :
273 : : /*
274 : : * create state structure
275 : : */
10467 bruce@momjian.us 276 : 69052 : nlstate = makeNode(NestLoopState);
8552 tgl@sss.pgh.pa.us 277 : 69052 : nlstate->js.ps.plan = (Plan *) node;
278 : 69052 : nlstate->js.ps.state = estate;
3214 andres@anarazel.de 279 : 69052 : nlstate->js.ps.ExecProcNode = ExecNestLoop;
280 : :
281 : : /*
282 : : * Miscellaneous initialization
283 : : *
284 : : * create expression context for node
285 : : */
8552 tgl@sss.pgh.pa.us 286 : 69052 : ExecAssignExprContext(estate, &nlstate->js.ps);
287 : :
288 : : /*
289 : : * initialize child nodes
290 : : *
291 : : * If we have no parameters to pass into the inner rel from the outer,
292 : : * tell the inner child that cheap rescans would be good. If we do have
293 : : * such parameters, then there is no point in REWIND support at all in the
294 : : * inner child, because it will always be rescanned with fresh parameter
295 : : * values.
296 : : */
7371 297 : 69052 : outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
5776 298 [ + + ]: 69052 : if (node->nestParams == NIL)
299 : 36562 : eflags |= EXEC_FLAG_REWIND;
300 : : else
301 : 32490 : eflags &= ~EXEC_FLAG_REWIND;
302 : 69052 : innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags);
303 : :
304 : : /*
305 : : * Initialize result slot, type and projection.
306 : : */
2728 andres@anarazel.de 307 : 69052 : ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
3000 308 : 69052 : ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
309 : :
310 : : /*
311 : : * initialize child expressions
312 : : */
313 : 69052 : nlstate->js.ps.qual =
314 : 69052 : ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
315 : 69052 : nlstate->js.jointype = node->join.jointype;
316 : 69052 : nlstate->js.joinqual =
317 : 69052 : ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
318 : :
319 : : /*
320 : : * detect whether we need only consider the first matching inner tuple
321 : : */
3315 tgl@sss.pgh.pa.us 322 [ + + ]: 98493 : nlstate->js.single_match = (node->join.inner_unique ||
323 [ + + ]: 29441 : node->join.jointype == JOIN_SEMI);
324 : :
325 : : /* set up null tuples for outer joins, if needed */
9366 326 [ + + - ]: 69052 : switch (node->join.jointype)
327 : : {
328 : 52744 : case JOIN_INNER:
329 : : case JOIN_SEMI:
330 : 52744 : break;
331 : 16308 : case JOIN_LEFT:
332 : : case JOIN_ANTI:
333 : 16308 : nlstate->nl_NullInnerTupleSlot =
334 : 16308 : ExecInitNullTupleSlot(estate,
2728 andres@anarazel.de 335 :ECB (11300) : ExecGetResultType(innerPlanState(nlstate)),
336 : : &TTSOpsVirtual);
9366 tgl@sss.pgh.pa.us 337 :CBC 16308 : break;
9366 tgl@sss.pgh.pa.us 338 :UBC 0 : default:
8324 339 [ # # ]: 0 : elog(ERROR, "unrecognized join type: %d",
340 : : (int) node->join.jointype);
341 : : }
342 : :
343 : : /*
344 : : * finally, wipe the current outer tuple clean.
345 : : */
9366 tgl@sss.pgh.pa.us 346 :CBC 69052 : nlstate->nl_NeedNewOuter = true;
347 : 69052 : nlstate->nl_MatchedOuter = false;
348 : :
349 : : NL1_printf("ExecInitNestLoop: %s\n",
350 : : "node initialized");
351 : :
8552 352 : 69052 : return nlstate;
353 : : }
354 : :
355 : : /* ----------------------------------------------------------------
356 : : * ExecEndNestLoop
357 : : *
358 : : * closes down scans and frees allocated storage
359 : : * ----------------------------------------------------------------
360 : : */
361 : : void
362 : 68906 : ExecEndNestLoop(NestLoopState *node)
363 : : {
364 : : NL1_printf("ExecEndNestLoop: %s\n",
365 : : "ending node processing");
366 : :
367 : : /*
368 : : * close down subplans
369 : : */
8542 370 : 68906 : ExecEndNode(outerPlanState(node));
371 : 68906 : ExecEndNode(innerPlanState(node));
372 : :
373 : : NL1_printf("ExecEndNestLoop: %s\n",
374 : : "node processing ended");
10892 scrappy@hub.org 375 : 68906 : }
376 : :
377 : : /* ----------------------------------------------------------------
378 : : * ExecReScanNestLoop
379 : : * ----------------------------------------------------------------
380 : : */
381 : : void
5776 tgl@sss.pgh.pa.us 382 : 9152 : ExecReScanNestLoop(NestLoopState *node)
383 : : {
8310 bruce@momjian.us 384 : 9152 : PlanState *outerPlan = outerPlanState(node);
385 : :
386 : : /*
387 : : * If outerPlan->chgParam is not null then plan will be automatically
388 : : * re-scanned by first ExecProcNode.
389 : : */
10308 vadim4o@yahoo.com 390 [ + + ]: 9152 : if (outerPlan->chgParam == NULL)
5776 tgl@sss.pgh.pa.us 391 : 185 : ExecReScan(outerPlan);
392 : :
393 : : /*
394 : : * innerPlan is re-scanned for each new outer tuple and MUST NOT be
395 : : * re-scanned from here or you'll get troubles from inner index scans when
396 : : * outer Vars are used as run-time keys...
397 : : */
398 : :
8552 399 : 9152 : node->nl_NeedNewOuter = true;
400 : 9152 : node->nl_MatchedOuter = false;
10308 vadim4o@yahoo.com 401 : 9152 : }
|