PostgreSQL Source Code git master
Loading...
Searching...
No Matches
nodeNestloop.c
Go to the documentation of this file.
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"
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 */
60static TupleTableSlot *
62{
63 NestLoopState *node = castNode(NestLoopState, pstate);
64 NestLoop *nl;
69 ExprState *joinqual;
71 ExprContext *econtext;
72 ListCell *lc;
73
75
76 /*
77 * get information from the node
78 */
79 ENL1_printf("getting info from node");
80
81 nl = (NestLoop *) node->js.ps.plan;
82 joinqual = node->js.joinqual;
83 otherqual = node->js.ps.qual;
86 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 */
92 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 */
106 if (node->nl_NeedNewOuter)
107 {
108 ENL1_printf("getting new outer tuple");
110
111 /*
112 * if there are no more outer tuples, then the join is complete..
113 */
115 {
116 ENL1_printf("no outer tuple, ending join");
117 return NULL;
118 }
119
120 ENL1_printf("saving new outer tuple information");
122 node->nl_NeedNewOuter = false;
123 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 */
129 foreach(lc, nl->nestParams)
130 {
132 int paramno = nlp->paramno;
134
135 prm = &(econtext->ecxt_param_exec_vals[paramno]);
136 /* Param value should be an OUTER_VAR var */
137 Assert(IsA(nlp->paramval, Var));
138 Assert(nlp->paramval->varno == OUTER_VAR);
139 Assert(nlp->paramval->varattno > 0);
141 nlp->paramval->varattno,
142 &(prm->isnull));
143 /* Flag parameter value as changed */
144 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");
153 }
154
155 /*
156 * we have an outerTuple, try to get the next inner tuple.
157 */
158 ENL1_printf("getting new inner tuple");
159
162
164 {
165 ENL1_printf("no inner tuple, need new outer tuple");
166
167 node->nl_NeedNewOuter = true;
168
169 if (!node->nl_MatchedOuter &&
170 (node->js.jointype == JOIN_LEFT ||
171 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 */
179 econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
180
181 ENL1_printf("testing qualification for outer-join tuple");
182
183 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
192 return ExecProject(node->js.ps.ps_ProjInfo);
193 }
194 else
195 InstrCountFiltered2(node, 1);
196 }
197
198 /*
199 * Otherwise just return to top of loop for a new outer tuple.
200 */
201 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
214 if (ExecQual(joinqual, econtext))
215 {
216 node->nl_MatchedOuter = true;
217
218 /* In an antijoin, we never return a matched tuple */
219 if (node->js.jointype == JOIN_ANTI)
220 {
221 node->nl_NeedNewOuter = true;
222 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 */
230 if (node->js.single_match)
231 node->nl_NeedNewOuter = true;
232
233 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
241 return ExecProject(node->js.ps.ps_ProjInfo);
242 }
243 else
244 InstrCountFiltered2(node, 1);
245 }
246 else
247 InstrCountFiltered1(node, 1);
248
249 /*
250 * Tuple fails qual, so free per-tuple memory and try again.
251 */
252 ResetExprContext(econtext);
253
254 ENL1_printf("qualification failed, looping");
255 }
256}
257
258/* ----------------------------------------------------------------
259 * ExecInitNestLoop
260 * ----------------------------------------------------------------
261 */
263ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
264{
266
267 /* check for unsupported flags */
269
270 NL1_printf("ExecInitNestLoop: %s\n",
271 "initializing node");
272
273 /*
274 * create state structure
275 */
277 nlstate->js.ps.plan = (Plan *) node;
278 nlstate->js.ps.state = estate;
279 nlstate->js.ps.ExecProcNode = ExecNestLoop;
280
281 /*
282 * Miscellaneous initialization
283 *
284 * create expression context for node
285 */
286 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 */
297 outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
298 if (node->nestParams == NIL)
299 eflags |= EXEC_FLAG_REWIND;
300 else
301 eflags &= ~EXEC_FLAG_REWIND;
302 innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags);
303
304 /*
305 * Initialize result slot, type and projection.
306 */
309
310 /*
311 * initialize child expressions
312 */
313 nlstate->js.ps.qual =
314 ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
315 nlstate->js.jointype = node->join.jointype;
316 nlstate->js.joinqual =
318
319 /*
320 * detect whether we need only consider the first matching inner tuple
321 */
322 nlstate->js.single_match = (node->join.inner_unique ||
323 node->join.jointype == JOIN_SEMI);
324
325 /* set up null tuples for outer joins, if needed */
326 switch (node->join.jointype)
327 {
328 case JOIN_INNER:
329 case JOIN_SEMI:
330 break;
331 case JOIN_LEFT:
332 case JOIN_ANTI:
333 nlstate->nl_NullInnerTupleSlot =
337 break;
338 default:
339 elog(ERROR, "unrecognized join type: %d",
340 (int) node->join.jointype);
341 }
342
343 /*
344 * finally, wipe the current outer tuple clean.
345 */
346 nlstate->nl_NeedNewOuter = true;
347 nlstate->nl_MatchedOuter = false;
348
349 NL1_printf("ExecInitNestLoop: %s\n",
350 "node initialized");
351
352 return nlstate;
353}
354
355/* ----------------------------------------------------------------
356 * ExecEndNestLoop
357 *
358 * closes down scans and frees allocated storage
359 * ----------------------------------------------------------------
360 */
361void
363{
364 NL1_printf("ExecEndNestLoop: %s\n",
365 "ending node processing");
366
367 /*
368 * close down subplans
369 */
372
373 NL1_printf("ExecEndNestLoop: %s\n",
374 "node processing ended");
375}
376
377/* ----------------------------------------------------------------
378 * ExecReScanNestLoop
379 * ----------------------------------------------------------------
380 */
381void
383{
385
386 /*
387 * If outerPlan->chgParam is not null then plan will be automatically
388 * re-scanned by first ExecProcNode.
389 */
390 if (outerPlan->chgParam == NULL)
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
399 node->nl_NeedNewOuter = true;
400 node->nl_MatchedOuter = false;
401}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
#define Assert(condition)
Definition c.h:945
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
void ExecReScan(PlanState *node)
Definition execAmi.c:78
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition execExpr.c:250
void ExecEndNode(PlanState *node)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
TupleTableSlot * ExecInitNullTupleSlot(EState *estate, TupleDesc tupType, const TupleTableSlotOps *tts_ops)
TupleDesc ExecGetResultType(PlanState *planstate)
Definition execUtils.c:500
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition execUtils.c:490
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition execUtils.c:588
#define ENL1_printf(message)
Definition execdebug.h:78
#define NL1_printf(s, a)
Definition execdebug.h:77
#define InstrCountFiltered1(node, delta)
Definition execnodes.h:1281
#define outerPlanState(node)
Definition execnodes.h:1273
#define InstrCountFiltered2(node, delta)
Definition execnodes.h:1286
#define innerPlanState(node)
Definition execnodes.h:1272
#define EXEC_FLAG_BACKWARD
Definition executor.h:70
#define EXEC_FLAG_REWIND
Definition executor.h:69
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition executor.h:486
#define ResetExprContext(econtext)
Definition executor.h:654
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:522
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:315
#define EXEC_FLAG_MARK
Definition executor.h:71
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
static TupleTableSlot * ExecNestLoop(PlanState *pstate)
void ExecEndNestLoop(NestLoopState *node)
void ExecReScanNestLoop(NestLoopState *node)
NestLoopState * ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
@ JOIN_SEMI
Definition nodes.h:317
@ JOIN_INNER
Definition nodes.h:303
@ JOIN_LEFT
Definition nodes.h:304
@ JOIN_ANTI
Definition nodes.h:318
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define innerPlan(node)
Definition plannodes.h:264
#define outerPlan(node)
Definition plannodes.h:265
static int fb(int x)
#define OUTER_VAR
Definition primnodes.h:244
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:286
ParamExecData * ecxt_param_exec_vals
Definition execnodes.h:295
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:288
JoinType jointype
Definition execnodes.h:2116
PlanState ps
Definition execnodes.h:2115
ExprState * joinqual
Definition execnodes.h:2119
bool single_match
Definition execnodes.h:2117
List * joinqual
Definition plannodes.h:988
JoinType jointype
Definition plannodes.h:985
bool inner_unique
Definition plannodes.h:986
TupleTableSlot * nl_NullInnerTupleSlot
Definition execnodes.h:2135
bool nl_NeedNewOuter
Definition execnodes.h:2133
JoinState js
Definition execnodes.h:2132
bool nl_MatchedOuter
Definition execnodes.h:2134
List * nestParams
Definition plannodes.h:1006
Join join
Definition plannodes.h:1004
ExprState * qual
Definition execnodes.h:1198
Plan * plan
Definition execnodes.h:1177
ExprContext * ps_ExprContext
Definition execnodes.h:1216
ProjectionInfo * ps_ProjInfo
Definition execnodes.h:1217
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:417
#define TupIsNull(slot)
Definition tuptable.h:325