Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeSubplan.c
4 : * routines to support sub-selects appearing in expressions
5 : *
6 : * This module is concerned with executing SubPlan expression nodes, which
7 : * should not be confused with sub-SELECTs appearing in FROM. SubPlans are
8 : * divided into "initplans", which are those that need only one evaluation per
9 : * query (among other restrictions, this requires that they don't use any
10 : * direct correlation variables from the parent plan level), and "regular"
11 : * subplans, which are re-evaluated every time their result is required.
12 : *
13 : *
14 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
15 : * Portions Copyright (c) 1994, Regents of the University of California
16 : *
17 : * IDENTIFICATION
18 : * src/backend/executor/nodeSubplan.c
19 : *
20 : *-------------------------------------------------------------------------
21 : */
22 : /*
23 : * INTERFACE ROUTINES
24 : * ExecSubPlan - process a subselect
25 : * ExecInitSubPlan - initialize a subselect
26 : */
27 : #include "postgres.h"
28 :
29 : #include <math.h>
30 :
31 : #include "access/htup_details.h"
32 : #include "executor/executor.h"
33 : #include "executor/nodeSubplan.h"
34 : #include "miscadmin.h"
35 : #include "nodes/makefuncs.h"
36 : #include "nodes/nodeFuncs.h"
37 : #include "utils/array.h"
38 : #include "utils/lsyscache.h"
39 : #include "utils/memutils.h"
40 :
41 : static Datum ExecHashSubPlan(SubPlanState *node,
42 : ExprContext *econtext,
43 : bool *isNull);
44 : static Datum ExecScanSubPlan(SubPlanState *node,
45 : ExprContext *econtext,
46 : bool *isNull);
47 : static void buildSubPlanHash(SubPlanState *node, ExprContext *econtext);
48 : static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
49 : FmgrInfo *eqfunctions);
50 : static bool slotAllNulls(TupleTableSlot *slot);
51 : static bool slotNoNulls(TupleTableSlot *slot);
52 :
53 :
54 : /* ----------------------------------------------------------------
55 : * ExecSubPlan
56 : *
57 : * This is the main entry point for execution of a regular SubPlan.
58 : * ----------------------------------------------------------------
59 : */
60 : Datum
61 3567012 : ExecSubPlan(SubPlanState *node,
62 : ExprContext *econtext,
63 : bool *isNull)
64 : {
65 3567012 : SubPlan *subplan = node->subplan;
66 3567012 : EState *estate = node->planstate->state;
67 3567012 : ScanDirection dir = estate->es_direction;
68 : Datum retval;
69 :
70 3567012 : CHECK_FOR_INTERRUPTS();
71 :
72 : /* Set non-null as default */
73 3567012 : *isNull = false;
74 :
75 : /* Sanity checks */
76 3567012 : if (subplan->subLinkType == CTE_SUBLINK)
77 0 : elog(ERROR, "CTE subplans should not be executed via ExecSubPlan");
78 3567012 : if (subplan->setParam != NIL && subplan->subLinkType != MULTIEXPR_SUBLINK)
79 0 : elog(ERROR, "cannot set parent params from subquery");
80 :
81 : /* Force forward-scan mode for evaluation */
82 3567012 : estate->es_direction = ForwardScanDirection;
83 :
84 : /* Select appropriate evaluation strategy */
85 3567012 : if (subplan->useHashTable)
86 1879360 : retval = ExecHashSubPlan(node, econtext, isNull);
87 : else
88 1687652 : retval = ExecScanSubPlan(node, econtext, isNull);
89 :
90 : /* restore scan direction */
91 3567006 : estate->es_direction = dir;
92 :
93 3567006 : return retval;
94 : }
95 :
96 : /*
97 : * ExecHashSubPlan: store subselect result in an in-memory hash table
98 : */
99 : static Datum
100 1879360 : ExecHashSubPlan(SubPlanState *node,
101 : ExprContext *econtext,
102 : bool *isNull)
103 : {
104 1879360 : bool result = false;
105 1879360 : SubPlan *subplan = node->subplan;
106 1879360 : PlanState *planstate = node->planstate;
107 : TupleTableSlot *slot;
108 :
109 : /* Shouldn't have any direct correlation Vars */
110 1879360 : if (subplan->parParam != NIL || subplan->args != NIL)
111 0 : elog(ERROR, "hashed subplan with direct correlation not supported");
112 :
113 : /*
114 : * If first time through or we need to rescan the subplan, build the hash
115 : * table.
116 : */
117 1879360 : if (node->hashtable == NULL || planstate->chgParam != NULL)
118 1550 : buildSubPlanHash(node, econtext);
119 :
120 : /*
121 : * The result for an empty subplan is always FALSE; no need to evaluate
122 : * lefthand side.
123 : */
124 1879354 : *isNull = false;
125 1879354 : if (!node->havehashrows && !node->havenullrows)
126 866040 : return BoolGetDatum(false);
127 :
128 : /*
129 : * Evaluate lefthand expressions and form a projection tuple. First we
130 : * have to set the econtext to use (hack alert!).
131 : */
132 1013314 : node->projLeft->pi_exprContext = econtext;
133 1013314 : slot = ExecProject(node->projLeft);
134 :
135 : /*
136 : * If the LHS is all non-null, probe for an exact match in the main hash
137 : * table. If we find one, the result is TRUE. Otherwise, scan the
138 : * partly-null table to see if there are any rows that aren't provably
139 : * unequal to the LHS; if so, the result is UNKNOWN. (We skip that part
140 : * if we don't care about UNKNOWN.) Otherwise, the result is FALSE.
141 : *
142 : * Note: the reason we can avoid a full scan of the main hash table is
143 : * that the combining operators are assumed never to yield NULL when both
144 : * inputs are non-null. If they were to do so, we might need to produce
145 : * UNKNOWN instead of FALSE because of an UNKNOWN result in comparing the
146 : * LHS to some main-table entry --- which is a comparison we will not even
147 : * make, unless there's a chance match of hash keys.
148 : */
149 1013314 : if (slotNoNulls(slot))
150 : {
151 2026532 : if (node->havehashrows &&
152 1013254 : FindTupleHashEntry(node->hashtable,
153 : slot,
154 : node->cur_eq_comp,
155 : node->lhs_hash_expr) != NULL)
156 63282 : result = true;
157 950032 : else if (node->havenullrows &&
158 36 : findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs))
159 18 : *isNull = true;
160 : }
161 :
162 : /*
163 : * When the LHS is partly or wholly NULL, we can never return TRUE. If we
164 : * don't care about UNKNOWN, just return FALSE. Otherwise, if the LHS is
165 : * wholly NULL, immediately return UNKNOWN. (Since the combining
166 : * operators are strict, the result could only be FALSE if the sub-select
167 : * were empty, but we already handled that case.) Otherwise, we must scan
168 : * both the main and partly-null tables to see if there are any rows that
169 : * aren't provably unequal to the LHS; if so, the result is UNKNOWN.
170 : * Otherwise, the result is FALSE.
171 : */
172 36 : else if (node->hashnulls == NULL)
173 : /* just return FALSE */ ;
174 36 : else if (slotAllNulls(slot))
175 0 : *isNull = true;
176 : /* Scan partly-null table first, since more likely to get a match */
177 72 : else if (node->havenullrows &&
178 36 : findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs))
179 18 : *isNull = true;
180 24 : else if (node->havehashrows &&
181 6 : findPartialMatch(node->hashtable, slot, node->cur_eq_funcs))
182 0 : *isNull = true;
183 :
184 : /*
185 : * Note: because we are typically called in a per-tuple context, we have
186 : * to explicitly clear the projected tuple before returning. Otherwise,
187 : * we'll have a double-free situation: the per-tuple context will probably
188 : * be reset before we're called again, and then the tuple slot will think
189 : * it still needs to free the tuple.
190 : */
191 1013314 : ExecClearTuple(slot);
192 :
193 : /* Also must reset the innerecontext after each hashtable lookup. */
194 1013314 : ResetExprContext(node->innerecontext);
195 :
196 1013314 : return BoolGetDatum(result);
197 : }
198 :
199 : /*
200 : * ExecScanSubPlan: default case where we have to rescan subplan each time
201 : */
202 : static Datum
203 1687652 : ExecScanSubPlan(SubPlanState *node,
204 : ExprContext *econtext,
205 : bool *isNull)
206 : {
207 1687652 : SubPlan *subplan = node->subplan;
208 1687652 : PlanState *planstate = node->planstate;
209 1687652 : SubLinkType subLinkType = subplan->subLinkType;
210 : MemoryContext oldcontext;
211 : TupleTableSlot *slot;
212 : Datum result;
213 1687652 : bool found = false; /* true if got at least one subplan tuple */
214 : ListCell *l;
215 1687652 : ArrayBuildStateAny *astate = NULL;
216 :
217 : /* Initialize ArrayBuildStateAny in caller's context, if needed */
218 1687652 : if (subLinkType == ARRAY_SUBLINK)
219 55022 : astate = initArrayResultAny(subplan->firstColType,
220 : CurrentMemoryContext, true);
221 :
222 : /*
223 : * We are probably in a short-lived expression-evaluation context. Switch
224 : * to the per-query context for manipulating the child plan's chgParam,
225 : * calling ExecProcNode on it, etc.
226 : */
227 1687652 : oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
228 :
229 : /*
230 : * We rely on the caller to evaluate plan correlation values, if
231 : * necessary. However we still need to record the fact that the values
232 : * (might have) changed, otherwise the ExecReScan() below won't know that
233 : * nodes need to be rescanned.
234 : */
235 3405086 : foreach(l, subplan->parParam)
236 : {
237 1717434 : int paramid = lfirst_int(l);
238 :
239 1717434 : planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
240 : }
241 :
242 : /* with that done, we can reset the subplan */
243 1687652 : ExecReScan(planstate);
244 :
245 : /*
246 : * For all sublink types except EXPR_SUBLINK and ARRAY_SUBLINK, the result
247 : * is boolean as are the results of the combining operators. We combine
248 : * results across tuples (if the subplan produces more than one) using OR
249 : * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK.
250 : * (ROWCOMPARE_SUBLINK doesn't allow multiple tuples from the subplan.)
251 : * NULL results from the combining operators are handled according to the
252 : * usual SQL semantics for OR and AND. The result for no input tuples is
253 : * FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK, NULL for
254 : * ROWCOMPARE_SUBLINK.
255 : *
256 : * For EXPR_SUBLINK we require the subplan to produce no more than one
257 : * tuple, else an error is raised. If zero tuples are produced, we return
258 : * NULL. Assuming we get a tuple, we just use its first column (there can
259 : * be only one non-junk column in this case).
260 : *
261 : * For MULTIEXPR_SUBLINK, we push the per-column subplan outputs out to
262 : * the setParams and then return a dummy false value. There must not be
263 : * multiple tuples returned from the subplan; if zero tuples are produced,
264 : * set the setParams to NULL.
265 : *
266 : * For ARRAY_SUBLINK we allow the subplan to produce any number of tuples,
267 : * and form an array of the first column's values. Note in particular
268 : * that we produce a zero-element array if no tuples are produced (this is
269 : * a change from pre-8.3 behavior of returning NULL).
270 : */
271 1687652 : result = BoolGetDatum(subLinkType == ALL_SUBLINK);
272 1687652 : *isNull = false;
273 :
274 1687652 : for (slot = ExecProcNode(planstate);
275 2048286 : !TupIsNull(slot);
276 360634 : slot = ExecProcNode(planstate))
277 : {
278 361490 : TupleDesc tdesc = slot->tts_tupleDescriptor;
279 : Datum rowresult;
280 : bool rownull;
281 : int col;
282 : ListCell *plst;
283 :
284 361490 : if (subLinkType == EXISTS_SUBLINK)
285 : {
286 708 : found = true;
287 708 : result = BoolGetDatum(true);
288 856 : break;
289 : }
290 :
291 360782 : if (subLinkType == EXPR_SUBLINK)
292 : {
293 : /* cannot allow multiple input tuples for EXPR sublink */
294 344648 : if (found)
295 0 : ereport(ERROR,
296 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
297 : errmsg("more than one row returned by a subquery used as an expression")));
298 344648 : found = true;
299 :
300 : /*
301 : * We need to copy the subplan's tuple in case the result is of
302 : * pass-by-ref type --- our return value will point into this
303 : * copied tuple! Can't use the subplan's instance of the tuple
304 : * since it won't still be valid after next ExecProcNode() call.
305 : * node->curTuple keeps track of the copied tuple for eventual
306 : * freeing.
307 : */
308 344648 : if (node->curTuple)
309 338610 : heap_freetuple(node->curTuple);
310 344648 : node->curTuple = ExecCopySlotHeapTuple(slot);
311 :
312 344648 : result = heap_getattr(node->curTuple, 1, tdesc, isNull);
313 : /* keep scanning subplan to make sure there's only one tuple */
314 349678 : continue;
315 : }
316 :
317 16134 : if (subLinkType == MULTIEXPR_SUBLINK)
318 : {
319 : /* cannot allow multiple input tuples for MULTIEXPR sublink */
320 240 : if (found)
321 0 : ereport(ERROR,
322 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
323 : errmsg("more than one row returned by a subquery used as an expression")));
324 240 : found = true;
325 :
326 : /*
327 : * We need to copy the subplan's tuple in case any result is of
328 : * pass-by-ref type --- our output values will point into this
329 : * copied tuple! Can't use the subplan's instance of the tuple
330 : * since it won't still be valid after next ExecProcNode() call.
331 : * node->curTuple keeps track of the copied tuple for eventual
332 : * freeing.
333 : */
334 240 : if (node->curTuple)
335 152 : heap_freetuple(node->curTuple);
336 240 : node->curTuple = ExecCopySlotHeapTuple(slot);
337 :
338 : /*
339 : * Now set all the setParam params from the columns of the tuple
340 : */
341 240 : col = 1;
342 714 : foreach(plst, subplan->setParam)
343 : {
344 474 : int paramid = lfirst_int(plst);
345 : ParamExecData *prmdata;
346 :
347 474 : prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
348 : Assert(prmdata->execPlan == NULL);
349 474 : prmdata->value = heap_getattr(node->curTuple, col, tdesc,
350 : &(prmdata->isnull));
351 474 : col++;
352 : }
353 :
354 : /* keep scanning subplan to make sure there's only one tuple */
355 240 : continue;
356 : }
357 :
358 15894 : if (subLinkType == ARRAY_SUBLINK)
359 4790 : {
360 : Datum dvalue;
361 : bool disnull;
362 :
363 4790 : found = true;
364 : /* stash away current value */
365 : Assert(subplan->firstColType == TupleDescAttr(tdesc, 0)->atttypid);
366 4790 : dvalue = slot_getattr(slot, 1, &disnull);
367 4790 : astate = accumArrayResultAny(astate, dvalue, disnull,
368 : subplan->firstColType, oldcontext);
369 : /* keep scanning subplan to collect all values */
370 4790 : continue;
371 : }
372 :
373 : /* cannot allow multiple input tuples for ROWCOMPARE sublink either */
374 11104 : if (subLinkType == ROWCOMPARE_SUBLINK && found)
375 0 : ereport(ERROR,
376 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
377 : errmsg("more than one row returned by a subquery used as an expression")));
378 :
379 11104 : found = true;
380 :
381 : /*
382 : * For ALL, ANY, and ROWCOMPARE sublinks, load up the Params
383 : * representing the columns of the sub-select, and then evaluate the
384 : * combining expression.
385 : */
386 11104 : col = 1;
387 32966 : foreach(plst, subplan->paramIds)
388 : {
389 21862 : int paramid = lfirst_int(plst);
390 : ParamExecData *prmdata;
391 :
392 21862 : prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
393 : Assert(prmdata->execPlan == NULL);
394 21862 : prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
395 21862 : col++;
396 : }
397 :
398 11104 : rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
399 : &rownull);
400 :
401 11104 : if (subLinkType == ANY_SUBLINK)
402 : {
403 : /* combine across rows per OR semantics */
404 10966 : if (rownull)
405 0 : *isNull = true;
406 10966 : else if (DatumGetBool(rowresult))
407 : {
408 124 : result = BoolGetDatum(true);
409 124 : *isNull = false;
410 124 : break; /* needn't look at any more rows */
411 : }
412 : }
413 138 : else if (subLinkType == ALL_SUBLINK)
414 : {
415 : /* combine across rows per AND semantics */
416 90 : if (rownull)
417 0 : *isNull = true;
418 90 : else if (!DatumGetBool(rowresult))
419 : {
420 24 : result = BoolGetDatum(false);
421 24 : *isNull = false;
422 24 : break; /* needn't look at any more rows */
423 : }
424 : }
425 : else
426 : {
427 : /* must be ROWCOMPARE_SUBLINK */
428 48 : result = rowresult;
429 48 : *isNull = rownull;
430 : }
431 : }
432 :
433 1687652 : MemoryContextSwitchTo(oldcontext);
434 :
435 1687652 : if (subLinkType == ARRAY_SUBLINK)
436 : {
437 : /* We return the result in the caller's context */
438 55022 : result = makeArrayResultAny(astate, oldcontext, true);
439 : }
440 1632630 : else if (!found)
441 : {
442 : /*
443 : * deal with empty subplan result. result/isNull were previously
444 : * initialized correctly for all sublink types except EXPR and
445 : * ROWCOMPARE; for those, return NULL.
446 : */
447 1281412 : if (subLinkType == EXPR_SUBLINK ||
448 : subLinkType == ROWCOMPARE_SUBLINK)
449 : {
450 76184 : result = (Datum) 0;
451 76184 : *isNull = true;
452 : }
453 1205228 : else if (subLinkType == MULTIEXPR_SUBLINK)
454 : {
455 : /* We don't care about function result, but set the setParams */
456 18 : foreach(l, subplan->setParam)
457 : {
458 12 : int paramid = lfirst_int(l);
459 : ParamExecData *prmdata;
460 :
461 12 : prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
462 : Assert(prmdata->execPlan == NULL);
463 12 : prmdata->value = (Datum) 0;
464 12 : prmdata->isnull = true;
465 : }
466 : }
467 : }
468 :
469 1687652 : return result;
470 : }
471 :
472 : /*
473 : * buildSubPlanHash: load hash table by scanning subplan output.
474 : */
475 : static void
476 1550 : buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
477 : {
478 1550 : SubPlan *subplan = node->subplan;
479 1550 : PlanState *planstate = node->planstate;
480 1550 : int ncols = node->numCols;
481 1550 : ExprContext *innerecontext = node->innerecontext;
482 : MemoryContext oldcontext;
483 : double nentries;
484 : TupleTableSlot *slot;
485 :
486 : Assert(subplan->subLinkType == ANY_SUBLINK);
487 :
488 : /*
489 : * If we already had any hash tables, reset 'em; otherwise create empty
490 : * hash table(s).
491 : *
492 : * If we need to distinguish accurately between FALSE and UNKNOWN (i.e.,
493 : * NULL) results of the IN operation, then we have to store subplan output
494 : * rows that are partly or wholly NULL. We store such rows in a separate
495 : * hash table that we expect will be much smaller than the main table. (We
496 : * can use hashing to eliminate partly-null rows that are not distinct. We
497 : * keep them separate to minimize the cost of the inevitable full-table
498 : * searches; see findPartialMatch.)
499 : *
500 : * If it's not necessary to distinguish FALSE and UNKNOWN, then we don't
501 : * need to store subplan output rows that contain NULL.
502 : *
503 : * Because the input slot for each hash table is always the slot resulting
504 : * from an ExecProject(), we can use TTSOpsVirtual for the input ops. This
505 : * saves a needless fetch inner op step for the hashing ExprState created
506 : * in BuildTupleHashTable().
507 : */
508 1550 : node->havehashrows = false;
509 1550 : node->havenullrows = false;
510 :
511 1550 : nentries = planstate->plan->plan_rows;
512 :
513 1550 : if (node->hashtable)
514 594 : ResetTupleHashTable(node->hashtable);
515 : else
516 956 : node->hashtable = BuildTupleHashTable(node->parent,
517 : node->descRight,
518 : &TTSOpsVirtual,
519 : ncols,
520 : node->keyColIdx,
521 956 : node->tab_eq_funcoids,
522 : node->tab_hash_funcs,
523 : node->tab_collations,
524 : nentries,
525 : 0, /* no additional data */
526 956 : node->planstate->state->es_query_cxt,
527 : node->tuplesContext,
528 : innerecontext->ecxt_per_tuple_memory,
529 : false);
530 :
531 1550 : if (!subplan->unknownEqFalse)
532 : {
533 778 : if (ncols == 1)
534 722 : nentries = 1; /* there can only be one entry */
535 : else
536 : {
537 56 : nentries /= 16;
538 56 : if (nentries < 1)
539 0 : nentries = 1;
540 : }
541 :
542 778 : if (node->hashnulls)
543 594 : ResetTupleHashTable(node->hashnulls);
544 : else
545 184 : node->hashnulls = BuildTupleHashTable(node->parent,
546 : node->descRight,
547 : &TTSOpsVirtual,
548 : ncols,
549 : node->keyColIdx,
550 184 : node->tab_eq_funcoids,
551 : node->tab_hash_funcs,
552 : node->tab_collations,
553 : nentries,
554 : 0, /* no additional data */
555 184 : node->planstate->state->es_query_cxt,
556 : node->tuplesContext,
557 : innerecontext->ecxt_per_tuple_memory,
558 : false);
559 : }
560 : else
561 772 : node->hashnulls = NULL;
562 :
563 : /*
564 : * We are probably in a short-lived expression-evaluation context. Switch
565 : * to the per-query context for manipulating the child plan.
566 : */
567 1550 : oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
568 :
569 : /*
570 : * Reset subplan to start.
571 : */
572 1550 : ExecReScan(planstate);
573 :
574 : /*
575 : * Scan the subplan and load the hash table(s). Note that when there are
576 : * duplicate rows coming out of the sub-select, only one copy is stored.
577 : */
578 1550 : for (slot = ExecProcNode(planstate);
579 256774 : !TupIsNull(slot);
580 255224 : slot = ExecProcNode(planstate))
581 : {
582 255230 : int col = 1;
583 : ListCell *plst;
584 : bool isnew;
585 :
586 : /*
587 : * Load up the Params representing the raw sub-select outputs, then
588 : * form the projection tuple to store in the hashtable.
589 : */
590 636416 : foreach(plst, subplan->paramIds)
591 : {
592 381186 : int paramid = lfirst_int(plst);
593 : ParamExecData *prmdata;
594 :
595 381186 : prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
596 : Assert(prmdata->execPlan == NULL);
597 381186 : prmdata->value = slot_getattr(slot, col,
598 : &(prmdata->isnull));
599 381186 : col++;
600 : }
601 255230 : slot = ExecProject(node->projRight);
602 :
603 : /*
604 : * If result contains any nulls, store separately or not at all.
605 : */
606 255230 : if (slotNoNulls(slot))
607 : {
608 255206 : (void) LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
609 255200 : node->havehashrows = true;
610 : }
611 24 : else if (node->hashnulls)
612 : {
613 24 : (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew, NULL);
614 24 : node->havenullrows = true;
615 : }
616 :
617 : /*
618 : * Reset innerecontext after each inner tuple to free any memory used
619 : * during ExecProject and hashtable lookup.
620 : */
621 255224 : ResetExprContext(innerecontext);
622 : }
623 :
624 : /*
625 : * Since the projected tuples are in the sub-query's context and not the
626 : * main context, we'd better clear the tuple slot before there's any
627 : * chance of a reset of the sub-query's context. Else we will have the
628 : * potential for a double free attempt. (XXX possibly no longer needed,
629 : * but can't hurt.)
630 : */
631 1544 : ExecClearTuple(node->projRight->pi_state.resultslot);
632 :
633 1544 : MemoryContextSwitchTo(oldcontext);
634 1544 : }
635 :
636 : /* Planner support routine to estimate space needed for hash table(s) */
637 : Size
638 3832 : EstimateSubplanHashTableSpace(double nentries,
639 : Size tupleWidth,
640 : bool unknownEqFalse)
641 : {
642 : Size tab1space,
643 : tab2space;
644 :
645 : /* Estimate size of main hashtable */
646 3832 : tab1space = EstimateTupleHashTableSpace(nentries,
647 : tupleWidth,
648 : 0 /* no additional data */ );
649 :
650 : /* Give up if that's already too big */
651 3832 : if (tab1space >= SIZE_MAX)
652 6 : return tab1space;
653 :
654 : /* Done if we don't need a hashnulls table */
655 3826 : if (unknownEqFalse)
656 3492 : return tab1space;
657 :
658 : /*
659 : * Adjust the rowcount estimate in the same way that buildSubPlanHash
660 : * will, except that we don't bother with the special case for a single
661 : * hash column. (We skip that detail because it'd be notationally painful
662 : * for our caller to provide the column count, and this table has
663 : * relatively little impact on the total estimate anyway.)
664 : */
665 334 : nentries /= 16;
666 334 : if (nentries < 1)
667 116 : nentries = 1;
668 :
669 : /*
670 : * It might be sane to also reduce the tupleWidth, but on the other hand
671 : * we are not accounting for the space taken by the tuples' null bitmaps.
672 : * Leave it alone for now.
673 : */
674 334 : tab2space = EstimateTupleHashTableSpace(nentries,
675 : tupleWidth,
676 : 0 /* no additional data */ );
677 :
678 : /* Guard against overflow */
679 334 : if (tab2space >= SIZE_MAX - tab1space)
680 0 : return SIZE_MAX;
681 :
682 334 : return tab1space + tab2space;
683 : }
684 :
685 : /*
686 : * execTuplesUnequal
687 : * Return true if two tuples are definitely unequal in the indicated
688 : * fields.
689 : *
690 : * Nulls are neither equal nor unequal to anything else. A true result
691 : * is obtained only if there are non-null fields that compare not-equal.
692 : *
693 : * slot1, slot2: the tuples to compare (must have same columns!)
694 : * numCols: the number of attributes to be examined
695 : * matchColIdx: array of attribute column numbers
696 : * eqFunctions: array of fmgr lookup info for the equality functions to use
697 : * evalContext: short-term memory context for executing the functions
698 : */
699 : static bool
700 78 : execTuplesUnequal(TupleTableSlot *slot1,
701 : TupleTableSlot *slot2,
702 : int numCols,
703 : AttrNumber *matchColIdx,
704 : FmgrInfo *eqfunctions,
705 : const Oid *collations,
706 : MemoryContext evalContext)
707 : {
708 : MemoryContext oldContext;
709 : bool result;
710 : int i;
711 :
712 : /* Reset and switch into the temp context. */
713 78 : MemoryContextReset(evalContext);
714 78 : oldContext = MemoryContextSwitchTo(evalContext);
715 :
716 : /*
717 : * We cannot report a match without checking all the fields, but we can
718 : * report a non-match as soon as we find unequal fields. So, start
719 : * comparing at the last field (least significant sort key). That's the
720 : * most likely to be different if we are dealing with sorted input.
721 : */
722 78 : result = false;
723 :
724 192 : for (i = numCols; --i >= 0;)
725 : {
726 156 : AttrNumber att = matchColIdx[i];
727 : Datum attr1,
728 : attr2;
729 : bool isNull1,
730 : isNull2;
731 :
732 156 : attr1 = slot_getattr(slot1, att, &isNull1);
733 :
734 156 : if (isNull1)
735 78 : continue; /* can't prove anything here */
736 :
737 114 : attr2 = slot_getattr(slot2, att, &isNull2);
738 :
739 114 : if (isNull2)
740 36 : continue; /* can't prove anything here */
741 :
742 : /* Apply the type-specific equality function */
743 78 : if (!DatumGetBool(FunctionCall2Coll(&eqfunctions[i],
744 78 : collations[i],
745 : attr1, attr2)))
746 : {
747 42 : result = true; /* they are unequal */
748 42 : break;
749 : }
750 : }
751 :
752 78 : MemoryContextSwitchTo(oldContext);
753 :
754 78 : return result;
755 : }
756 :
757 : /*
758 : * findPartialMatch: does the hashtable contain an entry that is not
759 : * provably distinct from the tuple?
760 : *
761 : * We have to scan the whole hashtable; we can't usefully use hashkeys
762 : * to guide probing, since we might get partial matches on tuples with
763 : * hashkeys quite unrelated to what we'd get from the given tuple.
764 : *
765 : * Caller must provide the equality functions to use, since in cross-type
766 : * cases these are different from the hashtable's internal functions.
767 : */
768 : static bool
769 78 : findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
770 : FmgrInfo *eqfunctions)
771 : {
772 78 : int numCols = hashtable->numCols;
773 78 : AttrNumber *keyColIdx = hashtable->keyColIdx;
774 : TupleHashIterator hashiter;
775 : TupleHashEntry entry;
776 :
777 78 : InitTupleHashIterator(hashtable, &hashiter);
778 120 : while ((entry = ScanTupleHashTable(hashtable, &hashiter)) != NULL)
779 : {
780 78 : CHECK_FOR_INTERRUPTS();
781 :
782 78 : ExecStoreMinimalTuple(TupleHashEntryGetTuple(entry), hashtable->tableslot, false);
783 78 : if (!execTuplesUnequal(slot, hashtable->tableslot,
784 : numCols, keyColIdx,
785 : eqfunctions,
786 78 : hashtable->tab_collations,
787 : hashtable->tempcxt))
788 : {
789 : TermTupleHashIterator(&hashiter);
790 36 : return true;
791 : }
792 : }
793 : /* No TermTupleHashIterator call needed here */
794 42 : return false;
795 : }
796 :
797 : /*
798 : * slotAllNulls: is the slot completely NULL?
799 : *
800 : * This does not test for dropped columns, which is OK because we only
801 : * use it on projected tuples.
802 : */
803 : static bool
804 36 : slotAllNulls(TupleTableSlot *slot)
805 : {
806 36 : int ncols = slot->tts_tupleDescriptor->natts;
807 : int i;
808 :
809 36 : for (i = 1; i <= ncols; i++)
810 : {
811 36 : if (!slot_attisnull(slot, i))
812 36 : return false;
813 : }
814 0 : return true;
815 : }
816 :
817 : /*
818 : * slotNoNulls: is the slot entirely not NULL?
819 : *
820 : * This does not test for dropped columns, which is OK because we only
821 : * use it on projected tuples.
822 : */
823 : static bool
824 1268544 : slotNoNulls(TupleTableSlot *slot)
825 : {
826 1268544 : int ncols = slot->tts_tupleDescriptor->natts;
827 : int i;
828 :
829 2723170 : for (i = 1; i <= ncols; i++)
830 : {
831 1454686 : if (slot_attisnull(slot, i))
832 60 : return false;
833 : }
834 1268484 : return true;
835 : }
836 :
837 : /* ----------------------------------------------------------------
838 : * ExecInitSubPlan
839 : *
840 : * Create a SubPlanState for a SubPlan; this is the SubPlan-specific part
841 : * of ExecInitExpr(). We split it out so that it can be used for InitPlans
842 : * as well as regular SubPlans. Note that we don't link the SubPlan into
843 : * the parent's subPlan list, because that shouldn't happen for InitPlans.
844 : * Instead, ExecInitExpr() does that one part.
845 : *
846 : * We also rely on ExecInitExpr(), more precisely ExecInitSubPlanExpr(), to
847 : * evaluate input parameters, as that allows them to be evaluated as part of
848 : * the expression referencing the SubPlan.
849 : * ----------------------------------------------------------------
850 : */
851 : SubPlanState *
852 43624 : ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
853 : {
854 43624 : SubPlanState *sstate = makeNode(SubPlanState);
855 43624 : EState *estate = parent->state;
856 :
857 43624 : sstate->subplan = subplan;
858 :
859 : /* Link the SubPlanState to already-initialized subplan */
860 87248 : sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates,
861 43624 : subplan->plan_id - 1);
862 :
863 : /*
864 : * This check can fail if the planner mistakenly puts a parallel-unsafe
865 : * subplan into a parallelized subquery; see ExecSerializePlan.
866 : */
867 43624 : if (sstate->planstate == NULL)
868 0 : elog(ERROR, "subplan \"%s\" was not initialized",
869 : subplan->plan_name);
870 :
871 : /* Link to parent's state, too */
872 43624 : sstate->parent = parent;
873 :
874 : /* Initialize subexpressions */
875 43624 : sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
876 :
877 : /*
878 : * initialize my state
879 : */
880 43624 : sstate->curTuple = NULL;
881 43624 : sstate->curArray = PointerGetDatum(NULL);
882 43624 : sstate->projLeft = NULL;
883 43624 : sstate->projRight = NULL;
884 43624 : sstate->hashtable = NULL;
885 43624 : sstate->hashnulls = NULL;
886 43624 : sstate->tuplesContext = NULL;
887 43624 : sstate->innerecontext = NULL;
888 43624 : sstate->keyColIdx = NULL;
889 43624 : sstate->tab_eq_funcoids = NULL;
890 43624 : sstate->tab_hash_funcs = NULL;
891 43624 : sstate->tab_collations = NULL;
892 43624 : sstate->cur_eq_funcs = NULL;
893 :
894 : /*
895 : * If this is an initplan, it has output parameters that the parent plan
896 : * will use, so mark those parameters as needing evaluation. We don't
897 : * actually run the subplan until we first need one of its outputs.
898 : *
899 : * A CTE subplan's output parameter is never to be evaluated in the normal
900 : * way, so skip this in that case.
901 : *
902 : * Note that we don't set parent->chgParam here: the parent plan hasn't
903 : * been run yet, so no need to force it to re-run.
904 : */
905 43624 : if (subplan->setParam != NIL && subplan->parParam == NIL &&
906 14418 : subplan->subLinkType != CTE_SUBLINK)
907 : {
908 : ListCell *lst;
909 :
910 23896 : foreach(lst, subplan->setParam)
911 : {
912 11972 : int paramid = lfirst_int(lst);
913 11972 : ParamExecData *prm = &(estate->es_param_exec_vals[paramid]);
914 :
915 11972 : prm->execPlan = sstate;
916 : }
917 : }
918 :
919 : /*
920 : * If we are going to hash the subquery output, initialize relevant stuff.
921 : * (We don't create the hashtable until needed, though.)
922 : */
923 43624 : if (subplan->useHashTable)
924 : {
925 : int ncols,
926 : i;
927 : TupleDesc tupDescLeft;
928 : TupleDesc tupDescRight;
929 : Oid *cross_eq_funcoids;
930 : TupleTableSlot *slot;
931 : FmgrInfo *lhs_hash_funcs;
932 : List *oplist,
933 : *lefttlist,
934 : *righttlist;
935 : ListCell *l;
936 :
937 : /* We need a memory context to hold the hash table(s)' tuples */
938 1144 : sstate->tuplesContext =
939 1144 : BumpContextCreate(CurrentMemoryContext,
940 : "SubPlan hashed tuples",
941 : ALLOCSET_DEFAULT_SIZES);
942 : /* and a short-lived exprcontext for function evaluation */
943 1144 : sstate->innerecontext = CreateExprContext(estate);
944 :
945 : /*
946 : * We use ExecProject to evaluate the lefthand and righthand
947 : * expression lists and form tuples. (You might think that we could
948 : * use the sub-select's output tuples directly, but that is not the
949 : * case if we had to insert any run-time coercions of the sub-select's
950 : * output datatypes; anyway this avoids storing any resjunk columns
951 : * that might be in the sub-select's output.) Run through the
952 : * combining expressions to build tlists for the lefthand and
953 : * righthand sides.
954 : *
955 : * We also extract the combining operators themselves to initialize
956 : * the equality and hashing functions for the hash tables.
957 : */
958 1144 : if (IsA(subplan->testexpr, OpExpr))
959 : {
960 : /* single combining operator */
961 1046 : oplist = list_make1(subplan->testexpr);
962 : }
963 98 : else if (is_andclause(subplan->testexpr))
964 : {
965 : /* multiple combining operators */
966 98 : oplist = castNode(BoolExpr, subplan->testexpr)->args;
967 : }
968 : else
969 : {
970 : /* shouldn't see anything else in a hashable subplan */
971 0 : elog(ERROR, "unrecognized testexpr type: %d",
972 : (int) nodeTag(subplan->testexpr));
973 : oplist = NIL; /* keep compiler quiet */
974 : }
975 1144 : ncols = list_length(oplist);
976 :
977 1144 : lefttlist = righttlist = NIL;
978 1144 : sstate->numCols = ncols;
979 1144 : sstate->keyColIdx = (AttrNumber *) palloc(ncols * sizeof(AttrNumber));
980 1144 : sstate->tab_eq_funcoids = (Oid *) palloc(ncols * sizeof(Oid));
981 1144 : sstate->tab_collations = (Oid *) palloc(ncols * sizeof(Oid));
982 1144 : sstate->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
983 1144 : lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
984 1144 : sstate->cur_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
985 : /* we'll need the cross-type equality fns below, but not in sstate */
986 1144 : cross_eq_funcoids = (Oid *) palloc(ncols * sizeof(Oid));
987 :
988 1144 : i = 1;
989 2386 : foreach(l, oplist)
990 : {
991 1242 : OpExpr *opexpr = lfirst_node(OpExpr, l);
992 : Expr *expr;
993 : TargetEntry *tle;
994 : Oid rhs_eq_oper;
995 : Oid left_hashfn;
996 : Oid right_hashfn;
997 :
998 : Assert(list_length(opexpr->args) == 2);
999 :
1000 : /* Process lefthand argument */
1001 1242 : expr = (Expr *) linitial(opexpr->args);
1002 1242 : tle = makeTargetEntry(expr,
1003 : i,
1004 : NULL,
1005 : false);
1006 1242 : lefttlist = lappend(lefttlist, tle);
1007 :
1008 : /* Process righthand argument */
1009 1242 : expr = (Expr *) lsecond(opexpr->args);
1010 1242 : tle = makeTargetEntry(expr,
1011 : i,
1012 : NULL,
1013 : false);
1014 1242 : righttlist = lappend(righttlist, tle);
1015 :
1016 : /* Lookup the equality function (potentially cross-type) */
1017 1242 : cross_eq_funcoids[i - 1] = opexpr->opfuncid;
1018 1242 : fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
1019 1242 : fmgr_info_set_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]);
1020 :
1021 : /* Look up the equality function for the RHS type */
1022 1242 : if (!get_compatible_hash_operators(opexpr->opno,
1023 : NULL, &rhs_eq_oper))
1024 0 : elog(ERROR, "could not find compatible hash operator for operator %u",
1025 : opexpr->opno);
1026 1242 : sstate->tab_eq_funcoids[i - 1] = get_opcode(rhs_eq_oper);
1027 :
1028 : /* Lookup the associated hash functions */
1029 1242 : if (!get_op_hash_functions(opexpr->opno,
1030 : &left_hashfn, &right_hashfn))
1031 0 : elog(ERROR, "could not find hash function for hash operator %u",
1032 : opexpr->opno);
1033 1242 : fmgr_info(left_hashfn, &lhs_hash_funcs[i - 1]);
1034 1242 : fmgr_info(right_hashfn, &sstate->tab_hash_funcs[i - 1]);
1035 :
1036 : /* Set collation */
1037 1242 : sstate->tab_collations[i - 1] = opexpr->inputcollid;
1038 :
1039 : /* keyColIdx is just column numbers 1..n */
1040 1242 : sstate->keyColIdx[i - 1] = i;
1041 :
1042 1242 : i++;
1043 : }
1044 :
1045 : /*
1046 : * Construct tupdescs, slots and projection nodes for left and right
1047 : * sides. The lefthand expressions will be evaluated in the parent
1048 : * plan node's exprcontext, which we don't have access to here.
1049 : * Fortunately we can just pass NULL for now and fill it in later
1050 : * (hack alert!). The righthand expressions will be evaluated in our
1051 : * own innerecontext.
1052 : */
1053 1144 : tupDescLeft = ExecTypeFromTL(lefttlist);
1054 1144 : slot = ExecInitExtraTupleSlot(estate, tupDescLeft, &TTSOpsVirtual);
1055 1144 : sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
1056 : NULL,
1057 : slot,
1058 : parent,
1059 : NULL);
1060 :
1061 1144 : sstate->descRight = tupDescRight = ExecTypeFromTL(righttlist);
1062 1144 : slot = ExecInitExtraTupleSlot(estate, tupDescRight, &TTSOpsVirtual);
1063 1144 : sstate->projRight = ExecBuildProjectionInfo(righttlist,
1064 : sstate->innerecontext,
1065 : slot,
1066 : sstate->planstate,
1067 : NULL);
1068 :
1069 : /* Build the ExprState for generating hash values */
1070 1144 : sstate->lhs_hash_expr = ExecBuildHash32FromAttrs(tupDescLeft,
1071 : &TTSOpsVirtual,
1072 : lhs_hash_funcs,
1073 : sstate->tab_collations,
1074 : sstate->numCols,
1075 : sstate->keyColIdx,
1076 : parent,
1077 : 0);
1078 :
1079 : /*
1080 : * Create comparator for lookups of rows in the table (potentially
1081 : * cross-type comparisons).
1082 : */
1083 1144 : sstate->cur_eq_comp = ExecBuildGroupingEqual(tupDescLeft, tupDescRight,
1084 : &TTSOpsVirtual, &TTSOpsMinimalTuple,
1085 : ncols,
1086 1144 : sstate->keyColIdx,
1087 : cross_eq_funcoids,
1088 1144 : sstate->tab_collations,
1089 : parent);
1090 : }
1091 :
1092 43624 : return sstate;
1093 : }
1094 :
1095 : /* ----------------------------------------------------------------
1096 : * ExecSetParamPlan
1097 : *
1098 : * Executes a subplan and sets its output parameters.
1099 : *
1100 : * This is called from ExecEvalParamExec() when the value of a PARAM_EXEC
1101 : * parameter is requested and the param's execPlan field is set (indicating
1102 : * that the param has not yet been evaluated). This allows lazy evaluation
1103 : * of initplans: we don't run the subplan until/unless we need its output.
1104 : * Note that this routine MUST clear the execPlan fields of the plan's
1105 : * output parameters after evaluating them!
1106 : *
1107 : * The results of this function are stored in the EState associated with the
1108 : * ExprContext (particularly, its ecxt_param_exec_vals); any pass-by-ref
1109 : * result Datums are allocated in the EState's per-query memory. The passed
1110 : * econtext can be any ExprContext belonging to that EState; which one is
1111 : * important only to the extent that the ExprContext's per-tuple memory
1112 : * context is used to evaluate any parameters passed down to the subplan.
1113 : * (Thus in principle, the shorter-lived the ExprContext the better, since
1114 : * that data isn't needed after we return. In practice, because initplan
1115 : * parameters are never more complex than Vars, Aggrefs, etc, evaluating them
1116 : * currently never leaks any memory anyway.)
1117 : * ----------------------------------------------------------------
1118 : */
1119 : void
1120 9566 : ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
1121 : {
1122 9566 : SubPlan *subplan = node->subplan;
1123 9566 : PlanState *planstate = node->planstate;
1124 9566 : SubLinkType subLinkType = subplan->subLinkType;
1125 9566 : EState *estate = planstate->state;
1126 9566 : ScanDirection dir = estate->es_direction;
1127 : MemoryContext oldcontext;
1128 : TupleTableSlot *slot;
1129 : ListCell *l;
1130 9566 : bool found = false;
1131 9566 : ArrayBuildStateAny *astate = NULL;
1132 :
1133 9566 : if (subLinkType == ANY_SUBLINK ||
1134 : subLinkType == ALL_SUBLINK)
1135 0 : elog(ERROR, "ANY/ALL subselect unsupported as initplan");
1136 9566 : if (subLinkType == CTE_SUBLINK)
1137 0 : elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
1138 9566 : if (subplan->parParam || subplan->args)
1139 0 : elog(ERROR, "correlated subplans should not be executed via ExecSetParamPlan");
1140 :
1141 : /*
1142 : * Enforce forward scan direction regardless of caller. It's hard but not
1143 : * impossible to get here in backward scan, so make it work anyway.
1144 : */
1145 9566 : estate->es_direction = ForwardScanDirection;
1146 :
1147 : /* Initialize ArrayBuildStateAny in caller's context, if needed */
1148 9566 : if (subLinkType == ARRAY_SUBLINK)
1149 122 : astate = initArrayResultAny(subplan->firstColType,
1150 : CurrentMemoryContext, true);
1151 :
1152 : /*
1153 : * Must switch to per-query memory context.
1154 : */
1155 9566 : oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
1156 :
1157 : /*
1158 : * Run the plan. (If it needs to be rescanned, the first ExecProcNode
1159 : * call will take care of that.)
1160 : */
1161 9566 : for (slot = ExecProcNode(planstate);
1162 30492 : !TupIsNull(slot);
1163 20926 : slot = ExecProcNode(planstate))
1164 : {
1165 21078 : TupleDesc tdesc = slot->tts_tupleDescriptor;
1166 21078 : int i = 1;
1167 :
1168 21078 : if (subLinkType == EXISTS_SUBLINK)
1169 : {
1170 : /* There can be only one setParam... */
1171 134 : int paramid = linitial_int(subplan->setParam);
1172 134 : ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
1173 :
1174 134 : prm->execPlan = NULL;
1175 134 : prm->value = BoolGetDatum(true);
1176 134 : prm->isnull = false;
1177 134 : found = true;
1178 134 : break;
1179 : }
1180 :
1181 20944 : if (subLinkType == ARRAY_SUBLINK)
1182 12334 : {
1183 : Datum dvalue;
1184 : bool disnull;
1185 :
1186 12334 : found = true;
1187 : /* stash away current value */
1188 : Assert(subplan->firstColType == TupleDescAttr(tdesc, 0)->atttypid);
1189 12334 : dvalue = slot_getattr(slot, 1, &disnull);
1190 12334 : astate = accumArrayResultAny(astate, dvalue, disnull,
1191 : subplan->firstColType, oldcontext);
1192 : /* keep scanning subplan to collect all values */
1193 12334 : continue;
1194 : }
1195 :
1196 8610 : if (found &&
1197 12 : (subLinkType == EXPR_SUBLINK ||
1198 6 : subLinkType == MULTIEXPR_SUBLINK ||
1199 : subLinkType == ROWCOMPARE_SUBLINK))
1200 18 : ereport(ERROR,
1201 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
1202 : errmsg("more than one row returned by a subquery used as an expression")));
1203 :
1204 8592 : found = true;
1205 :
1206 : /*
1207 : * We need to copy the subplan's tuple into our own context, in case
1208 : * any of the params are pass-by-ref type --- the pointers stored in
1209 : * the param structs will point at this copied tuple! node->curTuple
1210 : * keeps track of the copied tuple for eventual freeing.
1211 : */
1212 8592 : if (node->curTuple)
1213 520 : heap_freetuple(node->curTuple);
1214 8592 : node->curTuple = ExecCopySlotHeapTuple(slot);
1215 :
1216 : /*
1217 : * Now set all the setParam params from the columns of the tuple
1218 : */
1219 17220 : foreach(l, subplan->setParam)
1220 : {
1221 8628 : int paramid = lfirst_int(l);
1222 8628 : ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
1223 :
1224 8628 : prm->execPlan = NULL;
1225 8628 : prm->value = heap_getattr(node->curTuple, i, tdesc,
1226 : &(prm->isnull));
1227 8628 : i++;
1228 : }
1229 : }
1230 :
1231 9548 : if (subLinkType == ARRAY_SUBLINK)
1232 : {
1233 : /* There can be only one setParam... */
1234 122 : int paramid = linitial_int(subplan->setParam);
1235 122 : ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
1236 :
1237 : /*
1238 : * We build the result array in query context so it won't disappear;
1239 : * to avoid leaking memory across repeated calls, we have to remember
1240 : * the latest value, much as for curTuple above.
1241 : */
1242 122 : if (node->curArray != PointerGetDatum(NULL))
1243 0 : pfree(DatumGetPointer(node->curArray));
1244 122 : node->curArray = makeArrayResultAny(astate,
1245 : econtext->ecxt_per_query_memory,
1246 : true);
1247 122 : prm->execPlan = NULL;
1248 122 : prm->value = node->curArray;
1249 122 : prm->isnull = false;
1250 : }
1251 9426 : else if (!found)
1252 : {
1253 718 : if (subLinkType == EXISTS_SUBLINK)
1254 : {
1255 : /* There can be only one setParam... */
1256 668 : int paramid = linitial_int(subplan->setParam);
1257 668 : ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
1258 :
1259 668 : prm->execPlan = NULL;
1260 668 : prm->value = BoolGetDatum(false);
1261 668 : prm->isnull = false;
1262 : }
1263 : else
1264 : {
1265 : /* For other sublink types, set all the output params to NULL */
1266 106 : foreach(l, subplan->setParam)
1267 : {
1268 56 : int paramid = lfirst_int(l);
1269 56 : ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
1270 :
1271 56 : prm->execPlan = NULL;
1272 56 : prm->value = (Datum) 0;
1273 56 : prm->isnull = true;
1274 : }
1275 : }
1276 : }
1277 :
1278 9548 : MemoryContextSwitchTo(oldcontext);
1279 :
1280 : /* restore scan direction */
1281 9548 : estate->es_direction = dir;
1282 9548 : }
1283 :
1284 : /*
1285 : * ExecSetParamPlanMulti
1286 : *
1287 : * Apply ExecSetParamPlan to evaluate any not-yet-evaluated initplan output
1288 : * parameters whose ParamIDs are listed in "params". Any listed params that
1289 : * are not initplan outputs are ignored.
1290 : *
1291 : * As with ExecSetParamPlan, any ExprContext belonging to the current EState
1292 : * can be used, but in principle a shorter-lived ExprContext is better than a
1293 : * longer-lived one.
1294 : */
1295 : void
1296 1398 : ExecSetParamPlanMulti(const Bitmapset *params, ExprContext *econtext)
1297 : {
1298 : int paramid;
1299 :
1300 1398 : paramid = -1;
1301 1866 : while ((paramid = bms_next_member(params, paramid)) >= 0)
1302 : {
1303 468 : ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
1304 :
1305 468 : if (prm->execPlan != NULL)
1306 : {
1307 : /* Parameter not evaluated yet, so go do it */
1308 34 : ExecSetParamPlan(prm->execPlan, econtext);
1309 : /* ExecSetParamPlan should have processed this param... */
1310 : Assert(prm->execPlan == NULL);
1311 : }
1312 : }
1313 1398 : }
1314 :
1315 : /*
1316 : * Mark an initplan as needing recalculation
1317 : */
1318 : void
1319 1362 : ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
1320 : {
1321 1362 : PlanState *planstate = node->planstate;
1322 1362 : SubPlan *subplan = node->subplan;
1323 1362 : EState *estate = parent->state;
1324 : ListCell *l;
1325 :
1326 : /* sanity checks */
1327 1362 : if (subplan->parParam != NIL)
1328 0 : elog(ERROR, "direct correlated subquery unsupported as initplan");
1329 1362 : if (subplan->setParam == NIL)
1330 0 : elog(ERROR, "setParam list of initplan is empty");
1331 1362 : if (bms_is_empty(planstate->plan->extParam))
1332 0 : elog(ERROR, "extParam set of initplan is empty");
1333 :
1334 : /*
1335 : * Don't actually re-scan: it'll happen inside ExecSetParamPlan if needed.
1336 : */
1337 :
1338 : /*
1339 : * Mark this subplan's output parameters as needing recalculation.
1340 : *
1341 : * CTE subplans are never executed via parameter recalculation; instead
1342 : * they get run when called by nodeCtescan.c. So don't mark the output
1343 : * parameter of a CTE subplan as dirty, but do set the chgParam bit for it
1344 : * so that dependent plan nodes will get told to rescan.
1345 : */
1346 2724 : foreach(l, subplan->setParam)
1347 : {
1348 1362 : int paramid = lfirst_int(l);
1349 1362 : ParamExecData *prm = &(estate->es_param_exec_vals[paramid]);
1350 :
1351 1362 : if (subplan->subLinkType != CTE_SUBLINK)
1352 826 : prm->execPlan = node;
1353 :
1354 1362 : parent->chgParam = bms_add_member(parent->chgParam, paramid);
1355 : }
1356 1362 : }
|