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