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