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