1 /*-------------------------------------------------------------------------
2  *
3  * nodeNestloop.c
4  *	  routines to support nest-loop joins
5  *
6  * Portions Copyright (c) 1996-2021, 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/nodeNestloop.h"
26 #include "miscadmin.h"
27 #include "utils/memutils.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 *
ExecNestLoop(PlanState * pstate)61 ExecNestLoop(PlanState *pstate)
62 {
63 	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 
74 	CHECK_FOR_INTERRUPTS();
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;
84 	outerPlan = outerPlanState(node);
85 	innerPlan = innerPlanState(node);
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");
109 			outerTupleSlot = ExecProcNode(outerPlan);
110 
111 			/*
112 			 * if there are no more outer tuples, then the join is complete..
113 			 */
114 			if (TupIsNull(outerTupleSlot))
115 			{
116 				ENL1_printf("no outer tuple, ending join");
117 				return NULL;
118 			}
119 
120 			ENL1_printf("saving new outer tuple information");
121 			econtext->ecxt_outertuple = outerTupleSlot;
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 			{
131 				NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
132 				int			paramno = nlp->paramno;
133 				ParamExecData *prm;
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);
140 				prm->value = slot_getattr(outerTupleSlot,
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");
152 			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 
160 		innerTupleSlot = ExecProcNode(innerPlan);
161 		econtext->ecxt_innertuple = innerTupleSlot;
162 
163 		if (TupIsNull(innerTupleSlot))
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  */
262 NestLoopState *
ExecInitNestLoop(NestLoop * node,EState * estate,int eflags)263 ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
264 {
265 	NestLoopState *nlstate;
266 
267 	/* check for unsupported flags */
268 	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 	 */
276 	nlstate = makeNode(NestLoopState);
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 	 */
307 	ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
308 	ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
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 =
317 		ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
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 =
334 				ExecInitNullTupleSlot(estate,
335 									  ExecGetResultType(innerPlanState(nlstate)),
336 									  &TTSOpsVirtual);
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  */
361 void
ExecEndNestLoop(NestLoopState * node)362 ExecEndNestLoop(NestLoopState *node)
363 {
364 	NL1_printf("ExecEndNestLoop: %s\n",
365 			   "ending node processing");
366 
367 	/*
368 	 * Free the exprcontext
369 	 */
370 	ExecFreeExprContext(&node->js.ps);
371 
372 	/*
373 	 * clean out the tuple table
374 	 */
375 	ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
376 
377 	/*
378 	 * close down subplans
379 	 */
380 	ExecEndNode(outerPlanState(node));
381 	ExecEndNode(innerPlanState(node));
382 
383 	NL1_printf("ExecEndNestLoop: %s\n",
384 			   "node processing ended");
385 }
386 
387 /* ----------------------------------------------------------------
388  *		ExecReScanNestLoop
389  * ----------------------------------------------------------------
390  */
391 void
ExecReScanNestLoop(NestLoopState * node)392 ExecReScanNestLoop(NestLoopState *node)
393 {
394 	PlanState  *outerPlan = outerPlanState(node);
395 
396 	/*
397 	 * If outerPlan->chgParam is not null then plan will be automatically
398 	 * re-scanned by first ExecProcNode.
399 	 */
400 	if (outerPlan->chgParam == NULL)
401 		ExecReScan(outerPlan);
402 
403 	/*
404 	 * innerPlan is re-scanned for each new outer tuple and MUST NOT be
405 	 * re-scanned from here or you'll get troubles from inner index scans when
406 	 * outer Vars are used as run-time keys...
407 	 */
408 
409 	node->nl_NeedNewOuter = true;
410 	node->nl_MatchedOuter = false;
411 }
412