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