Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * subselect.c
4 : : * Planning routines for subselects.
5 : : *
6 : : * This module deals with SubLinks and CTEs, but not subquery RTEs (i.e.,
7 : : * not sub-SELECT-in-FROM cases).
8 : : *
9 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
10 : : * Portions Copyright (c) 1994, Regents of the University of California
11 : : *
12 : : * IDENTIFICATION
13 : : * src/backend/optimizer/plan/subselect.c
14 : : *
15 : : *-------------------------------------------------------------------------
16 : : */
17 : : #include "postgres.h"
18 : :
19 : : #include "access/htup_details.h"
20 : : #include "catalog/pg_operator.h"
21 : : #include "catalog/pg_type.h"
22 : : #include "executor/executor.h"
23 : : #include "miscadmin.h"
24 : : #include "nodes/makefuncs.h"
25 : : #include "nodes/nodeFuncs.h"
26 : : #include "optimizer/clauses.h"
27 : : #include "optimizer/cost.h"
28 : : #include "optimizer/optimizer.h"
29 : : #include "optimizer/paramassign.h"
30 : : #include "optimizer/pathnode.h"
31 : : #include "optimizer/planmain.h"
32 : : #include "optimizer/planner.h"
33 : : #include "optimizer/prep.h"
34 : : #include "optimizer/subselect.h"
35 : : #include "parser/parse_relation.h"
36 : : #include "rewrite/rewriteManip.h"
37 : : #include "utils/builtins.h"
38 : : #include "utils/lsyscache.h"
39 : : #include "utils/syscache.h"
40 : :
41 : :
42 : : typedef struct convert_testexpr_context
43 : : {
44 : : PlannerInfo *root;
45 : : List *subst_nodes; /* Nodes to substitute for Params */
46 : : } convert_testexpr_context;
47 : :
48 : : typedef struct process_sublinks_context
49 : : {
50 : : PlannerInfo *root;
51 : : bool isTopQual;
52 : : } process_sublinks_context;
53 : :
54 : : typedef struct finalize_primnode_context
55 : : {
56 : : PlannerInfo *root;
57 : : Bitmapset *paramids; /* Non-local PARAM_EXEC paramids found */
58 : : } finalize_primnode_context;
59 : :
60 : : typedef struct inline_cte_walker_context
61 : : {
62 : : const char *ctename; /* name and relative level of target CTE */
63 : : int levelsup;
64 : : Query *ctequery; /* query to substitute */
65 : : } inline_cte_walker_context;
66 : :
67 : :
68 : : static Node *build_subplan(PlannerInfo *root, Plan *plan, Path *path,
69 : : PlannerInfo *subroot, List *plan_params,
70 : : SubLinkType subLinkType, int subLinkId,
71 : : Node *testexpr, List *testexpr_paramids,
72 : : bool unknownEqFalse);
73 : : static List *generate_subquery_params(PlannerInfo *root, List *tlist,
74 : : List **paramIds);
75 : : static List *generate_subquery_vars(PlannerInfo *root, List *tlist,
76 : : Index varno);
77 : : static Node *convert_testexpr(PlannerInfo *root,
78 : : Node *testexpr,
79 : : List *subst_nodes);
80 : : static Node *convert_testexpr_mutator(Node *node,
81 : : convert_testexpr_context *context);
82 : : static bool subplan_is_hashable(Plan *plan);
83 : : static bool subpath_is_hashable(Path *path);
84 : : static bool testexpr_is_hashable(Node *testexpr, List *param_ids);
85 : : static bool test_opexpr_is_hashable(OpExpr *testexpr, List *param_ids);
86 : : static bool hash_ok_operator(OpExpr *expr);
87 : : static bool contain_dml(Node *node);
88 : : static bool contain_dml_walker(Node *node, void *context);
89 : : static bool contain_outer_selfref(Node *node);
90 : : static bool contain_outer_selfref_walker(Node *node, Index *depth);
91 : : static void inline_cte(PlannerInfo *root, CommonTableExpr *cte);
92 : : static bool inline_cte_walker(Node *node, inline_cte_walker_context *context);
93 : : static bool simplify_EXISTS_query(PlannerInfo *root, Query *query);
94 : : static Query *convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
95 : : Node **testexpr, List **paramIds);
96 : : static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);
97 : : static Node *process_sublinks_mutator(Node *node,
98 : : process_sublinks_context *context);
99 : : static Bitmapset *finalize_plan(PlannerInfo *root,
100 : : Plan *plan,
101 : : int gather_param,
102 : : Bitmapset *valid_params,
103 : : Bitmapset *scan_params);
104 : : static bool finalize_primnode(Node *node, finalize_primnode_context *context);
105 : : static bool finalize_agg_primnode(Node *node, finalize_primnode_context *context);
106 : :
107 : :
108 : : /*
109 : : * Get the datatype/typmod/collation of the first column of the plan's output.
110 : : *
111 : : * This information is stored for ARRAY_SUBLINK execution and for
112 : : * exprType()/exprTypmod()/exprCollation(), which have no way to get at the
113 : : * plan associated with a SubPlan node. We really only need the info for
114 : : * EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we save it
115 : : * always.
116 : : */
117 : : static void
4741 tgl@sss.pgh.pa.us 118 :CBC 18475 : get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod,
119 : : Oid *colcollation)
120 : : {
121 : : /* In cases such as EXISTS, tlist might be empty; arbitrarily use VOID */
5722 122 [ + + ]: 18475 : if (plan->targetlist)
123 : : {
2561 124 : 17319 : TargetEntry *tent = linitial_node(TargetEntry, plan->targetlist);
125 : :
5722 126 [ + - ]: 17319 : if (!tent->resjunk)
127 : : {
5514 128 : 17319 : *coltype = exprType((Node *) tent->expr);
129 : 17319 : *coltypmod = exprTypmod((Node *) tent->expr);
4814 peter_e@gmx.net 130 : 17319 : *colcollation = exprCollation((Node *) tent->expr);
5514 tgl@sss.pgh.pa.us 131 : 17319 : return;
132 : : }
133 : : }
134 : 1156 : *coltype = VOIDOID;
135 : 1156 : *coltypmod = -1;
4814 peter_e@gmx.net 136 : 1156 : *colcollation = InvalidOid;
137 : : }
138 : :
139 : : /*
140 : : * Convert a SubLink (as created by the parser) into a SubPlan.
141 : : *
142 : : * We are given the SubLink's contained query, type, ID, and testexpr. We are
143 : : * also told if this expression appears at top level of a WHERE/HAVING qual.
144 : : *
145 : : * Note: we assume that the testexpr has been AND/OR flattened (actually,
146 : : * it's been through eval_const_expressions), but not converted to
147 : : * implicit-AND form; and any SubLinks in it should already have been
148 : : * converted to SubPlans. The subquery is as yet untouched, however.
149 : : *
150 : : * The result is whatever we need to substitute in place of the SubLink node
151 : : * in the executable expression. If we're going to do the subplan as a
152 : : * regular subplan, this will be the constructed SubPlan node. If we're going
153 : : * to do the subplan as an InitPlan, the SubPlan node instead goes into
154 : : * root->init_plans, and what we return here is an expression tree
155 : : * representing the InitPlan's result: usually just a Param node representing
156 : : * a single scalar result, but possibly a row comparison tree containing
157 : : * multiple Param nodes, or for a MULTIEXPR subquery a simple NULL constant
158 : : * (since the real output Params are elsewhere in the tree, and the MULTIEXPR
159 : : * subquery itself is in a resjunk tlist entry whose value is uninteresting).
160 : : */
161 : : static Node *
3588 tgl@sss.pgh.pa.us 162 : 16517 : make_subplan(PlannerInfo *root, Query *orig_subquery,
163 : : SubLinkType subLinkType, int subLinkId,
164 : : Node *testexpr, bool isTopQual)
165 : : {
166 : : Query *subquery;
5714 167 : 16517 : bool simple_exists = false;
168 : : double tuple_fraction;
169 : : PlannerInfo *subroot;
170 : : RelOptInfo *final_rel;
171 : : Path *best_path;
172 : : Plan *plan;
173 : : List *plan_params;
174 : : Node *result;
175 : :
176 : : /*
177 : : * Copy the source Query node. This is a quick and dirty kluge to resolve
178 : : * the fact that the parser can generate trees with multiple links to the
179 : : * same sub-Query node, but the planner wants to scribble on the Query.
180 : : * Try to clean this up when we do querytree redesign...
181 : : */
2593 peter_e@gmx.net 182 : 16517 : subquery = copyObject(orig_subquery);
183 : :
184 : : /*
185 : : * If it's an EXISTS subplan, we might be able to simplify it.
186 : : */
5714 tgl@sss.pgh.pa.us 187 [ + + ]: 16517 : if (subLinkType == EXISTS_SUBLINK)
3431 188 : 1032 : simple_exists = simplify_EXISTS_query(root, subquery);
189 : :
190 : : /*
191 : : * For an EXISTS subplan, tell lower-level planner to expect that only the
192 : : * first tuple will be retrieved. For ALL and ANY subplans, we will be
193 : : * able to stop evaluating if the test condition fails or matches, so very
194 : : * often not all the tuples will be retrieved; for lack of a better idea,
195 : : * specify 50% retrieval. For EXPR, MULTIEXPR, and ROWCOMPARE subplans,
196 : : * use default behavior (we're only expecting one row out, anyway).
197 : : *
198 : : * NOTE: if you change these numbers, also change cost_subplan() in
199 : : * path/costsize.c.
200 : : *
201 : : * XXX If an ANY subplan is uncorrelated, build_subplan may decide to hash
202 : : * its output. In that case it would've been better to specify full
203 : : * retrieval. At present, however, we can only check hashability after
204 : : * we've made the subplan :-(. (Determining whether it'll fit in hash_mem
205 : : * is the really hard part.) Therefore, we don't want to be too
206 : : * optimistic about the percentage of tuples retrieved, for fear of
207 : : * selecting a plan that's bad for the materialization case.
208 : : */
5714 209 [ + + ]: 16517 : if (subLinkType == EXISTS_SUBLINK)
8825 210 : 1032 : tuple_fraction = 1.0; /* just like a LIMIT 1 */
5714 211 [ + + + + ]: 15485 : else if (subLinkType == ALL_SUBLINK ||
212 : : subLinkType == ANY_SUBLINK)
8825 213 : 246 : tuple_fraction = 0.5; /* 50% */
214 : : else
7706 215 : 15239 : tuple_fraction = 0.0; /* default behavior */
216 : :
217 : : /* plan_params should not be in use in current query level */
4239 218 [ - + ]: 16517 : Assert(root->plan_params == NIL);
219 : :
220 : : /* Generate Paths for the subquery */
12 drowley@postgresql.o 221 :GNC 16517 : subroot = subquery_planner(root->glob, subquery, root, false,
222 : : tuple_fraction, NULL);
223 : :
224 : : /* Isolate the params needed by this specific subplan */
4239 tgl@sss.pgh.pa.us 225 :CBC 16517 : plan_params = root->plan_params;
226 : 16517 : root->plan_params = NIL;
227 : :
228 : : /*
229 : : * Select best Path and turn it into a Plan. At least for now, there
230 : : * seems no reason to postpone doing that.
231 : : */
2960 232 : 16517 : final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
233 : 16517 : best_path = get_cheapest_fractional_path(final_rel, tuple_fraction);
234 : :
235 : 16517 : plan = create_plan(subroot, best_path);
236 : :
237 : : /* And convert to SubPlan or InitPlan format. */
19 tgl@sss.pgh.pa.us 238 :GNC 16517 : result = build_subplan(root, plan, best_path,
239 : : subroot, plan_params,
240 : : subLinkType, subLinkId,
241 : : testexpr, NIL, isTopQual);
242 : :
243 : : /*
244 : : * If it's a correlated EXISTS with an unimportant targetlist, we might be
245 : : * able to transform it to the equivalent of an IN and then implement it
246 : : * by hashing. We don't have enough information yet to tell which way is
247 : : * likely to be better (it depends on the expected number of executions of
248 : : * the EXISTS qual, and we are much too early in planning the outer query
249 : : * to be able to guess that). So we generate both plans, if possible, and
250 : : * leave it to setrefs.c to decide which to use.
251 : : */
5714 tgl@sss.pgh.pa.us 252 [ + + + + ]:CBC 16517 : if (simple_exists && IsA(result, SubPlan))
253 : : {
254 : : Node *newtestexpr;
255 : : List *paramIds;
256 : :
257 : : /* Make a second copy of the original subquery */
2593 peter_e@gmx.net 258 : 913 : subquery = copyObject(orig_subquery);
259 : : /* and re-simplify */
3431 tgl@sss.pgh.pa.us 260 : 913 : simple_exists = simplify_EXISTS_query(root, subquery);
5714 261 [ - + ]: 913 : Assert(simple_exists);
262 : : /* See if it can be converted to an ANY query */
263 : 913 : subquery = convert_EXISTS_to_ANY(root, subquery,
264 : : &newtestexpr, ¶mIds);
265 [ + + ]: 913 : if (subquery)
266 : : {
267 : : /* Generate Paths for the ANY subquery; we'll need all rows */
12 drowley@postgresql.o 268 :GNC 750 : subroot = subquery_planner(root->glob, subquery, root, false, 0.0,
269 : : NULL);
270 : :
271 : : /* Isolate the params needed by this specific subplan */
4239 tgl@sss.pgh.pa.us 272 :CBC 750 : plan_params = root->plan_params;
273 : 750 : root->plan_params = NIL;
274 : :
275 : : /* Select best Path */
2960 276 : 750 : final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
277 : 750 : best_path = final_rel->cheapest_total_path;
278 : :
279 : : /* Now we can check if it'll fit in hash_mem */
1295 280 [ + - ]: 750 : if (subpath_is_hashable(best_path))
281 : : {
282 : : SubPlan *hashplan;
283 : : AlternativeSubPlan *asplan;
284 : :
285 : : /* OK, finish planning the ANY subquery */
286 : 750 : plan = create_plan(subroot, best_path);
287 : :
288 : : /* ... and convert to SubPlan format */
2609 peter_e@gmx.net 289 : 750 : hashplan = castNode(SubPlan,
290 : : build_subplan(root, plan, best_path,
291 : : subroot, plan_params,
292 : : ANY_SUBLINK, 0,
293 : : newtestexpr,
294 : : paramIds,
295 : : true));
296 : : /* Check we got what we expected */
5714 tgl@sss.pgh.pa.us 297 [ - + ]: 750 : Assert(hashplan->parParam == NIL);
298 [ - + ]: 750 : Assert(hashplan->useHashTable);
299 : :
300 : : /* Leave it to setrefs.c to decide which plan to use */
301 : 750 : asplan = makeNode(AlternativeSubPlan);
302 : 750 : asplan->subplans = list_make2(result, hashplan);
303 : 750 : result = (Node *) asplan;
1295 304 : 750 : root->hasAlternativeSubPlans = true;
305 : : }
306 : : }
307 : : }
308 : :
5714 309 : 16517 : return result;
310 : : }
311 : :
312 : : /*
313 : : * Build a SubPlan node given the raw inputs --- subroutine for make_subplan
314 : : *
315 : : * Returns either the SubPlan, or a replacement expression if we decide to
316 : : * make it an InitPlan, as explained in the comments for make_subplan.
317 : : */
318 : : static Node *
19 tgl@sss.pgh.pa.us 319 :GNC 17267 : build_subplan(PlannerInfo *root, Plan *plan, Path *path,
320 : : PlannerInfo *subroot, List *plan_params,
321 : : SubLinkType subLinkType, int subLinkId,
322 : : Node *testexpr, List *testexpr_paramids,
323 : : bool unknownEqFalse)
324 : : {
325 : : Node *result;
326 : : SubPlan *splan;
327 : : bool isInitPlan;
328 : : ListCell *lc;
329 : :
330 : : /*
331 : : * Initialize the SubPlan node. Note plan_id, plan_name, and cost fields
332 : : * are set further down.
333 : : */
6256 tgl@sss.pgh.pa.us 334 :CBC 17267 : splan = makeNode(SubPlan);
5714 335 : 17267 : splan->subLinkType = subLinkType;
6256 336 : 17267 : splan->testexpr = NULL;
337 : 17267 : splan->paramIds = NIL;
4741 338 : 17267 : get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
339 : : &splan->firstColCollation);
6256 340 : 17267 : splan->useHashTable = false;
5714 341 : 17267 : splan->unknownEqFalse = unknownEqFalse;
2559 342 : 17267 : splan->parallel_safe = plan->parallel_safe;
6256 343 : 17267 : splan->setParam = NIL;
344 : 17267 : splan->parParam = NIL;
345 : 17267 : splan->args = NIL;
346 : :
347 : : /*
348 : : * Make parParam and args lists of param IDs and expressions that current
349 : : * query level will pass to this child plan.
350 : : */
4239 351 [ + + + + : 35815 : foreach(lc, plan_params)
+ + ]
352 : : {
353 : 18548 : PlannerParamItem *pitem = (PlannerParamItem *) lfirst(lc);
354 : 18548 : Node *arg = pitem->item;
355 : :
356 : : /*
357 : : * The Var, PlaceHolderVar, Aggref or GroupingFunc has already been
358 : : * adjusted to have the correct varlevelsup, phlevelsup, or
359 : : * agglevelsup.
360 : : *
361 : : * If it's a PlaceHolderVar, Aggref or GroupingFunc, its arguments
362 : : * might contain SubLinks, which have not yet been processed (see the
363 : : * comments for SS_replace_correlation_vars). Do that now.
364 : : */
365 [ + + ]: 18548 : if (IsA(arg, PlaceHolderVar) ||
755 366 [ + + ]: 18542 : IsA(arg, Aggref) ||
367 [ + + ]: 18516 : IsA(arg, GroupingFunc))
4239 368 : 64 : arg = SS_process_sublinks(root, arg, false);
369 : :
370 : 18548 : splan->parParam = lappend_int(splan->parParam, pitem->paramId);
371 : 18548 : splan->args = lappend(splan->args, arg);
372 : : }
373 : :
374 : : /*
375 : : * Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY,
376 : : * ROWCOMPARE, or MULTIEXPR types can be used as initPlans. For EXISTS,
377 : : * EXPR, or ARRAY, we return a Param referring to the result of evaluating
378 : : * the initPlan. For ROWCOMPARE, we must modify the testexpr tree to
379 : : * contain PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted
380 : : * by the parser, and then return that tree. For MULTIEXPR, we return a
381 : : * null constant: the resjunk targetlist item containing the SubLink does
382 : : * not need to return anything useful, since the referencing Params are
383 : : * elsewhere.
384 : : */
5714 385 [ + + + + ]: 17267 : if (splan->parParam == NIL && subLinkType == EXISTS_SUBLINK)
8917 386 : 103 : {
387 : : Param *prm;
388 : :
5714 389 [ - + ]: 103 : Assert(testexpr == NULL);
1920 390 : 103 : prm = generate_new_exec_param(root, BOOLOID, -1, InvalidOid);
6256 391 : 103 : splan->setParam = list_make1_int(prm->paramid);
392 : 103 : isInitPlan = true;
8917 393 : 103 : result = (Node *) prm;
394 : : }
5714 395 [ + + + + ]: 17164 : else if (splan->parParam == NIL && subLinkType == EXPR_SUBLINK)
8917 396 : 4703 : {
7263 neilc@samurai.com 397 : 4703 : TargetEntry *te = linitial(plan->targetlist);
398 : : Param *prm;
399 : :
6948 tgl@sss.pgh.pa.us 400 [ - + ]: 4703 : Assert(!te->resjunk);
5714 401 [ - + ]: 4703 : Assert(testexpr == NULL);
1920 402 : 4703 : prm = generate_new_exec_param(root,
403 : 4703 : exprType((Node *) te->expr),
404 : 4703 : exprTypmod((Node *) te->expr),
405 : 4703 : exprCollation((Node *) te->expr));
6256 406 : 4703 : splan->setParam = list_make1_int(prm->paramid);
407 : 4703 : isInitPlan = true;
8917 408 : 4703 : result = (Node *) prm;
409 : : }
5714 410 [ + + + + ]: 12461 : else if (splan->parParam == NIL && subLinkType == ARRAY_SUBLINK)
7677 411 : 40 : {
7263 neilc@samurai.com 412 : 40 : TargetEntry *te = linitial(plan->targetlist);
413 : : Oid arraytype;
414 : : Param *prm;
415 : :
6948 tgl@sss.pgh.pa.us 416 [ - + ]: 40 : Assert(!te->resjunk);
5714 417 [ - + ]: 40 : Assert(testexpr == NULL);
3428 418 : 40 : arraytype = get_promoted_array_type(exprType((Node *) te->expr));
7677 419 [ - + ]: 40 : if (!OidIsValid(arraytype))
7569 tgl@sss.pgh.pa.us 420 [ # # ]:UBC 0 : elog(ERROR, "could not find array type for datatype %s",
421 : : format_type_be(exprType((Node *) te->expr)));
1920 tgl@sss.pgh.pa.us 422 :CBC 40 : prm = generate_new_exec_param(root,
423 : : arraytype,
424 : 40 : exprTypmod((Node *) te->expr),
425 : 40 : exprCollation((Node *) te->expr));
6256 426 : 40 : splan->setParam = list_make1_int(prm->paramid);
427 : 40 : isInitPlan = true;
7677 428 : 40 : result = (Node *) prm;
429 : : }
5714 430 [ + + + + ]: 12421 : else if (splan->parParam == NIL && subLinkType == ROWCOMPARE_SUBLINK)
9557 vadim4o@yahoo.com 431 :GBC 9 : {
432 : : /* Adjust the Params */
433 : : List *params;
434 : :
5714 tgl@sss.pgh.pa.us 435 [ - + ]: 9 : Assert(testexpr != NULL);
5932 436 : 9 : params = generate_subquery_params(root,
437 : : plan->targetlist,
438 : : &splan->paramIds);
6264 439 : 9 : result = convert_testexpr(root,
440 : : testexpr,
441 : : params);
6256 442 : 9 : splan->setParam = list_copy(splan->paramIds);
443 : 9 : isInitPlan = true;
444 : :
445 : : /*
446 : : * The executable expression is returned to become part of the outer
447 : : * plan's expression tree; it is not kept in the initplan node.
448 : : */
449 : : }
3588 tgl@sss.pgh.pa.us 450 [ + + ]:CBC 12412 : else if (subLinkType == MULTIEXPR_SUBLINK)
451 : : {
452 : : /*
453 : : * Whether it's an initplan or not, it needs to set a PARAM_EXEC Param
454 : : * for each output column.
455 : : */
456 : : List *params;
457 : :
458 [ - + ]: 66 : Assert(testexpr == NULL);
459 : 66 : params = generate_subquery_params(root,
460 : : plan->targetlist,
461 : : &splan->setParam);
462 : :
463 : : /*
464 : : * Save the list of replacement Params in the n'th cell of
465 : : * root->multiexpr_params; setrefs.c will use it to replace
466 : : * PARAM_MULTIEXPR Params.
467 : : */
468 [ + + ]: 132 : while (list_length(root->multiexpr_params) < subLinkId)
469 : 66 : root->multiexpr_params = lappend(root->multiexpr_params, NIL);
470 : 66 : lc = list_nth_cell(root->multiexpr_params, subLinkId - 1);
471 [ - + ]: 66 : Assert(lfirst(lc) == NIL);
472 : 66 : lfirst(lc) = params;
473 : :
474 : : /* It can be an initplan if there are no parParams. */
475 [ + + ]: 66 : if (splan->parParam == NIL)
476 : : {
477 : 15 : isInitPlan = true;
478 : 15 : result = (Node *) makeNullConst(RECORDOID, -1, InvalidOid);
479 : : }
480 : : else
481 : : {
482 : 51 : isInitPlan = false;
483 : 51 : result = (Node *) splan;
484 : : }
485 : : }
486 : : else
487 : : {
488 : : /*
489 : : * Adjust the Params in the testexpr, unless caller already took care
490 : : * of it (as indicated by passing a list of Param IDs).
491 : : */
1339 492 [ + + + + ]: 12346 : if (testexpr && testexpr_paramids == NIL)
5757 493 : 252 : {
494 : : List *params;
495 : :
496 : 252 : params = generate_subquery_params(root,
497 : : plan->targetlist,
498 : : &splan->paramIds);
499 : 252 : splan->testexpr = convert_testexpr(root,
500 : : testexpr,
501 : : params);
502 : : }
503 : : else
504 : : {
5714 505 : 12094 : splan->testexpr = testexpr;
1339 506 : 12094 : splan->paramIds = testexpr_paramids;
507 : : }
508 : :
509 : : /*
510 : : * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types to
511 : : * initPlans, even when they are uncorrelated or undirect correlated,
512 : : * because we need to scan the output of the subplan for each outer
513 : : * tuple. But if it's a not-direct-correlated IN (= ANY) test, we
514 : : * might be able to use a hashtable to avoid comparing all the tuples.
515 : : */
5714 516 [ + + ]: 12346 : if (subLinkType == ANY_SUBLINK &&
517 [ + + + - ]: 1945 : splan->parParam == NIL &&
518 [ + + ]: 1916 : subplan_is_hashable(plan) &&
1339 519 : 958 : testexpr_is_hashable(splan->testexpr, splan->paramIds))
6256 520 : 946 : splan->useHashTable = true;
521 : :
522 : : /*
523 : : * Otherwise, we have the option to tack a Material node onto the top
524 : : * of the subplan, to reduce the cost of reading it repeatedly. This
525 : : * is pointless for a direct-correlated subplan, since we'd have to
526 : : * recompute its results each time anyway. For uncorrelated/undirect
527 : : * correlated subplans, we add Material unless the subplan's top plan
528 : : * node would materialize its output anyway. Also, if enable_material
529 : : * is false, then the user does not want us to materialize anything
530 : : * unnecessarily, so we don't.
531 : : */
5109 rhaas@postgresql.org 532 [ + + + - ]: 11400 : else if (splan->parParam == NIL && enable_material &&
5328 tgl@sss.pgh.pa.us 533 [ + - ]: 21 : !ExecMaterializesOutput(nodeTag(plan)))
534 : 21 : plan = materialize_finished_plan(plan);
535 : :
6256 536 : 12346 : result = (Node *) splan;
537 : 12346 : isInitPlan = false;
538 : : }
539 : :
540 : : /*
541 : : * Add the subplan, its path, and its PlannerInfo to the global lists.
542 : : */
5714 543 : 17267 : root->glob->subplans = lappend(root->glob->subplans, plan);
19 tgl@sss.pgh.pa.us 544 :GNC 17267 : root->glob->subpaths = lappend(root->glob->subpaths, path);
4607 tgl@sss.pgh.pa.us 545 :CBC 17267 : root->glob->subroots = lappend(root->glob->subroots, subroot);
6256 546 : 17267 : splan->plan_id = list_length(root->glob->subplans);
547 : :
548 [ + + ]: 17267 : if (isInitPlan)
549 : 4870 : root->init_plans = lappend(root->init_plans, splan);
550 : :
551 : : /*
552 : : * A parameterless subplan (not initplan) should be prepared to handle
553 : : * REWIND efficiently. If it has direct parameters then there's no point
554 : : * since it'll be reset on each scan anyway; and if it's an initplan then
555 : : * there's no point since it won't get re-run without parameter changes
556 : : * anyway. The input of a hashed subplan doesn't need REWIND either.
557 : : */
558 [ + + + + : 17267 : if (splan->parParam == NIL && !isInitPlan && !splan->useHashTable)
+ + ]
559 : 21 : root->glob->rewindPlanIDs = bms_add_member(root->glob->rewindPlanIDs,
560 : : splan->plan_id);
561 : :
562 : : /* Label the subplan for EXPLAIN purposes */
26 tgl@sss.pgh.pa.us 563 [ + + ]:GNC 17267 : splan->plan_name = psprintf("%s %d",
564 : : isInitPlan ? "InitPlan" : "SubPlan",
565 : : splan->plan_id);
566 : :
567 : : /* Lastly, fill in the cost estimates for use later */
5714 tgl@sss.pgh.pa.us 568 :CBC 17267 : cost_subplan(root, splan, plan);
569 : :
7810 570 : 17267 : return result;
571 : : }
572 : :
573 : : /*
574 : : * generate_subquery_params: build a list of Params representing the output
575 : : * columns of a sublink's sub-select, given the sub-select's targetlist.
576 : : *
577 : : * We also return an integer list of the paramids of the Params.
578 : : */
579 : : static List *
5932 580 : 327 : generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds)
581 : : {
582 : : List *result;
583 : : List *ids;
584 : : ListCell *lc;
585 : :
586 : 327 : result = ids = NIL;
587 [ + - + + : 778 : foreach(lc, tlist)
+ + ]
588 : : {
589 : 451 : TargetEntry *tent = (TargetEntry *) lfirst(lc);
590 : : Param *param;
591 : :
592 [ + + ]: 451 : if (tent->resjunk)
593 : 3 : continue;
594 : :
1920 595 : 448 : param = generate_new_exec_param(root,
596 : 448 : exprType((Node *) tent->expr),
597 : 448 : exprTypmod((Node *) tent->expr),
598 : 448 : exprCollation((Node *) tent->expr));
5932 599 : 448 : result = lappend(result, param);
600 : 448 : ids = lappend_int(ids, param->paramid);
601 : : }
602 : :
603 : 327 : *paramIds = ids;
604 : 327 : return result;
605 : : }
606 : :
607 : : /*
608 : : * generate_subquery_vars: build a list of Vars representing the output
609 : : * columns of a sublink's sub-select, given the sub-select's targetlist.
610 : : * The Vars have the specified varno (RTE index).
611 : : */
612 : : static List *
613 : 715 : generate_subquery_vars(PlannerInfo *root, List *tlist, Index varno)
614 : : {
615 : : List *result;
616 : : ListCell *lc;
617 : :
618 : 715 : result = NIL;
619 [ + - + + : 1454 : foreach(lc, tlist)
+ + ]
620 : : {
621 : 739 : TargetEntry *tent = (TargetEntry *) lfirst(lc);
622 : : Var *var;
623 : :
624 [ - + ]: 739 : if (tent->resjunk)
5932 tgl@sss.pgh.pa.us 625 :UBC 0 : continue;
626 : :
4979 peter_e@gmx.net 627 :CBC 739 : var = makeVarFromTargetEntry(varno, tent);
5932 tgl@sss.pgh.pa.us 628 : 739 : result = lappend(result, var);
629 : : }
630 : :
631 : 715 : return result;
632 : : }
633 : :
634 : : /*
635 : : * convert_testexpr: convert the testexpr given by the parser into
636 : : * actually executable form. This entails replacing PARAM_SUBLINK Params
637 : : * with Params or Vars representing the results of the sub-select. The
638 : : * nodes to be substituted are passed in as the List result from
639 : : * generate_subquery_params or generate_subquery_vars.
640 : : */
641 : : static Node *
6264 642 : 976 : convert_testexpr(PlannerInfo *root,
643 : : Node *testexpr,
644 : : List *subst_nodes)
645 : : {
646 : : convert_testexpr_context context;
647 : :
648 : 976 : context.root = root;
5932 649 : 976 : context.subst_nodes = subst_nodes;
650 : 976 : return convert_testexpr_mutator(testexpr, &context);
651 : : }
652 : :
653 : : static Node *
6682 654 : 4936 : convert_testexpr_mutator(Node *node,
655 : : convert_testexpr_context *context)
656 : : {
657 [ + + ]: 4936 : if (node == NULL)
658 : 23 : return NULL;
659 [ + + ]: 4913 : if (IsA(node, Param))
660 : : {
6402 bruce@momjian.us 661 : 1062 : Param *param = (Param *) node;
662 : :
6682 tgl@sss.pgh.pa.us 663 [ + - ]: 1062 : if (param->paramkind == PARAM_SUBLINK)
664 : : {
5932 665 [ + - - + ]: 2124 : if (param->paramid <= 0 ||
666 : 1062 : param->paramid > list_length(context->subst_nodes))
6682 tgl@sss.pgh.pa.us 667 [ # # ]:UBC 0 : elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);
668 : :
669 : : /*
670 : : * We copy the list item to avoid having doubly-linked
671 : : * substructure in the modified parse tree. This is probably
672 : : * unnecessary when it's a Param, but be safe.
673 : : */
5932 tgl@sss.pgh.pa.us 674 :CBC 1062 : return (Node *) copyObject(list_nth(context->subst_nodes,
675 : : param->paramid - 1));
676 : : }
677 : : }
3778 678 [ + + ]: 3851 : if (IsA(node, SubLink))
679 : : {
680 : : /*
681 : : * If we come across a nested SubLink, it is neither necessary nor
682 : : * correct to recurse into it: any PARAM_SUBLINKs we might find inside
683 : : * belong to the inner SubLink not the outer. So just return it as-is.
684 : : *
685 : : * This reasoning depends on the assumption that nothing will pull
686 : : * subexpressions into or out of the testexpr field of a SubLink, at
687 : : * least not without replacing PARAM_SUBLINKs first. If we did want
688 : : * to do that we'd need to rethink the parser-output representation
689 : : * altogether, since currently PARAM_SUBLINKs are only unique per
690 : : * SubLink not globally across the query. The whole point of
691 : : * replacing them with Vars or PARAM_EXEC nodes is to make them
692 : : * globally unique before they escape from the SubLink's testexpr.
693 : : *
694 : : * Note: this can't happen when called during SS_process_sublinks,
695 : : * because that recursively processes inner SubLinks first. It can
696 : : * happen when called from convert_ANY_sublink_to_join, though.
697 : : */
698 : 6 : return node;
699 : : }
6682 700 : 3845 : return expression_tree_mutator(node,
701 : : convert_testexpr_mutator,
702 : : (void *) context);
703 : : }
704 : :
705 : : /*
706 : : * subplan_is_hashable: can we implement an ANY subplan by hashing?
707 : : *
708 : : * This is not responsible for checking whether the combining testexpr
709 : : * is suitable for hashing. We only look at the subquery itself.
710 : : */
711 : : static bool
5714 712 : 958 : subplan_is_hashable(Plan *plan)
713 : : {
714 : : double subquery_size;
715 : :
716 : : /*
717 : : * The estimated size of the subquery result must fit in hash_mem. (Note:
718 : : * we use heap tuple overhead here even though the tuples will actually be
719 : : * stored as MinimalTuples; this provides some fudge factor for hashtable
720 : : * overhead.)
721 : : */
6261 722 : 958 : subquery_size = plan->plan_rows *
3340 723 : 958 : (MAXALIGN(plan->plan_width) + MAXALIGN(SizeofHeapTupleHeader));
994 724 [ - + ]: 958 : if (subquery_size > get_hash_memory_limit())
1295 tgl@sss.pgh.pa.us 725 :UBC 0 : return false;
726 : :
1295 tgl@sss.pgh.pa.us 727 :CBC 958 : return true;
728 : : }
729 : :
730 : : /*
731 : : * subpath_is_hashable: can we implement an ANY subplan by hashing?
732 : : *
733 : : * Identical to subplan_is_hashable, but work from a Path for the subplan.
734 : : */
735 : : static bool
736 : 750 : subpath_is_hashable(Path *path)
737 : : {
738 : : double subquery_size;
739 : :
740 : : /*
741 : : * The estimated size of the subquery result must fit in hash_mem. (Note:
742 : : * we use heap tuple overhead here even though the tuples will actually be
743 : : * stored as MinimalTuples; this provides some fudge factor for hashtable
744 : : * overhead.)
745 : : */
746 : 750 : subquery_size = path->rows *
747 : 750 : (MAXALIGN(path->pathtarget->width) + MAXALIGN(SizeofHeapTupleHeader));
994 748 [ - + ]: 750 : if (subquery_size > get_hash_memory_limit())
7765 tgl@sss.pgh.pa.us 749 :UBC 0 : return false;
750 : :
5714 tgl@sss.pgh.pa.us 751 :CBC 750 : return true;
752 : : }
753 : :
754 : : /*
755 : : * testexpr_is_hashable: is an ANY SubLink's test expression hashable?
756 : : *
757 : : * To identify LHS vs RHS of the hash expression, we must be given the
758 : : * list of output Param IDs of the SubLink's subquery.
759 : : */
760 : : static bool
1339 761 : 958 : testexpr_is_hashable(Node *testexpr, List *param_ids)
762 : : {
763 : : /*
764 : : * The testexpr must be a single OpExpr, or an AND-clause containing only
765 : : * OpExprs, each of which satisfy test_opexpr_is_hashable().
766 : : */
5714 767 [ + - + + ]: 958 : if (testexpr && IsA(testexpr, OpExpr))
768 : : {
1339 769 [ + + ]: 537 : if (test_opexpr_is_hashable((OpExpr *) testexpr, param_ids))
5714 770 : 525 : return true;
771 : : }
1902 772 [ + - ]: 421 : else if (is_andclause(testexpr))
773 : : {
774 : : ListCell *l;
775 : :
5714 776 [ + - + + : 1263 : foreach(l, ((BoolExpr *) testexpr)->args)
+ + ]
777 : : {
6402 bruce@momjian.us 778 : 842 : Node *andarg = (Node *) lfirst(l);
779 : :
6682 tgl@sss.pgh.pa.us 780 [ - + ]: 842 : if (!IsA(andarg, OpExpr))
5714 tgl@sss.pgh.pa.us 781 :UBC 0 : return false;
1339 tgl@sss.pgh.pa.us 782 [ - + ]:CBC 842 : if (!test_opexpr_is_hashable((OpExpr *) andarg, param_ids))
6682 tgl@sss.pgh.pa.us 783 :UBC 0 : return false;
784 : : }
5714 tgl@sss.pgh.pa.us 785 :CBC 421 : return true;
786 : : }
787 : :
788 : 12 : return false;
789 : : }
790 : :
791 : : static bool
1339 792 : 1379 : test_opexpr_is_hashable(OpExpr *testexpr, List *param_ids)
793 : : {
794 : : /*
795 : : * The combining operator must be hashable and strict. The need for
796 : : * hashability is obvious, since we want to use hashing. Without
797 : : * strictness, behavior in the presence of nulls is too unpredictable. We
798 : : * actually must assume even more than plain strictness: it can't yield
799 : : * NULL for non-null inputs, either (see nodeSubplan.c). However, hash
800 : : * indexes and hash joins assume that too.
801 : : */
802 [ + + ]: 1379 : if (!hash_ok_operator(testexpr))
803 : 6 : return false;
804 : :
805 : : /*
806 : : * The left and right inputs must belong to the outer and inner queries
807 : : * respectively; hence Params that will be supplied by the subquery must
808 : : * not appear in the LHS, and Vars of the outer query must not appear in
809 : : * the RHS. (Ordinarily, this must be true because of the way that the
810 : : * parser builds an ANY SubLink's testexpr ... but inlining of functions
811 : : * could have changed the expression's structure, so we have to check.
812 : : * Such cases do not occur often enough to be worth trying to optimize, so
813 : : * we don't worry about trying to commute the clause or anything like
814 : : * that; we just need to be sure not to build an invalid plan.)
815 : : */
816 [ - + ]: 1373 : if (list_length(testexpr->args) != 2)
1339 tgl@sss.pgh.pa.us 817 :UBC 0 : return false;
1339 tgl@sss.pgh.pa.us 818 [ + + ]:CBC 1373 : if (contain_exec_param((Node *) linitial(testexpr->args), param_ids))
819 : 6 : return false;
820 [ - + ]: 1367 : if (contain_var_clause((Node *) lsecond(testexpr->args)))
1339 tgl@sss.pgh.pa.us 821 :UBC 0 : return false;
1339 tgl@sss.pgh.pa.us 822 :CBC 1367 : return true;
823 : : }
824 : :
825 : : /*
826 : : * Check expression is hashable + strict
827 : : *
828 : : * We could use op_hashjoinable() and op_strict(), but do it like this to
829 : : * avoid a redundant cache lookup.
830 : : */
831 : : static bool
6682 832 : 3965 : hash_ok_operator(OpExpr *expr)
833 : : {
834 : 3965 : Oid opid = expr->opno;
835 : :
836 : : /* quick out if not a binary operator */
5714 837 [ - + ]: 3965 : if (list_length(expr->args) != 2)
5714 tgl@sss.pgh.pa.us 838 :UBC 0 : return false;
819 tgl@sss.pgh.pa.us 839 [ + - + + ]:CBC 3965 : if (opid == ARRAY_EQ_OP ||
840 : : opid == RECORD_EQ_OP)
841 : : {
842 : : /* these are strict, but must check input type to ensure hashable */
4915 843 : 6 : Node *leftarg = linitial(expr->args);
844 : :
845 : 6 : return op_hashjoinable(opid, exprType(leftarg));
846 : : }
847 : : else
848 : : {
849 : : /* else must look up the operator properties */
850 : : HeapTuple tup;
851 : : Form_pg_operator optup;
852 : :
853 : 3959 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opid));
854 [ - + ]: 3959 : if (!HeapTupleIsValid(tup))
4915 tgl@sss.pgh.pa.us 855 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", opid);
4915 tgl@sss.pgh.pa.us 856 :CBC 3959 : optup = (Form_pg_operator) GETSTRUCT(tup);
857 [ + + - + ]: 3959 : if (!optup->oprcanhash || !func_strict(optup->oprcode))
858 : : {
859 : 311 : ReleaseSysCache(tup);
860 : 311 : return false;
861 : : }
7765 862 : 3648 : ReleaseSysCache(tup);
4915 863 : 3648 : return true;
864 : : }
865 : : }
866 : :
867 : :
868 : : /*
869 : : * SS_process_ctes: process a query's WITH list
870 : : *
871 : : * Consider each CTE in the WITH list and either ignore it (if it's an
872 : : * unreferenced SELECT), "inline" it to create a regular sub-SELECT-in-FROM,
873 : : * or convert it to an initplan.
874 : : *
875 : : * A side effect is to fill in root->cte_plan_ids with a list that
876 : : * parallels root->parse->cteList and provides the subplan ID for
877 : : * each CTE's initplan, or a dummy ID (-1) if we didn't make an initplan.
878 : : */
879 : : void
5671 880 : 1205 : SS_process_ctes(PlannerInfo *root)
881 : : {
882 : : ListCell *lc;
883 : :
884 [ - + ]: 1205 : Assert(root->cte_plan_ids == NIL);
885 : :
886 [ + - + + : 2913 : foreach(lc, root->parse->cteList)
+ + ]
887 : : {
888 : 1711 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
4797 889 : 1711 : CmdType cmdType = ((Query *) cte->ctequery)->commandType;
890 : : Query *subquery;
891 : : PlannerInfo *subroot;
892 : : RelOptInfo *final_rel;
893 : : Path *best_path;
894 : : Plan *plan;
895 : : SubPlan *splan;
896 : : int paramid;
897 : :
898 : : /*
899 : : * Ignore SELECT CTEs that are not actually referenced anywhere.
900 : : */
901 [ + + + + ]: 1711 : if (cte->cterefcount == 0 && cmdType == CMD_SELECT)
902 : : {
903 : : /* Make a dummy entry in cte_plan_ids */
5671 904 : 9 : root->cte_plan_ids = lappend_int(root->cte_plan_ids, -1);
905 : 688 : continue;
906 : : }
907 : :
908 : : /*
909 : : * Consider inlining the CTE (creating RTE_SUBQUERY RTE(s)) instead of
910 : : * implementing it as a separately-planned CTE.
911 : : *
912 : : * We cannot inline if any of these conditions hold:
913 : : *
914 : : * 1. The user said not to (the CTEMaterializeAlways option).
915 : : *
916 : : * 2. The CTE is recursive.
917 : : *
918 : : * 3. The CTE has side-effects; this includes either not being a plain
919 : : * SELECT, or containing volatile functions. Inlining might change
920 : : * the side-effects, which would be bad.
921 : : *
922 : : * 4. The CTE is multiply-referenced and contains a self-reference to
923 : : * a recursive CTE outside itself. Inlining would result in multiple
924 : : * recursive self-references, which we don't support.
925 : : *
926 : : * Otherwise, we have an option whether to inline or not. That should
927 : : * always be a win if there's just a single reference, but if the CTE
928 : : * is multiply-referenced then it's unclear: inlining adds duplicate
929 : : * computations, but the ability to absorb restrictions from the outer
930 : : * query level could outweigh that. We do not have nearly enough
931 : : * information at this point to tell whether that's true, so we let
932 : : * the user express a preference. Our default behavior is to inline
933 : : * only singly-referenced CTEs, but a CTE marked CTEMaterializeNever
934 : : * will be inlined even if multiply referenced.
935 : : *
936 : : * Note: we check for volatile functions last, because that's more
937 : : * expensive than the other tests needed.
938 : : */
1884 939 [ + + ]: 1702 : if ((cte->ctematerialized == CTEMaterializeNever ||
940 [ + + ]: 1678 : (cte->ctematerialized == CTEMaterializeDefault &&
941 [ + + ]: 1583 : cte->cterefcount == 1)) &&
942 [ + + + + ]: 1227 : !cte->cterecursive &&
943 : 722 : cmdType == CMD_SELECT &&
944 [ + + ]: 722 : !contain_dml(cte->ctequery) &&
1832 945 [ + + ]: 718 : (cte->cterefcount <= 1 ||
946 [ + + ]: 18 : !contain_outer_selfref(cte->ctequery)) &&
1884 947 [ + + ]: 712 : !contain_volatile_functions(cte->ctequery))
948 : : {
949 : 679 : inline_cte(root, cte);
950 : : /* Make a dummy entry in cte_plan_ids */
951 : 679 : root->cte_plan_ids = lappend_int(root->cte_plan_ids, -1);
952 : 679 : continue;
953 : : }
954 : :
955 : : /*
956 : : * Copy the source Query node. Probably not necessary, but let's keep
957 : : * this similar to make_subplan.
958 : : */
5671 959 : 1023 : subquery = (Query *) copyObject(cte->ctequery);
960 : :
961 : : /* plan_params should not be in use in current query level */
4239 962 [ - + ]: 1023 : Assert(root->plan_params == NIL);
963 : :
964 : : /*
965 : : * Generate Paths for the CTE query. Always plan for full retrieval
966 : : * --- we don't have enough info to predict otherwise.
967 : : */
12 drowley@postgresql.o 968 :GNC 1023 : subroot = subquery_planner(root->glob, subquery, root,
969 : 1023 : cte->cterecursive, 0.0, NULL);
970 : :
971 : : /*
972 : : * Since the current query level doesn't yet contain any RTEs, it
973 : : * should not be possible for the CTE to have requested parameters of
974 : : * this level.
975 : : */
4239 tgl@sss.pgh.pa.us 976 [ - + ]:CBC 1020 : if (root->plan_params)
4239 tgl@sss.pgh.pa.us 977 [ # # ]:UBC 0 : elog(ERROR, "unexpected outer reference in CTE query");
978 : :
979 : : /*
980 : : * Select best Path and turn it into a Plan. At least for now, there
981 : : * seems no reason to postpone doing that.
982 : : */
2960 tgl@sss.pgh.pa.us 983 :CBC 1020 : final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
984 : 1020 : best_path = final_rel->cheapest_total_path;
985 : :
986 : 1020 : plan = create_plan(subroot, best_path);
987 : :
988 : : /*
989 : : * Make a SubPlan node for it. This is just enough unlike
990 : : * build_subplan that we can't share code.
991 : : *
992 : : * Note plan_id, plan_name, and cost fields are set further down.
993 : : */
5671 994 : 1020 : splan = makeNode(SubPlan);
995 : 1020 : splan->subLinkType = CTE_SUBLINK;
996 : 1020 : splan->testexpr = NULL;
997 : 1020 : splan->paramIds = NIL;
4741 998 : 1020 : get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
999 : : &splan->firstColCollation);
5671 1000 : 1020 : splan->useHashTable = false;
1001 : 1020 : splan->unknownEqFalse = false;
1002 : :
1003 : : /*
1004 : : * CTE scans are not considered for parallelism (cf
1005 : : * set_rel_consider_parallel).
1006 : : */
2616 rhaas@postgresql.org 1007 : 1020 : splan->parallel_safe = false;
5671 tgl@sss.pgh.pa.us 1008 : 1020 : splan->setParam = NIL;
1009 : 1020 : splan->parParam = NIL;
1010 : 1020 : splan->args = NIL;
1011 : :
1012 : : /*
1013 : : * The node can't have any inputs (since it's an initplan), so the
1014 : : * parParam and args lists remain empty. (It could contain references
1015 : : * to earlier CTEs' output param IDs, but CTE outputs are not
1016 : : * propagated via the args list.)
1017 : : */
1018 : :
1019 : : /*
1020 : : * Assign a param ID to represent the CTE's output. No ordinary
1021 : : * "evaluation" of this param slot ever happens, but we use the param
1022 : : * ID for setParam/chgParam signaling just as if the CTE plan were
1023 : : * returning a simple scalar output. (Also, the executor abuses the
1024 : : * ParamExecData slot for this param ID for communication among
1025 : : * multiple CteScan nodes that might be scanning this CTE.)
1026 : : */
1920 1027 : 1020 : paramid = assign_special_exec_param(root);
4239 1028 : 1020 : splan->setParam = list_make1_int(paramid);
1029 : :
1030 : : /*
1031 : : * Add the subplan, its path, and its PlannerInfo to the global lists.
1032 : : */
5671 1033 : 1020 : root->glob->subplans = lappend(root->glob->subplans, plan);
19 tgl@sss.pgh.pa.us 1034 :GNC 1020 : root->glob->subpaths = lappend(root->glob->subpaths, best_path);
4607 tgl@sss.pgh.pa.us 1035 :CBC 1020 : root->glob->subroots = lappend(root->glob->subroots, subroot);
5671 1036 : 1020 : splan->plan_id = list_length(root->glob->subplans);
1037 : :
1038 : 1020 : root->init_plans = lappend(root->init_plans, splan);
1039 : :
1040 : 1020 : root->cte_plan_ids = lappend_int(root->cte_plan_ids, splan->plan_id);
1041 : :
1042 : : /* Label the subplan for EXPLAIN purposes */
3836 peter_e@gmx.net 1043 : 1020 : splan->plan_name = psprintf("CTE %s", cte->ctename);
1044 : :
1045 : : /* Lastly, fill in the cost estimates for use later */
5671 tgl@sss.pgh.pa.us 1046 : 1020 : cost_subplan(root, splan, plan);
1047 : : }
1048 : 1202 : }
1049 : :
1050 : : /*
1051 : : * contain_dml: is any subquery not a plain SELECT?
1052 : : *
1053 : : * We reject SELECT FOR UPDATE/SHARE as well as INSERT etc.
1054 : : */
1055 : : static bool
1884 1056 : 722 : contain_dml(Node *node)
1057 : : {
1058 : 722 : return contain_dml_walker(node, NULL);
1059 : : }
1060 : :
1061 : : static bool
1062 : 49495 : contain_dml_walker(Node *node, void *context)
1063 : : {
1064 [ + + ]: 49495 : if (node == NULL)
1065 : 16016 : return false;
1066 [ + + ]: 33479 : if (IsA(node, Query))
1067 : : {
1068 : 1254 : Query *query = (Query *) node;
1069 : :
1070 [ + - ]: 1254 : if (query->commandType != CMD_SELECT ||
1071 [ + + ]: 1254 : query->rowMarks != NIL)
1072 : 4 : return true;
1073 : :
1074 : 1250 : return query_tree_walker(query, contain_dml_walker, context, 0);
1075 : : }
1076 : 32225 : return expression_tree_walker(node, contain_dml_walker, context);
1077 : : }
1078 : :
1079 : : /*
1080 : : * contain_outer_selfref: is there an external recursive self-reference?
1081 : : */
1082 : : static bool
1832 1083 : 18 : contain_outer_selfref(Node *node)
1084 : : {
1085 : 18 : Index depth = 0;
1086 : :
1087 : : /*
1088 : : * We should be starting with a Query, so that depth will be 1 while
1089 : : * examining its immediate contents.
1090 : : */
1091 [ - + ]: 18 : Assert(IsA(node, Query));
1092 : :
1093 : 18 : return contain_outer_selfref_walker(node, &depth);
1094 : : }
1095 : :
1096 : : static bool
1097 : 405 : contain_outer_selfref_walker(Node *node, Index *depth)
1098 : : {
1099 [ + + ]: 405 : if (node == NULL)
1100 : 243 : return false;
1101 [ + + ]: 162 : if (IsA(node, RangeTblEntry))
1102 : : {
1103 : 15 : RangeTblEntry *rte = (RangeTblEntry *) node;
1104 : :
1105 : : /*
1106 : : * Check for a self-reference to a CTE that's above the Query that our
1107 : : * search started at.
1108 : : */
1109 [ + + ]: 15 : if (rte->rtekind == RTE_CTE &&
1110 [ + - ]: 6 : rte->self_reference &&
1111 [ + - ]: 6 : rte->ctelevelsup >= *depth)
1112 : 6 : return true;
1113 : 9 : return false; /* allow range_table_walker to continue */
1114 : : }
1115 [ + + ]: 147 : if (IsA(node, Query))
1116 : : {
1117 : : /* Recurse into subquery, tracking nesting depth properly */
1118 : 21 : Query *query = (Query *) node;
1119 : : bool result;
1120 : :
1121 : 21 : (*depth)++;
1122 : :
1123 : 21 : result = query_tree_walker(query, contain_outer_selfref_walker,
1124 : : (void *) depth, QTW_EXAMINE_RTES_BEFORE);
1125 : :
1126 : 21 : (*depth)--;
1127 : :
1128 : 21 : return result;
1129 : : }
1130 : 126 : return expression_tree_walker(node, contain_outer_selfref_walker,
1131 : : (void *) depth);
1132 : : }
1133 : :
1134 : : /*
1135 : : * inline_cte: convert RTE_CTE references to given CTE into RTE_SUBQUERYs
1136 : : */
1137 : : static void
1884 1138 : 679 : inline_cte(PlannerInfo *root, CommonTableExpr *cte)
1139 : : {
1140 : : struct inline_cte_walker_context context;
1141 : :
1142 : 679 : context.ctename = cte->ctename;
1143 : : /* Start at levelsup = -1 because we'll immediately increment it */
1144 : 679 : context.levelsup = -1;
1145 : 679 : context.ctequery = castNode(Query, cte->ctequery);
1146 : :
1147 : 679 : (void) inline_cte_walker((Node *) root->parse, &context);
1148 : 679 : }
1149 : :
1150 : : static bool
1151 : 249182 : inline_cte_walker(Node *node, inline_cte_walker_context *context)
1152 : : {
1153 [ + + ]: 249182 : if (node == NULL)
1154 : 66960 : return false;
1155 [ + + ]: 182222 : if (IsA(node, Query))
1156 : : {
1157 : 4921 : Query *query = (Query *) node;
1158 : :
1159 : 4921 : context->levelsup++;
1160 : :
1161 : : /*
1162 : : * Visit the query's RTE nodes after their contents; otherwise
1163 : : * query_tree_walker would descend into the newly inlined CTE query,
1164 : : * which we don't want.
1165 : : */
1166 : 4921 : (void) query_tree_walker(query, inline_cte_walker, context,
1167 : : QTW_EXAMINE_RTES_AFTER);
1168 : :
1169 : 4921 : context->levelsup--;
1170 : :
1171 : 4921 : return false;
1172 : : }
1173 [ + + ]: 177301 : else if (IsA(node, RangeTblEntry))
1174 : : {
1175 : 9068 : RangeTblEntry *rte = (RangeTblEntry *) node;
1176 : :
1177 [ + + ]: 9068 : if (rte->rtekind == RTE_CTE &&
1178 [ + + ]: 2726 : strcmp(rte->ctename, context->ctename) == 0 &&
1179 [ + + ]: 694 : rte->ctelevelsup == context->levelsup)
1180 : : {
1181 : : /*
1182 : : * Found a reference to replace. Generate a copy of the CTE query
1183 : : * with appropriate level adjustment for outer references (e.g.,
1184 : : * to other CTEs).
1185 : : */
1186 : 691 : Query *newquery = copyObject(context->ctequery);
1187 : :
1188 [ + + ]: 691 : if (context->levelsup > 0)
1189 : 417 : IncrementVarSublevelsUp((Node *) newquery, context->levelsup, 1);
1190 : :
1191 : : /*
1192 : : * Convert the RTE_CTE RTE into a RTE_SUBQUERY.
1193 : : *
1194 : : * Historically, a FOR UPDATE clause has been treated as extending
1195 : : * into views and subqueries, but not into CTEs. We preserve this
1196 : : * distinction by not trying to push rowmarks into the new
1197 : : * subquery.
1198 : : */
1199 : 691 : rte->rtekind = RTE_SUBQUERY;
1200 : 691 : rte->subquery = newquery;
1201 : 691 : rte->security_barrier = false;
1202 : :
1203 : : /* Zero out CTE-specific fields */
1204 : 691 : rte->ctename = NULL;
1205 : 691 : rte->ctelevelsup = 0;
1206 : 691 : rte->self_reference = false;
1207 : 691 : rte->coltypes = NIL;
1208 : 691 : rte->coltypmods = NIL;
1209 : 691 : rte->colcollations = NIL;
1210 : : }
1211 : :
1212 : 9068 : return false;
1213 : : }
1214 : :
1215 : 168233 : return expression_tree_walker(node, inline_cte_walker, context);
1216 : : }
1217 : :
1218 : :
1219 : : /*
1220 : : * convert_ANY_sublink_to_join: try to convert an ANY SubLink to a join
1221 : : *
1222 : : * The caller has found an ANY SubLink at the top level of one of the query's
1223 : : * qual clauses, but has not checked the properties of the SubLink further.
1224 : : * Decide whether it is appropriate to process this SubLink in join style.
1225 : : * If so, form a JoinExpr and return it. Return NULL if the SubLink cannot
1226 : : * be converted to a join.
1227 : : *
1228 : : * The only non-obvious input parameter is available_rels: this is the set
1229 : : * of query rels that can safely be referenced in the sublink expression.
1230 : : * (We must restrict this to avoid changing the semantics when a sublink
1231 : : * is present in an outer join's ON qual.) The conversion must fail if
1232 : : * the converted qual would reference any but these parent-query relids.
1233 : : *
1234 : : * On success, the returned JoinExpr has larg = NULL and rarg = the jointree
1235 : : * item representing the pulled-up subquery. The caller must set larg to
1236 : : * represent the relation(s) on the lefthand side of the new join, and insert
1237 : : * the JoinExpr into the upper query's jointree at an appropriate place
1238 : : * (typically, where the lefthand relation(s) had been). Note that the
1239 : : * passed-in SubLink must also be removed from its original position in the
1240 : : * query quals, since the quals of the returned JoinExpr replace it.
1241 : : * (Notionally, we replace the SubLink with a constant TRUE, then elide the
1242 : : * redundant constant from the qual.)
1243 : : *
1244 : : * On success, the caller is also responsible for recursively applying
1245 : : * pull_up_sublinks processing to the rarg and quals of the returned JoinExpr.
1246 : : * (On failure, there is no need to do anything, since pull_up_sublinks will
1247 : : * be applied when we recursively plan the sub-select.)
1248 : : *
1249 : : * Side effects of a successful conversion include adding the SubLink's
1250 : : * subselect to the query's rangetable, so that it can be referenced in
1251 : : * the JoinExpr's rarg.
1252 : : */
1253 : : JoinExpr *
5719 1254 : 765 : convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
1255 : : Relids available_rels)
1256 : : {
1257 : : JoinExpr *result;
6888 1258 : 765 : Query *parse = root->parse;
7755 1259 : 765 : Query *subselect = (Query *) sublink->subselect;
1260 : : Relids upper_varnos;
1261 : : int rtindex;
1262 : : ParseNamespaceItem *nsitem;
1263 : : RangeTblEntry *rte;
1264 : : RangeTblRef *rtr;
1265 : : List *subquery_vars;
1266 : : Node *quals;
1267 : : ParseState *pstate;
1268 : : Relids sub_ref_outer_relids;
1269 : : bool use_lateral;
1270 : :
5722 1271 [ - + ]: 765 : Assert(sublink->subLinkType == ANY_SUBLINK);
1272 : :
1273 : : /*
1274 : : * If the sub-select refers to any Vars of the parent query, we so let's
1275 : : * considering it as LATERAL. (Vars of higher levels don't matter here.)
1276 : : */
59 akorotkov@postgresql 1277 :GNC 765 : sub_ref_outer_relids = pull_varnos_of_level(NULL, (Node *) subselect, 1);
1278 : 765 : use_lateral = !bms_is_empty(sub_ref_outer_relids);
1279 : :
1280 : : /*
1281 : : * Check that sub-select refers nothing outside of available_rels of the
1282 : : * parent query.
1283 : : */
1284 [ + + ]: 765 : if (!bms_is_subset(sub_ref_outer_relids, available_rels))
5527 tgl@sss.pgh.pa.us 1285 :CBC 6 : return NULL;
1286 : :
1287 : : /*
1288 : : * The test expression must contain some Vars of the parent query, else
1289 : : * it's not gonna be a join. (Note that it won't have Vars referring to
1290 : : * the subquery, rather Params.)
1291 : : */
1179 1292 : 759 : upper_varnos = pull_varnos(root, sublink->testexpr);
5527 1293 [ + + ]: 759 : if (bms_is_empty(upper_varnos))
1294 : 6 : return NULL;
1295 : :
1296 : : /*
1297 : : * However, it can't refer to anything outside available_rels.
1298 : : */
1299 [ + + ]: 753 : if (!bms_is_subset(upper_varnos, available_rels))
5527 tgl@sss.pgh.pa.us 1300 :GBC 6 : return NULL;
1301 : :
1302 : : /*
1303 : : * The combining operators and left-hand expressions mustn't be volatile.
1304 : : */
6682 tgl@sss.pgh.pa.us 1305 [ + + ]:CBC 747 : if (contain_volatile_functions(sublink->testexpr))
5527 1306 : 32 : return NULL;
1307 : :
1308 : : /* Create a dummy ParseState for addRangeTableEntryForSubquery */
3322 rhaas@postgresql.org 1309 : 715 : pstate = make_parsestate(NULL);
1310 : :
1311 : : /*
1312 : : * Okay, pull up the sub-select into upper range table.
1313 : : *
1314 : : * We rely here on the assumption that the outer query has no references
1315 : : * to the inner (necessarily true, other than the Vars that we build
1316 : : * below). Therefore this is a lot easier than what pull_up_subqueries has
1317 : : * to go through.
1318 : : */
1564 tgl@sss.pgh.pa.us 1319 : 715 : nsitem = addRangeTableEntryForSubquery(pstate,
1320 : : subselect,
1321 : : makeAlias("ANY_subquery", NIL),
1322 : : use_lateral,
1323 : : false);
1324 : 715 : rte = nsitem->p_rte;
7755 1325 : 715 : parse->rtable = lappend(parse->rtable, rte);
7259 neilc@samurai.com 1326 : 715 : rtindex = list_length(parse->rtable);
1327 : :
1328 : : /*
1329 : : * Form a RangeTblRef for the pulled-up sub-select.
1330 : : */
5719 tgl@sss.pgh.pa.us 1331 : 715 : rtr = makeNode(RangeTblRef);
1332 : 715 : rtr->rtindex = rtindex;
1333 : :
1334 : : /*
1335 : : * Build a list of Vars representing the subselect outputs.
1336 : : */
5837 1337 : 715 : subquery_vars = generate_subquery_vars(root,
1338 : : subselect->targetList,
1339 : : rtindex);
1340 : :
1341 : : /*
1342 : : * Build the new join's qual expression, replacing Params with these Vars.
1343 : : */
5527 1344 : 715 : quals = convert_testexpr(root, sublink->testexpr, subquery_vars);
1345 : :
1346 : : /*
1347 : : * And finally, build the JoinExpr node.
1348 : : */
1349 : 715 : result = makeNode(JoinExpr);
1350 : 715 : result->jointype = JOIN_SEMI;
1351 : 715 : result->isNatural = false;
1352 : 715 : result->larg = NULL; /* caller must fill this in */
1353 : 715 : result->rarg = (Node *) rtr;
5386 peter_e@gmx.net 1354 : 715 : result->usingClause = NIL;
1110 peter@eisentraut.org 1355 : 715 : result->join_using_alias = NULL;
5527 tgl@sss.pgh.pa.us 1356 : 715 : result->quals = quals;
1357 : 715 : result->alias = NULL;
1358 : 715 : result->rtindex = 0; /* we don't need an RTE for it */
1359 : :
1360 : 715 : return result;
1361 : : }
1362 : :
1363 : : /*
1364 : : * convert_EXISTS_sublink_to_join: try to convert an EXISTS SubLink to a join
1365 : : *
1366 : : * The API of this function is identical to convert_ANY_sublink_to_join's,
1367 : : * except that we also support the case where the caller has found NOT EXISTS,
1368 : : * so we need an additional input parameter "under_not".
1369 : : */
1370 : : JoinExpr *
5722 1371 : 1616 : convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
1372 : : bool under_not, Relids available_rels)
1373 : : {
1374 : : JoinExpr *result;
1375 : 1616 : Query *parse = root->parse;
1376 : 1616 : Query *subselect = (Query *) sublink->subselect;
1377 : : Node *whereClause;
1378 : : int rtoffset;
1379 : : int varno;
1380 : : Relids clause_varnos;
1381 : : Relids upper_varnos;
1382 : :
1383 [ - + ]: 1616 : Assert(sublink->subLinkType == EXISTS_SUBLINK);
1384 : :
1385 : : /*
1386 : : * Can't flatten if it contains WITH. (We could arrange to pull up the
1387 : : * WITH into the parent query's cteList, but that risks changing the
1388 : : * semantics, since a WITH ought to be executed once per associated query
1389 : : * call.) Note that convert_ANY_sublink_to_join doesn't have to reject
1390 : : * this case, since it just produces a subquery RTE that doesn't have to
1391 : : * get flattened into the parent query.
1392 : : */
5200 1393 [ - + ]: 1616 : if (subselect->cteList)
5200 tgl@sss.pgh.pa.us 1394 :UBC 0 : return NULL;
1395 : :
1396 : : /*
1397 : : * Copy the subquery so we can modify it safely (see comments in
1398 : : * make_subplan).
1399 : : */
2593 peter_e@gmx.net 1400 :CBC 1616 : subselect = copyObject(subselect);
1401 : :
1402 : : /*
1403 : : * See if the subquery can be simplified based on the knowledge that it's
1404 : : * being used in EXISTS(). If we aren't able to get rid of its
1405 : : * targetlist, we have to fail, because the pullup operation leaves us
1406 : : * with noplace to evaluate the targetlist.
1407 : : */
3431 tgl@sss.pgh.pa.us 1408 [ + + ]: 1616 : if (!simplify_EXISTS_query(root, subselect))
5527 1409 : 16 : return NULL;
1410 : :
1411 : : /*
1412 : : * Separate out the WHERE clause. (We could theoretically also remove
1413 : : * top-level plain JOIN/ON clauses, but it's probably not worth the
1414 : : * trouble.)
1415 : : */
5722 1416 : 1600 : whereClause = subselect->jointree->quals;
1417 : 1600 : subselect->jointree->quals = NULL;
1418 : :
1419 : : /*
1420 : : * The rest of the sub-select must not refer to any Vars of the parent
1421 : : * query. (Vars of higher levels should be okay, though.)
1422 : : */
1423 [ + + ]: 1600 : if (contain_vars_of_level((Node *) subselect, 1))
5527 1424 : 6 : return NULL;
1425 : :
1426 : : /*
1427 : : * On the other hand, the WHERE clause must contain some Vars of the
1428 : : * parent query, else it's not gonna be a join.
1429 : : */
5722 1430 [ + + ]: 1594 : if (!contain_vars_of_level(whereClause, 1))
5527 1431 : 28 : return NULL;
1432 : :
1433 : : /*
1434 : : * We don't risk optimizing if the WHERE clause is volatile, either.
1435 : : */
5722 1436 [ - + ]: 1566 : if (contain_volatile_functions(whereClause))
5527 tgl@sss.pgh.pa.us 1437 :UBC 0 : return NULL;
1438 : :
1439 : : /*
1440 : : * The subquery must have a nonempty jointree, but we can make it so.
1441 : : */
1903 tgl@sss.pgh.pa.us 1442 :CBC 1566 : replace_empty_jointree(subselect);
1443 : :
1444 : : /*
1445 : : * Prepare to pull up the sub-select into top range table.
1446 : : *
1447 : : * We rely here on the assumption that the outer query has no references
1448 : : * to the inner (necessarily true). Therefore this is a lot easier than
1449 : : * what pull_up_subqueries has to go through.
1450 : : *
1451 : : * In fact, it's even easier than what convert_ANY_sublink_to_join has to
1452 : : * do. The machinations of simplify_EXISTS_query ensured that there is
1453 : : * nothing interesting in the subquery except an rtable and jointree, and
1454 : : * even the jointree FromExpr no longer has quals. So we can just append
1455 : : * the rtable to our own and use the FromExpr in our jointree. But first,
1456 : : * adjust all level-zero varnos in the subquery to account for the rtable
1457 : : * merger.
1458 : : */
5722 1459 : 1566 : rtoffset = list_length(parse->rtable);
1460 : 1566 : OffsetVarNodes((Node *) subselect, rtoffset, 0);
1461 : 1566 : OffsetVarNodes(whereClause, rtoffset, 0);
1462 : :
1463 : : /*
1464 : : * Upper-level vars in subquery will now be one level closer to their
1465 : : * parent than before; in particular, anything that had been level 1
1466 : : * becomes level zero.
1467 : : */
1468 : 1566 : IncrementVarSublevelsUp((Node *) subselect, -1, 1);
1469 : 1566 : IncrementVarSublevelsUp(whereClause, -1, 1);
1470 : :
1471 : : /*
1472 : : * Now that the WHERE clause is adjusted to match the parent query
1473 : : * environment, we can easily identify all the level-zero rels it uses.
1474 : : * The ones <= rtoffset belong to the upper query; the ones > rtoffset do
1475 : : * not.
1476 : : */
1179 1477 : 1566 : clause_varnos = pull_varnos(root, whereClause);
5527 1478 : 1566 : upper_varnos = NULL;
409 1479 : 1566 : varno = -1;
1480 [ + + ]: 4710 : while ((varno = bms_next_member(clause_varnos, varno)) >= 0)
1481 : : {
5722 1482 [ + + ]: 3144 : if (varno <= rtoffset)
5527 1483 : 1578 : upper_varnos = bms_add_member(upper_varnos, varno);
1484 : : }
5722 1485 : 1566 : bms_free(clause_varnos);
5527 1486 [ - + ]: 1566 : Assert(!bms_is_empty(upper_varnos));
1487 : :
1488 : : /*
1489 : : * Now that we've got the set of upper-level varnos, we can make the last
1490 : : * check: only available_rels can be referenced.
1491 : : */
1492 [ + + ]: 1566 : if (!bms_is_subset(upper_varnos, available_rels))
5527 tgl@sss.pgh.pa.us 1493 :GBC 16 : return NULL;
1494 : :
1495 : : /*
1496 : : * Now we can attach the modified subquery rtable to the parent. This also
1497 : : * adds subquery's RTEPermissionInfos into the upper query.
1498 : : */
495 alvherre@alvh.no-ip. 1499 :CBC 1550 : CombineRangeTables(&parse->rtable, &parse->rteperminfos,
1500 : : subselect->rtable, subselect->rteperminfos);
1501 : :
1502 : : /*
1503 : : * And finally, build the JoinExpr node.
1504 : : */
5527 tgl@sss.pgh.pa.us 1505 : 1550 : result = makeNode(JoinExpr);
1506 [ + + ]: 1550 : result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
1507 : 1550 : result->isNatural = false;
1508 : 1550 : result->larg = NULL; /* caller must fill this in */
1509 : : /* flatten out the FromExpr node if it's useless */
1510 [ + + ]: 1550 : if (list_length(subselect->jointree->fromlist) == 1)
1511 : 1541 : result->rarg = (Node *) linitial(subselect->jointree->fromlist);
1512 : : else
1513 : 9 : result->rarg = (Node *) subselect->jointree;
5386 peter_e@gmx.net 1514 : 1550 : result->usingClause = NIL;
1110 peter@eisentraut.org 1515 : 1550 : result->join_using_alias = NULL;
5527 tgl@sss.pgh.pa.us 1516 : 1550 : result->quals = whereClause;
1517 : 1550 : result->alias = NULL;
1518 : 1550 : result->rtindex = 0; /* we don't need an RTE for it */
1519 : :
1520 : 1550 : return result;
1521 : : }
1522 : :
1523 : : /*
1524 : : * simplify_EXISTS_query: remove any useless stuff in an EXISTS's subquery
1525 : : *
1526 : : * The only thing that matters about an EXISTS query is whether it returns
1527 : : * zero or more than zero rows. Therefore, we can remove certain SQL features
1528 : : * that won't affect that. The only part that is really likely to matter in
1529 : : * typical usage is simplifying the targetlist: it's a common habit to write
1530 : : * "SELECT * FROM" even though there is no need to evaluate any columns.
1531 : : *
1532 : : * Note: by suppressing the targetlist we could cause an observable behavioral
1533 : : * change, namely that any errors that might occur in evaluating the tlist
1534 : : * won't occur, nor will other side-effects of volatile functions. This seems
1535 : : * unlikely to bother anyone in practice.
1536 : : *
1537 : : * Returns true if was able to discard the targetlist, else false.
1538 : : */
1539 : : static bool
3431 1540 : 3561 : simplify_EXISTS_query(PlannerInfo *root, Query *query)
1541 : : {
1542 : : /*
1543 : : * We don't try to simplify at all if the query uses set operations,
1544 : : * aggregates, grouping sets, SRFs, modifying CTEs, HAVING, OFFSET, or FOR
1545 : : * UPDATE/SHARE; none of these seem likely in normal usage and their
1546 : : * possible effects are complex. (Note: we could ignore an "OFFSET 0"
1547 : : * clause, but that traditionally is used as an optimization fence, so we
1548 : : * don't.)
1549 : : */
5714 1550 [ + - ]: 3561 : if (query->commandType != CMD_SELECT ||
1551 [ + - ]: 3561 : query->setOperations ||
1552 [ + - ]: 3561 : query->hasAggs ||
3256 andres@anarazel.de 1553 [ + - ]: 3561 : query->groupingSets ||
5586 tgl@sss.pgh.pa.us 1554 [ + - ]: 3561 : query->hasWindowFuncs ||
2770 1555 [ + - ]: 3561 : query->hasTargetSRFs ||
4797 1556 [ + - ]: 3561 : query->hasModifyingCTE ||
5714 1557 [ + - ]: 3561 : query->havingQual ||
1558 [ + + ]: 3561 : query->limitOffset ||
1559 [ + + ]: 3549 : query->rowMarks)
1560 : 26 : return false;
1561 : :
1562 : : /*
1563 : : * LIMIT with a constant positive (or NULL) value doesn't affect the
1564 : : * semantics of EXISTS, so let's ignore such clauses. This is worth doing
1565 : : * because people accustomed to certain other DBMSes may be in the habit
1566 : : * of writing EXISTS(SELECT ... LIMIT 1) as an optimization. If there's a
1567 : : * LIMIT with anything else as argument, though, we can't simplify.
1568 : : */
3431 1569 [ + + ]: 3535 : if (query->limitCount)
1570 : : {
1571 : : /*
1572 : : * The LIMIT clause has not yet been through eval_const_expressions,
1573 : : * so we have to apply that here. It might seem like this is a waste
1574 : : * of cycles, since the only case plausibly worth worrying about is
1575 : : * "LIMIT 1" ... but what we'll actually see is "LIMIT int8(1::int4)",
1576 : : * so we have to fold constants or we're not going to recognize it.
1577 : : */
1578 : 12 : Node *node = eval_const_expressions(root, query->limitCount);
1579 : : Const *limit;
1580 : :
1581 : : /* Might as well update the query if we simplified the clause. */
1582 : 12 : query->limitCount = node;
1583 : :
1584 [ - + ]: 12 : if (!IsA(node, Const))
3431 tgl@sss.pgh.pa.us 1585 :UBC 0 : return false;
1586 : :
3431 tgl@sss.pgh.pa.us 1587 :CBC 12 : limit = (Const *) node;
1588 [ - + ]: 12 : Assert(limit->consttype == INT8OID);
1589 [ + + + + ]: 12 : if (!limit->constisnull && DatumGetInt64(limit->constvalue) <= 0)
1590 : 6 : return false;
1591 : :
1592 : : /* Whether or not the targetlist is safe, we can drop the LIMIT. */
1593 : 6 : query->limitCount = NULL;
1594 : : }
1595 : :
1596 : : /*
1597 : : * Otherwise, we can throw away the targetlist, as well as any GROUP,
1598 : : * WINDOW, DISTINCT, and ORDER BY clauses; none of those clauses will
1599 : : * change a nonzero-rows result to zero rows or vice versa. (Furthermore,
1600 : : * since our parsetree representation of these clauses depends on the
1601 : : * targetlist, we'd better throw them away if we drop the targetlist.)
1602 : : */
5714 1603 : 3529 : query->targetList = NIL;
1604 : 3529 : query->groupClause = NIL;
5586 1605 : 3529 : query->windowClause = NIL;
5714 1606 : 3529 : query->distinctClause = NIL;
1607 : 3529 : query->sortClause = NIL;
1608 : 3529 : query->hasDistinctOn = false;
1609 : :
1610 : 3529 : return true;
1611 : : }
1612 : :
1613 : : /*
1614 : : * convert_EXISTS_to_ANY: try to convert EXISTS to a hashable ANY sublink
1615 : : *
1616 : : * The subselect is expected to be a fresh copy that we can munge up,
1617 : : * and to have been successfully passed through simplify_EXISTS_query.
1618 : : *
1619 : : * On success, the modified subselect is returned, and we store a suitable
1620 : : * upper-level test expression at *testexpr, plus a list of the subselect's
1621 : : * output Params at *paramIds. (The test expression is already Param-ified
1622 : : * and hence need not go through convert_testexpr, which is why we have to
1623 : : * deal with the Param IDs specially.)
1624 : : *
1625 : : * On failure, returns NULL.
1626 : : */
1627 : : static Query *
1628 : 913 : convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
1629 : : Node **testexpr, List **paramIds)
1630 : : {
1631 : : Node *whereClause;
1632 : : List *leftargs,
1633 : : *rightargs,
1634 : : *opids,
1635 : : *opcollations,
1636 : : *newWhere,
1637 : : *tlist,
1638 : : *testlist,
1639 : : *paramids;
1640 : : ListCell *lc,
1641 : : *rc,
1642 : : *oc,
1643 : : *cc;
1644 : : AttrNumber resno;
1645 : :
1646 : : /*
1647 : : * Query must not require a targetlist, since we have to insert a new one.
1648 : : * Caller should have dealt with the case already.
1649 : : */
1650 [ - + ]: 913 : Assert(subselect->targetList == NIL);
1651 : :
1652 : : /*
1653 : : * Separate out the WHERE clause. (We could theoretically also remove
1654 : : * top-level plain JOIN/ON clauses, but it's probably not worth the
1655 : : * trouble.)
1656 : : */
1657 : 913 : whereClause = subselect->jointree->quals;
1658 : 913 : subselect->jointree->quals = NULL;
1659 : :
1660 : : /*
1661 : : * The rest of the sub-select must not refer to any Vars of the parent
1662 : : * query. (Vars of higher levels should be okay, though.)
1663 : : *
1664 : : * Note: we need not check for Aggrefs separately because we know the
1665 : : * sub-select is as yet unoptimized; any uplevel Aggref must therefore
1666 : : * contain an uplevel Var reference. This is not the case below ...
1667 : : */
1668 [ + + ]: 913 : if (contain_vars_of_level((Node *) subselect, 1))
1669 : 9 : return NULL;
1670 : :
1671 : : /*
1672 : : * We don't risk optimizing if the WHERE clause is volatile, either.
1673 : : */
1674 [ - + ]: 904 : if (contain_volatile_functions(whereClause))
5714 tgl@sss.pgh.pa.us 1675 :UBC 0 : return NULL;
1676 : :
1677 : : /*
1678 : : * Clean up the WHERE clause by doing const-simplification etc on it.
1679 : : * Aside from simplifying the processing we're about to do, this is
1680 : : * important for being able to pull chunks of the WHERE clause up into the
1681 : : * parent query. Since we are invoked partway through the parent's
1682 : : * preprocess_expression() work, earlier steps of preprocess_expression()
1683 : : * wouldn't get applied to the pulled-up stuff unless we do them here. For
1684 : : * the parts of the WHERE clause that get put back into the child query,
1685 : : * this work is partially duplicative, but it shouldn't hurt.
1686 : : *
1687 : : * Note: we do not run flatten_join_alias_vars. This is OK because any
1688 : : * parent aliases were flattened already, and we're not going to pull any
1689 : : * child Vars (of any description) into the parent.
1690 : : *
1691 : : * Note: passing the parent's root to eval_const_expressions is
1692 : : * technically wrong, but we can get away with it since only the
1693 : : * boundParams (if any) are used, and those would be the same in a
1694 : : * subroot.
1695 : : */
5714 tgl@sss.pgh.pa.us 1696 :CBC 904 : whereClause = eval_const_expressions(root, whereClause);
2226 1697 : 904 : whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false);
5714 1698 : 904 : whereClause = (Node *) make_ands_implicit((Expr *) whereClause);
1699 : :
1700 : : /*
1701 : : * We now have a flattened implicit-AND list of clauses, which we try to
1702 : : * break apart into "outervar = innervar" hash clauses. Anything that
1703 : : * can't be broken apart just goes back into the newWhere list. Note that
1704 : : * we aren't trying hard yet to ensure that we have only outer or only
1705 : : * inner on each side; we'll check that if we get to the end.
1706 : : */
4775 1707 : 904 : leftargs = rightargs = opids = opcollations = newWhere = NIL;
5714 1708 [ + - + + : 3451 : foreach(lc, (List *) whereClause)
+ + ]
1709 : : {
1710 : 2547 : OpExpr *expr = (OpExpr *) lfirst(lc);
1711 : :
1712 [ + + + + ]: 4164 : if (IsA(expr, OpExpr) &&
1713 : 1617 : hash_ok_operator(expr))
1714 : : {
5421 bruce@momjian.us 1715 : 1306 : Node *leftarg = (Node *) linitial(expr->args);
1716 : 1306 : Node *rightarg = (Node *) lsecond(expr->args);
1717 : :
5714 tgl@sss.pgh.pa.us 1718 [ + + ]: 1306 : if (contain_vars_of_level(leftarg, 1))
1719 : : {
1720 : 169 : leftargs = lappend(leftargs, leftarg);
1721 : 169 : rightargs = lappend(rightargs, rightarg);
1722 : 169 : opids = lappend_oid(opids, expr->opno);
4775 1723 : 169 : opcollations = lappend_oid(opcollations, expr->inputcollid);
5714 1724 : 169 : continue;
1725 : : }
1726 [ + + ]: 1137 : if (contain_vars_of_level(rightarg, 1))
1727 : : {
1728 : : /*
1729 : : * We must commute the clause to put the outer var on the
1730 : : * left, because the hashing code in nodeSubplan.c expects
1731 : : * that. This probably shouldn't ever fail, since hashable
1732 : : * operators ought to have commutators, but be paranoid.
1733 : : */
1734 : 969 : expr->opno = get_commutator(expr->opno);
1735 [ + - + - ]: 969 : if (OidIsValid(expr->opno) && hash_ok_operator(expr))
1736 : : {
1737 : 969 : leftargs = lappend(leftargs, rightarg);
1738 : 969 : rightargs = lappend(rightargs, leftarg);
1739 : 969 : opids = lappend_oid(opids, expr->opno);
4775 1740 : 969 : opcollations = lappend_oid(opcollations, expr->inputcollid);
5714 1741 : 969 : continue;
1742 : : }
1743 : : /* If no commutator, no chance to optimize the WHERE clause */
5714 tgl@sss.pgh.pa.us 1744 :UBC 0 : return NULL;
1745 : : }
1746 : : }
1747 : : /* Couldn't handle it as a hash clause */
5714 tgl@sss.pgh.pa.us 1748 :CBC 1409 : newWhere = lappend(newWhere, expr);
1749 : : }
1750 : :
1751 : : /*
1752 : : * If we didn't find anything we could convert, fail.
1753 : : */
1754 [ + + ]: 904 : if (leftargs == NIL)
1755 : 154 : return NULL;
1756 : :
1757 : : /*
1758 : : * There mustn't be any parent Vars or Aggs in the stuff that we intend to
1759 : : * put back into the child query. Note: you might think we don't need to
1760 : : * check for Aggs separately, because an uplevel Agg must contain an
1761 : : * uplevel Var in its argument. But it is possible that the uplevel Var
1762 : : * got optimized away by eval_const_expressions. Consider
1763 : : *
1764 : : * SUM(CASE WHEN false THEN uplevelvar ELSE 0 END)
1765 : : */
1766 [ + - - + ]: 1500 : if (contain_vars_of_level((Node *) newWhere, 1) ||
1767 : 750 : contain_vars_of_level((Node *) rightargs, 1))
5714 tgl@sss.pgh.pa.us 1768 :UBC 0 : return NULL;
5714 tgl@sss.pgh.pa.us 1769 [ + + + - ]:CBC 771 : if (root->parse->hasAggs &&
1770 [ - + ]: 42 : (contain_aggs_of_level((Node *) newWhere, 1) ||
1771 : 21 : contain_aggs_of_level((Node *) rightargs, 1)))
5714 tgl@sss.pgh.pa.us 1772 :UBC 0 : return NULL;
1773 : :
1774 : : /*
1775 : : * And there can't be any child Vars in the stuff we intend to pull up.
1776 : : * (Note: we'd need to check for child Aggs too, except we know the child
1777 : : * has no aggs at all because of simplify_EXISTS_query's check. The same
1778 : : * goes for window functions.)
1779 : : */
5714 tgl@sss.pgh.pa.us 1780 [ - + ]:CBC 750 : if (contain_vars_of_level((Node *) leftargs, 0))
5714 tgl@sss.pgh.pa.us 1781 :UBC 0 : return NULL;
1782 : :
1783 : : /*
1784 : : * Also reject sublinks in the stuff we intend to pull up. (It might be
1785 : : * possible to support this, but doesn't seem worth the complication.)
1786 : : */
5714 tgl@sss.pgh.pa.us 1787 [ - + ]:CBC 750 : if (contain_subplans((Node *) leftargs))
5714 tgl@sss.pgh.pa.us 1788 :UBC 0 : return NULL;
1789 : :
1790 : : /*
1791 : : * Okay, adjust the sublevelsup in the stuff we're pulling up.
1792 : : */
5714 tgl@sss.pgh.pa.us 1793 :CBC 750 : IncrementVarSublevelsUp((Node *) leftargs, -1, 1);
1794 : :
1795 : : /*
1796 : : * Put back any child-level-only WHERE clauses.
1797 : : */
1798 [ + + ]: 750 : if (newWhere)
1799 : 654 : subselect->jointree->quals = (Node *) make_ands_explicit(newWhere);
1800 : :
1801 : : /*
1802 : : * Build a new targetlist for the child that emits the expressions we
1803 : : * need. Concurrently, build a testexpr for the parent using Params to
1804 : : * reference the child outputs. (Since we generate Params directly here,
1805 : : * there will be no need to convert the testexpr in build_subplan.)
1806 : : */
1807 : 750 : tlist = testlist = paramids = NIL;
1808 : 750 : resno = 1;
1872 1809 [ + - + + : 1888 : forfour(lc, leftargs, rc, rightargs, oc, opids, cc, opcollations)
+ - + + +
- + + + -
+ + + + +
- + - + -
+ + ]
1810 : : {
5714 1811 : 1138 : Node *leftarg = (Node *) lfirst(lc);
1812 : 1138 : Node *rightarg = (Node *) lfirst(rc);
1813 : 1138 : Oid opid = lfirst_oid(oc);
4775 1814 : 1138 : Oid opcollation = lfirst_oid(cc);
1815 : : Param *param;
1816 : :
1920 1817 : 1138 : param = generate_new_exec_param(root,
1818 : : exprType(rightarg),
1819 : : exprTypmod(rightarg),
1820 : : exprCollation(rightarg));
5714 1821 : 1138 : tlist = lappend(tlist,
1822 : 1138 : makeTargetEntry((Expr *) rightarg,
1823 : 1138 : resno++,
1824 : : NULL,
1825 : : false));
1826 : 1138 : testlist = lappend(testlist,
1827 : 1138 : make_opclause(opid, BOOLOID, false,
1828 : : (Expr *) leftarg, (Expr *) param,
1829 : : InvalidOid, opcollation));
1830 : 1138 : paramids = lappend_int(paramids, param->paramid);
1831 : : }
1832 : :
1833 : : /* Put everything where it should go, and we're done */
1834 : 750 : subselect->targetList = tlist;
1835 : 750 : *testexpr = (Node *) make_ands_explicit(testlist);
1836 : 750 : *paramIds = paramids;
1837 : :
1838 : 750 : return subselect;
1839 : : }
1840 : :
1841 : :
1842 : : /*
1843 : : * Replace correlation vars (uplevel vars) with Params.
1844 : : *
1845 : : * Uplevel PlaceHolderVars, aggregates, GROUPING() expressions, and
1846 : : * MergeSupportFuncs are replaced, too.
1847 : : *
1848 : : * Note: it is critical that this runs immediately after SS_process_sublinks.
1849 : : * Since we do not recurse into the arguments of uplevel PHVs and aggregates,
1850 : : * they will get copied to the appropriate subplan args list in the parent
1851 : : * query with uplevel vars not replaced by Params, but only adjusted in level
1852 : : * (see replace_outer_placeholdervar and replace_outer_agg). That's exactly
1853 : : * what we want for the vars of the parent level --- but if a PHV's or
1854 : : * aggregate's argument contains any further-up variables, they have to be
1855 : : * replaced with Params in their turn. That will happen when the parent level
1856 : : * runs SS_replace_correlation_vars. Therefore it must do so after expanding
1857 : : * its sublinks to subplans. And we don't want any steps in between, else
1858 : : * those steps would never get applied to the argument expressions, either in
1859 : : * the parent or the child level.
1860 : : *
1861 : : * Another fairly tricky thing going on here is the handling of SubLinks in
1862 : : * the arguments of uplevel PHVs/aggregates. Those are not touched inside the
1863 : : * intermediate query level, either. Instead, SS_process_sublinks recurses on
1864 : : * them after copying the PHV or Aggref expression into the parent plan level
1865 : : * (this is actually taken care of in build_subplan).
1866 : : */
1867 : : Node *
6264 1868 : 64898 : SS_replace_correlation_vars(PlannerInfo *root, Node *expr)
1869 : : {
1870 : : /* No setup needed for tree walk, so away we go */
1871 : 64898 : return replace_correlation_vars_mutator(expr, root);
1872 : : }
1873 : :
1874 : : static Node *
1875 : 508861 : replace_correlation_vars_mutator(Node *node, PlannerInfo *root)
1876 : : {
8999 1877 [ + + ]: 508861 : if (node == NULL)
1878 : 22529 : return NULL;
1879 [ + + ]: 486332 : if (IsA(node, Var))
1880 : : {
1881 [ + + ]: 130128 : if (((Var *) node)->varlevelsup > 0)
6264 1882 : 22230 : return (Node *) replace_outer_var(root, (Var *) node);
1883 : : }
4404 1884 [ + + ]: 464102 : if (IsA(node, PlaceHolderVar))
1885 : : {
1886 [ + + ]: 42 : if (((PlaceHolderVar *) node)->phlevelsup > 0)
1887 : 21 : return (Node *) replace_outer_placeholdervar(root,
1888 : : (PlaceHolderVar *) node);
1889 : : }
7618 1890 [ + + ]: 464081 : if (IsA(node, Aggref))
1891 : : {
1892 [ + + ]: 3534 : if (((Aggref *) node)->agglevelsup > 0)
6264 1893 : 26 : return (Node *) replace_outer_agg(root, (Aggref *) node);
1894 : : }
3256 andres@anarazel.de 1895 [ + + ]: 464055 : if (IsA(node, GroupingFunc))
1896 : : {
1897 [ + + ]: 45 : if (((GroupingFunc *) node)->agglevelsup > 0)
1898 : 32 : return (Node *) replace_outer_grouping(root, (GroupingFunc *) node);
1899 : : }
28 dean.a.rasheed@gmail 1900 [ + + ]:GNC 464023 : if (IsA(node, MergeSupportFunc))
1901 : : {
1902 [ + + ]: 12 : if (root->parse->commandType != CMD_MERGE)
1903 : 3 : return (Node *) replace_outer_merge_support(root,
1904 : : (MergeSupportFunc *) node);
1905 : : }
8999 tgl@sss.pgh.pa.us 1906 :CBC 464020 : return expression_tree_mutator(node,
1907 : : replace_correlation_vars_mutator,
1908 : : (void *) root);
1909 : : }
1910 : :
1911 : : /*
1912 : : * Expand SubLinks to SubPlans in the given expression.
1913 : : *
1914 : : * The isQual argument tells whether or not this expression is a WHERE/HAVING
1915 : : * qualifier expression. If it is, any sublinks appearing at top level need
1916 : : * not distinguish FALSE from UNKNOWN return values.
1917 : : */
1918 : : Node *
6264 1919 : 39161 : SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual)
1920 : : {
1921 : : process_sublinks_context context;
1922 : :
1923 : 39161 : context.root = root;
1924 : 39161 : context.isTopQual = isQual;
1925 : 39161 : return process_sublinks_mutator(expr, &context);
1926 : : }
1927 : :
1928 : : static Node *
5995 bruce@momjian.us 1929 : 534621 : process_sublinks_mutator(Node *node, process_sublinks_context *context)
1930 : : {
1931 : : process_sublinks_context locContext;
1932 : :
6264 tgl@sss.pgh.pa.us 1933 : 534621 : locContext.root = context->root;
1934 : :
8999 1935 [ + + ]: 534621 : if (node == NULL)
9357 bruce@momjian.us 1936 : 24864 : return NULL;
8999 tgl@sss.pgh.pa.us 1937 [ + + ]: 509757 : if (IsA(node, SubLink))
1938 : : {
8768 bruce@momjian.us 1939 : 16517 : SubLink *sublink = (SubLink *) node;
1940 : : Node *testexpr;
1941 : :
1942 : : /*
1943 : : * First, recursively process the lefthand-side expressions, if any.
1944 : : * They're not top-level anymore.
1945 : : */
6264 tgl@sss.pgh.pa.us 1946 : 16517 : locContext.isTopQual = false;
1947 : 16517 : testexpr = process_sublinks_mutator(sublink->testexpr, &locContext);
1948 : :
1949 : : /*
1950 : : * Now build the SubPlan node and make the expr to return.
1951 : : */
1952 : 16517 : return make_subplan(context->root,
5714 1953 : 16517 : (Query *) sublink->subselect,
1954 : : sublink->subLinkType,
1955 : : sublink->subLinkId,
1956 : : testexpr,
6264 1957 : 16517 : context->isTopQual);
1958 : : }
1959 : :
1960 : : /*
1961 : : * Don't recurse into the arguments of an outer PHV, Aggref or
1962 : : * GroupingFunc here. Any SubLinks in the arguments have to be dealt with
1963 : : * at the outer query level; they'll be handled when build_subplan
1964 : : * collects the PHV, Aggref or GroupingFunc into the arguments to be
1965 : : * passed down to the current subplan.
1966 : : */
4404 1967 [ + + ]: 493240 : if (IsA(node, PlaceHolderVar))
1968 : : {
1969 [ - + ]: 81 : if (((PlaceHolderVar *) node)->phlevelsup > 0)
4404 tgl@sss.pgh.pa.us 1970 :UBC 0 : return node;
1971 : : }
4404 tgl@sss.pgh.pa.us 1972 [ + + ]:CBC 493159 : else if (IsA(node, Aggref))
1973 : : {
5468 1974 [ + + ]: 286 : if (((Aggref *) node)->agglevelsup > 0)
1975 : 9 : return node;
1976 : : }
755 1977 [ + + ]: 492873 : else if (IsA(node, GroupingFunc))
1978 : : {
1979 [ + + ]: 56 : if (((GroupingFunc *) node)->agglevelsup > 0)
1980 : 18 : return node;
1981 : : }
1982 : :
1983 : : /*
1984 : : * We should never see a SubPlan expression in the input (since this is
1985 : : * the very routine that creates 'em to begin with). We shouldn't find
1986 : : * ourselves invoked directly on a Query, either.
1987 : : */
5714 1988 [ - + ]: 493213 : Assert(!IsA(node, SubPlan));
1989 [ - + ]: 493213 : Assert(!IsA(node, AlternativeSubPlan));
7758 1990 [ - + ]: 493213 : Assert(!IsA(node, Query));
1991 : :
1992 : : /*
1993 : : * Because make_subplan() could return an AND or OR clause, we have to
1994 : : * take steps to preserve AND/OR flatness of a qual. We assume the input
1995 : : * has been AND/OR flattened and so we need no recursion here.
1996 : : *
1997 : : * (Due to the coding here, we will not get called on the List subnodes of
1998 : : * an AND; and the input is *not* yet in implicit-AND format. So no check
1999 : : * is needed for a bare List.)
2000 : : *
2001 : : * Anywhere within the top-level AND/OR clause structure, we can tell
2002 : : * make_subplan() that NULL and FALSE are interchangeable. So isTopQual
2003 : : * propagates down in both cases. (Note that this is unlike the meaning
2004 : : * of "top level qual" used in most other places in Postgres.)
2005 : : */
1902 2006 [ + + ]: 493213 : if (is_andclause(node))
2007 : : {
7168 bruce@momjian.us 2008 : 7278 : List *newargs = NIL;
2009 : : ListCell *l;
2010 : :
2011 : : /* Still at qual top-level */
6264 tgl@sss.pgh.pa.us 2012 : 7278 : locContext.isTopQual = context->isTopQual;
2013 : :
7398 2014 [ + - + + : 27247 : foreach(l, ((BoolExpr *) node)->args)
+ + ]
2015 : : {
2016 : : Node *newarg;
2017 : :
6264 2018 : 19969 : newarg = process_sublinks_mutator(lfirst(l), &locContext);
1902 2019 [ - + ]: 19969 : if (is_andclause(newarg))
7259 neilc@samurai.com 2020 :UBC 0 : newargs = list_concat(newargs, ((BoolExpr *) newarg)->args);
2021 : : else
7398 tgl@sss.pgh.pa.us 2022 :CBC 19969 : newargs = lappend(newargs, newarg);
2023 : : }
2024 : 7278 : return (Node *) make_andclause(newargs);
2025 : : }
2026 : :
1902 2027 [ + + ]: 485935 : if (is_orclause(node))
2028 : : {
7168 bruce@momjian.us 2029 : 981 : List *newargs = NIL;
2030 : : ListCell *l;
2031 : :
2032 : : /* Still at qual top-level */
5716 tgl@sss.pgh.pa.us 2033 : 981 : locContext.isTopQual = context->isTopQual;
2034 : :
7398 2035 [ + - + + : 3454 : foreach(l, ((BoolExpr *) node)->args)
+ + ]
2036 : : {
2037 : : Node *newarg;
2038 : :
6264 2039 : 2473 : newarg = process_sublinks_mutator(lfirst(l), &locContext);
1902 2040 [ - + ]: 2473 : if (is_orclause(newarg))
7259 neilc@samurai.com 2041 :UBC 0 : newargs = list_concat(newargs, ((BoolExpr *) newarg)->args);
2042 : : else
7398 tgl@sss.pgh.pa.us 2043 :CBC 2473 : newargs = lappend(newargs, newarg);
2044 : : }
2045 : 981 : return (Node *) make_orclause(newargs);
2046 : : }
2047 : :
2048 : : /*
2049 : : * If we recurse down through anything other than an AND or OR node, we
2050 : : * are definitely not at top qual level anymore.
2051 : : */
5716 2052 : 484954 : locContext.isTopQual = false;
2053 : :
8999 2054 : 484954 : return expression_tree_mutator(node,
2055 : : process_sublinks_mutator,
2056 : : (void *) &locContext);
2057 : : }
2058 : :
2059 : : /*
2060 : : * SS_identify_outer_params - identify the Params available from outer levels
2061 : : *
2062 : : * This must be run after SS_replace_correlation_vars and SS_process_sublinks
2063 : : * processing is complete in a given query level as well as all of its
2064 : : * descendant levels (which means it's most practical to do it at the end of
2065 : : * processing the query level). We compute the set of paramIds that outer
2066 : : * levels will make available to this level+descendants, and record it in
2067 : : * root->outer_params for use while computing extParam/allParam sets in final
2068 : : * plan cleanup. (We can't just compute it then, because the upper levels'
2069 : : * plan_params lists are transient and will be gone by then.)
2070 : : */
2071 : : void
3169 2072 : 246318 : SS_identify_outer_params(PlannerInfo *root)
2073 : : {
2074 : : Bitmapset *outer_params;
2075 : : PlannerInfo *proot;
2076 : : ListCell *l;
2077 : :
2078 : : /*
2079 : : * If no parameters have been assigned anywhere in the tree, we certainly
2080 : : * don't need to do anything here.
2081 : : */
2344 rhaas@postgresql.org 2082 [ + + ]: 246318 : if (root->glob->paramExecTypes == NIL)
3169 tgl@sss.pgh.pa.us 2083 : 168003 : return;
2084 : :
2085 : : /*
2086 : : * Scan all query levels above this one to see which parameters are due to
2087 : : * be available from them, either because lower query levels have
2088 : : * requested them (via plan_params) or because they will be available from
2089 : : * initPlans of those levels.
2090 : : */
2091 : 78315 : outer_params = NULL;
4239 2092 [ + + ]: 101122 : for (proot = root->parent_root; proot != NULL; proot = proot->parent_root)
2093 : : {
2094 : : /* Include ordinary Var/PHV/Aggref/GroupingFunc params */
2095 [ + + + + : 41882 : foreach(l, proot->plan_params)
+ + ]
2096 : : {
2097 : 19075 : PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
2098 : :
3169 2099 : 19075 : outer_params = bms_add_member(outer_params, pitem->paramId);
2100 : : }
2101 : : /* Include any outputs of outer-level initPlans */
4239 2102 [ + + + + : 25278 : foreach(l, proot->init_plans)
+ + ]
2103 : : {
2104 : 2471 : SubPlan *initsubplan = (SubPlan *) lfirst(l);
2105 : : ListCell *l2;
2106 : :
2107 [ + - + + : 4942 : foreach(l2, initsubplan->setParam)
+ + ]
2108 : : {
3169 2109 : 2471 : outer_params = bms_add_member(outer_params, lfirst_int(l2));
2110 : : }
2111 : : }
2112 : : /* Include worktable ID, if a recursive query is being planned */
4239 2113 [ + + ]: 22807 : if (proot->wt_param_id >= 0)
3169 2114 : 1186 : outer_params = bms_add_member(outer_params, proot->wt_param_id);
2115 : : }
2116 : 78315 : root->outer_params = outer_params;
2117 : : }
2118 : :
2119 : : /*
2120 : : * SS_charge_for_initplans - account for initplans in Path costs & parallelism
2121 : : *
2122 : : * If any initPlans have been created in the current query level, they will
2123 : : * get attached to the Plan tree created from whichever Path we select from
2124 : : * the given rel. Increment all that rel's Paths' costs to account for them,
2125 : : * and if any of the initPlans are parallel-unsafe, mark all the rel's Paths
2126 : : * parallel-unsafe as well.
2127 : : *
2128 : : * This is separate from SS_attach_initplans because we might conditionally
2129 : : * create more initPlans during create_plan(), depending on which Path we
2130 : : * select. However, Paths that would generate such initPlans are expected
2131 : : * to have included their cost and parallel-safety effects already.
2132 : : */
2133 : : void
2960 2134 : 246318 : SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
2135 : : {
2136 : : Cost initplan_cost;
2137 : : bool unsafe_initplans;
2138 : : ListCell *lc;
2139 : :
2140 : : /* Nothing to do if no initPlans */
2141 [ + + ]: 246318 : if (root->init_plans == NIL)
2142 : 240959 : return;
2143 : :
2144 : : /*
2145 : : * Compute the cost increment just once, since it will be the same for all
2146 : : * Paths. Also check for parallel-unsafe initPlans.
2147 : : */
276 tgl@sss.pgh.pa.us 2148 :GNC 5359 : SS_compute_initplan_cost(root->init_plans,
2149 : : &initplan_cost, &unsafe_initplans);
2150 : :
2151 : : /*
2152 : : * Now adjust the costs and parallel_safe flags.
2153 : : */
2960 tgl@sss.pgh.pa.us 2154 [ + - + + :CBC 10784 : foreach(lc, final_rel->pathlist)
+ + ]
2155 : : {
2156 : 5425 : Path *path = (Path *) lfirst(lc);
2157 : :
2158 : 5425 : path->startup_cost += initplan_cost;
2159 : 5425 : path->total_cost += initplan_cost;
276 tgl@sss.pgh.pa.us 2160 [ + + ]:GNC 5425 : if (unsafe_initplans)
2161 : 3311 : path->parallel_safe = false;
2162 : : }
2163 : :
2164 : : /*
2165 : : * Adjust partial paths' costs too, or forget them entirely if we must
2166 : : * consider the rel parallel-unsafe.
2167 : : */
2168 [ + + ]: 5359 : if (unsafe_initplans)
2169 : : {
2170 : 3292 : final_rel->partial_pathlist = NIL;
2171 : 3292 : final_rel->consider_parallel = false;
2172 : : }
2173 : : else
2174 : : {
2175 [ + + + + : 2073 : foreach(lc, final_rel->partial_pathlist)
+ + ]
2176 : : {
2177 : 6 : Path *path = (Path *) lfirst(lc);
2178 : :
2179 : 6 : path->startup_cost += initplan_cost;
2180 : 6 : path->total_cost += initplan_cost;
2181 : : }
2182 : : }
2183 : :
2184 : : /* We needn't do set_cheapest() here, caller will do it */
2185 : : }
2186 : :
2187 : : /*
2188 : : * SS_compute_initplan_cost - count up the cost delta for some initplans
2189 : : *
2190 : : * The total cost returned in *initplan_cost_p should be added to both the
2191 : : * startup and total costs of the plan node the initplans get attached to.
2192 : : * We also report whether any of the initplans are not parallel-safe.
2193 : : *
2194 : : * The primary user of this is SS_charge_for_initplans, but it's also
2195 : : * used in adjusting costs when we move initplans to another plan node.
2196 : : */
2197 : : void
2198 : 5431 : SS_compute_initplan_cost(List *init_plans,
2199 : : Cost *initplan_cost_p,
2200 : : bool *unsafe_initplans_p)
2201 : : {
2202 : : Cost initplan_cost;
2203 : : bool unsafe_initplans;
2204 : : ListCell *lc;
2205 : :
2206 : : /*
2207 : : * We assume each initPlan gets run once during top plan startup. This is
2208 : : * a conservative overestimate, since in fact an initPlan might be
2209 : : * executed later than plan startup, or even not at all.
2210 : : */
2211 : 5431 : initplan_cost = 0;
2212 : 5431 : unsafe_initplans = false;
2213 [ + + + + : 11351 : foreach(lc, init_plans)
+ + ]
2214 : : {
2215 : 5920 : SubPlan *initsubplan = lfirst_node(SubPlan, lc);
2216 : :
2217 : 5920 : initplan_cost += initsubplan->startup_cost + initsubplan->per_call_cost;
2218 [ + + ]: 5920 : if (!initsubplan->parallel_safe)
2219 : 3739 : unsafe_initplans = true;
2220 : : }
2221 : 5431 : *initplan_cost_p = initplan_cost;
2222 : 5431 : *unsafe_initplans_p = unsafe_initplans;
2223 : 5431 : }
2224 : :
2225 : : /*
2226 : : * SS_attach_initplans - attach initplans to topmost plan node
2227 : : *
2228 : : * Attach any initplans created in the current query level to the specified
2229 : : * plan node, which should normally be the topmost node for the query level.
2230 : : * (In principle the initPlans could go in any node at or above where they're
2231 : : * referenced; but there seems no reason to put them any lower than the
2232 : : * topmost node, so we don't bother to track exactly where they came from.)
2233 : : *
2234 : : * We do not touch the plan node's cost or parallel_safe flag. The initplans
2235 : : * must have been accounted for in SS_charge_for_initplans, or by any later
2236 : : * code that adds initplans via SS_make_initplan_from_plan.
2237 : : */
2238 : : void
2960 tgl@sss.pgh.pa.us 2239 :CBC 245827 : SS_attach_initplans(PlannerInfo *root, Plan *plan)
2240 : : {
2241 : 245827 : plan->initPlan = root->init_plans;
7735 2242 : 245827 : }
2243 : :
2244 : : /*
2245 : : * SS_finalize_plan - do final parameter processing for a completed Plan.
2246 : : *
2247 : : * This recursively computes the extParam and allParam sets for every Plan
2248 : : * node in the given plan tree. (Oh, and RangeTblFunction.funcparams too.)
2249 : : *
2250 : : * We assume that SS_finalize_plan has already been run on any initplans or
2251 : : * subplans the plan tree could reference.
2252 : : */
2253 : : void
3169 2254 : 90719 : SS_finalize_plan(PlannerInfo *root, Plan *plan)
2255 : : {
2256 : : /* No setup needed, just recurse through plan tree. */
2419 2257 : 90719 : (void) finalize_plan(root, plan, -1, root->outer_params, NULL);
3169 2258 : 90719 : }
2259 : :
2260 : : /*
2261 : : * Recursive processing of all nodes in the plan tree
2262 : : *
2263 : : * gather_param is the rescan_param of an ancestral Gather/GatherMerge,
2264 : : * or -1 if there is none.
2265 : : *
2266 : : * valid_params is the set of param IDs supplied by outer plan levels
2267 : : * that are valid to reference in this plan node or its children.
2268 : : *
2269 : : * scan_params is a set of param IDs to force scan plan nodes to reference.
2270 : : * This is for EvalPlanQual support, and is always NULL at the top of the
2271 : : * recursion.
2272 : : *
2273 : : * The return value is the computed allParam set for the given Plan node.
2274 : : * This is just an internal notational convenience: we can add a child
2275 : : * plan's allParams to the set of param IDs of interest to this level
2276 : : * in the same statement that recurses to that child.
2277 : : *
2278 : : * Do not scribble on caller's values of valid_params or scan_params!
2279 : : *
2280 : : * Note: although we attempt to deal with initPlans anywhere in the tree, the
2281 : : * logic is not really right. The problem is that a plan node might return an
2282 : : * output Param of its initPlan as a targetlist item, in which case it's valid
2283 : : * for the parent plan level to reference that same Param; the parent's usage
2284 : : * will be converted into a Var referencing the child plan node by setrefs.c.
2285 : : * But this function would see the parent's reference as out of scope and
2286 : : * complain about it. For now, this does not matter because the planner only
2287 : : * attaches initPlans to the topmost plan node in a query level, so the case
2288 : : * doesn't arise. If we ever merge this processing into setrefs.c, maybe it
2289 : : * can be handled more cleanly.
2290 : : */
2291 : : static Bitmapset *
2419 2292 : 663987 : finalize_plan(PlannerInfo *root, Plan *plan,
2293 : : int gather_param,
2294 : : Bitmapset *valid_params,
2295 : : Bitmapset *scan_params)
2296 : : {
2297 : : finalize_primnode_context context;
2298 : : int locally_added_param;
2299 : : Bitmapset *nestloop_params;
2300 : : Bitmapset *initExtParam;
2301 : : Bitmapset *initSetParam;
2302 : : Bitmapset *child_params;
2303 : : ListCell *l;
2304 : :
9544 bruce@momjian.us 2305 [ + + ]: 663987 : if (plan == NULL)
7735 tgl@sss.pgh.pa.us 2306 : 387077 : return NULL;
2307 : :
6261 2308 : 276910 : context.root = root;
7735 2309 : 276910 : context.paramids = NULL; /* initialize set to empty */
5284 2310 : 276910 : locally_added_param = -1; /* there isn't one */
5025 2311 : 276910 : nestloop_params = NULL; /* there aren't any */
2312 : :
2313 : : /*
2314 : : * Examine any initPlans to determine the set of external params they
2315 : : * reference and the set of output params they supply. (We assume
2316 : : * SS_finalize_plan was run on them already.)
2317 : : */
3169 2318 : 276910 : initExtParam = initSetParam = NULL;
2319 [ + + + + : 282988 : foreach(l, plan->initPlan)
+ + ]
2320 : : {
2321 : 6078 : SubPlan *initsubplan = (SubPlan *) lfirst(l);
2322 : 6078 : Plan *initplan = planner_subplan_get_plan(root, initsubplan);
2323 : : ListCell *l2;
2324 : :
2325 : 6078 : initExtParam = bms_add_members(initExtParam, initplan->extParam);
2326 [ + - + + : 12180 : foreach(l2, initsubplan->setParam)
+ + ]
2327 : : {
2328 : 6102 : initSetParam = bms_add_member(initSetParam, lfirst_int(l2));
2329 : : }
2330 : : }
2331 : :
2332 : : /* Any setParams are validly referenceable in this node and children */
2333 [ + + ]: 276910 : if (initSetParam)
2334 : 5523 : valid_params = bms_union(valid_params, initSetParam);
2335 : :
2336 : : /*
2337 : : * When we call finalize_primnode, context.paramids sets are automatically
2338 : : * merged together. But when recursing to self, we have to do it the hard
2339 : : * way. We want the paramids set to include params in subplans as well as
2340 : : * at this level.
2341 : : */
2342 : :
2343 : : /* Find params in targetlist and qual */
7735 2344 : 276910 : finalize_primnode((Node *) plan->targetlist, &context);
2345 : 276910 : finalize_primnode((Node *) plan->qual, &context);
2346 : :
2347 : : /*
2348 : : * If it's a parallel-aware scan node, mark it as dependent on the parent
2349 : : * Gather/GatherMerge's rescan Param.
2350 : : */
2419 2351 [ + + ]: 276910 : if (plan->parallel_aware)
2352 : : {
2353 [ - + ]: 1229 : if (gather_param < 0)
2419 tgl@sss.pgh.pa.us 2354 [ # # ]:UBC 0 : elog(ERROR, "parallel-aware plan node is not below a Gather");
2419 tgl@sss.pgh.pa.us 2355 :CBC 1229 : context.paramids =
2356 : 1229 : bms_add_member(context.paramids, gather_param);
2357 : : }
2358 : :
2359 : : /* Check additional node-type-specific fields */
9557 vadim4o@yahoo.com 2360 [ + + + + : 276910 : switch (nodeTag(plan))
+ + + + +
+ + + + +
+ + + - +
+ + + + +
+ + + + +
+ + + + +
+ + - ]
2361 : : {
2362 : 36390 : case T_Result:
8917 tgl@sss.pgh.pa.us 2363 : 36390 : finalize_primnode(((Result *) plan)->resconstantqual,
2364 : : &context);
9557 vadim4o@yahoo.com 2365 : 36390 : break;
2366 : :
5284 tgl@sss.pgh.pa.us 2367 : 38442 : case T_SeqScan:
3186 2368 : 38442 : context.paramids = bms_add_members(context.paramids, scan_params);
2369 : 38442 : break;
2370 : :
3257 simon@2ndQuadrant.co 2371 : 49 : case T_SampleScan:
3186 tgl@sss.pgh.pa.us 2372 : 49 : finalize_primnode((Node *) ((SampleScan *) plan)->tablesample,
2373 : : &context);
5284 2374 : 49 : context.paramids = bms_add_members(context.paramids, scan_params);
2375 : 49 : break;
2376 : :
8002 2377 : 43855 : case T_IndexScan:
6929 2378 : 43855 : finalize_primnode((Node *) ((IndexScan *) plan)->indexqual,
2379 : : &context);
4882 2380 : 43855 : finalize_primnode((Node *) ((IndexScan *) plan)->indexorderby,
2381 : : &context);
2382 : :
2383 : : /*
2384 : : * we need not look at indexqualorig, since it will have the same
2385 : : * param references as indexqual. Likewise, we can ignore
2386 : : * indexorderbyorig.
2387 : : */
5284 2388 : 43855 : context.paramids = bms_add_members(context.paramids, scan_params);
8002 2389 : 43855 : break;
2390 : :
4569 2391 : 3188 : case T_IndexOnlyScan:
2392 : 3188 : finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexqual,
2393 : : &context);
832 2394 : 3188 : finalize_primnode((Node *) ((IndexOnlyScan *) plan)->recheckqual,
2395 : : &context);
4569 2396 : 3188 : finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexorderby,
2397 : : &context);
2398 : :
2399 : : /*
2400 : : * we need not look at indextlist, since it cannot contain Params.
2401 : : */
2402 : 3188 : context.paramids = bms_add_members(context.paramids, scan_params);
2403 : 3188 : break;
2404 : :
6935 2405 : 3341 : case T_BitmapIndexScan:
6929 2406 : 3341 : finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual,
2407 : : &context);
2408 : :
2409 : : /*
2410 : : * we need not look at indexqualorig, since it will have the same
2411 : : * param references as indexqual.
2412 : : */
6935 2413 : 3341 : break;
2414 : :
2415 : 3235 : case T_BitmapHeapScan:
2416 : 3235 : finalize_primnode((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
2417 : : &context);
5284 2418 : 3235 : context.paramids = bms_add_members(context.paramids, scan_params);
6935 2419 : 3235 : break;
2420 : :
8002 2421 : 279 : case T_TidScan:
6714 2422 : 279 : finalize_primnode((Node *) ((TidScan *) plan)->tidquals,
2423 : : &context);
5284 2424 : 279 : context.paramids = bms_add_members(context.paramids, scan_params);
9557 vadim4o@yahoo.com 2425 : 279 : break;
2426 : :
1142 drowley@postgresql.o 2427 : 17 : case T_TidRangeScan:
2428 : 17 : finalize_primnode((Node *) ((TidRangeScan *) plan)->tidrangequals,
2429 : : &context);
2430 : 17 : context.paramids = bms_add_members(context.paramids, scan_params);
2431 : 17 : break;
2432 : :
8592 tgl@sss.pgh.pa.us 2433 : 7860 : case T_SubqueryScan:
2434 : : {
3169 2435 : 7860 : SubqueryScan *sscan = (SubqueryScan *) plan;
2436 : : RelOptInfo *rel;
2437 : : Bitmapset *subquery_params;
2438 : :
2439 : : /* We must run finalize_plan on the subquery */
2440 : 7860 : rel = find_base_rel(root, sscan->scan.scanrelid);
2224 rhaas@postgresql.org 2441 : 7860 : subquery_params = rel->subroot->outer_params;
2442 [ + + ]: 7860 : if (gather_param >= 0)
2443 : 12 : subquery_params = bms_add_member(bms_copy(subquery_params),
2444 : : gather_param);
2445 : 7860 : finalize_plan(rel->subroot, sscan->subplan, gather_param,
2446 : : subquery_params, NULL);
2447 : :
2448 : : /* Now we can add its extParams to the parent's params */
3169 tgl@sss.pgh.pa.us 2449 : 15720 : context.paramids = bms_add_members(context.paramids,
2450 : 7860 : sscan->subplan->extParam);
2451 : : /* We need scan_params too, though */
2452 : 7860 : context.paramids = bms_add_members(context.paramids,
2453 : : scan_params);
2454 : : }
8592 2455 : 7860 : break;
2456 : :
8002 2457 : 10921 : case T_FunctionScan:
2458 : : {
3797 2459 : 10921 : FunctionScan *fscan = (FunctionScan *) plan;
2460 : : ListCell *lc;
2461 : :
2462 : : /*
2463 : : * Call finalize_primnode independently on each function
2464 : : * expression, so that we can record which params are
2465 : : * referenced in each, in order to decide which need
2466 : : * re-evaluating during rescan.
2467 : : */
2468 [ + - + + : 21854 : foreach(lc, fscan->functions)
+ + ]
2469 : : {
2470 : 10933 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
2471 : : finalize_primnode_context funccontext;
2472 : :
2473 : 10933 : funccontext = context;
2474 : 10933 : funccontext.paramids = NULL;
2475 : :
2476 : 10933 : finalize_primnode(rtfunc->funcexpr, &funccontext);
2477 : :
2478 : : /* remember results for execution */
2479 : 10933 : rtfunc->funcparams = funccontext.paramids;
2480 : :
2481 : : /* add the function's params to the overall set */
2482 : 10933 : context.paramids = bms_add_members(context.paramids,
2483 : 10933 : funccontext.paramids);
2484 : : }
2485 : :
2486 : 10921 : context.paramids = bms_add_members(context.paramids,
2487 : : scan_params);
2488 : : }
8002 2489 : 10921 : break;
2490 : :
2594 alvherre@alvh.no-ip. 2491 : 117 : case T_TableFuncScan:
2492 : 117 : finalize_primnode((Node *) ((TableFuncScan *) plan)->tablefunc,
2493 : : &context);
2494 : 117 : context.paramids = bms_add_members(context.paramids, scan_params);
2495 : 117 : break;
2496 : :
6465 mail@joeconway.com 2497 : 2624 : case T_ValuesScan:
6264 tgl@sss.pgh.pa.us 2498 : 2624 : finalize_primnode((Node *) ((ValuesScan *) plan)->values_lists,
2499 : : &context);
5284 2500 : 2624 : context.paramids = bms_add_members(context.paramids, scan_params);
6465 mail@joeconway.com 2501 : 2624 : break;
2502 : :
5671 tgl@sss.pgh.pa.us 2503 : 1599 : case T_CteScan:
2504 : : {
2505 : : /*
2506 : : * You might think we should add the node's cteParam to
2507 : : * paramids, but we shouldn't because that param is just a
2508 : : * linkage mechanism for multiple CteScan nodes for the same
2509 : : * CTE; it is never used for changed-param signaling. What we
2510 : : * have to do instead is to find the referenced CTE plan and
2511 : : * incorporate its external paramids, so that the correct
2512 : : * things will happen if the CTE references outer-level
2513 : : * variables. See test cases for bug #4902. (We assume
2514 : : * SS_finalize_plan was run on the CTE plan already.)
2515 : : */
5161 bruce@momjian.us 2516 : 1599 : int plan_id = ((CteScan *) plan)->ctePlanId;
2517 : : Plan *cteplan;
2518 : :
2519 : : /* so, do this ... */
5396 tgl@sss.pgh.pa.us 2520 [ + - - + ]: 1599 : if (plan_id < 1 || plan_id > list_length(root->glob->subplans))
5396 tgl@sss.pgh.pa.us 2521 [ # # ]:UBC 0 : elog(ERROR, "could not find plan for CteScan referencing plan ID %d",
2522 : : plan_id);
5396 tgl@sss.pgh.pa.us 2523 :CBC 1599 : cteplan = (Plan *) list_nth(root->glob->subplans, plan_id - 1);
2524 : 1599 : context.paramids =
2525 : 1599 : bms_add_members(context.paramids, cteplan->extParam);
2526 : :
2527 : : #ifdef NOT_USED
2528 : : /* ... but not this */
2529 : : context.paramids =
2530 : : bms_add_member(context.paramids,
2531 : : ((CteScan *) plan)->cteParam);
2532 : : #endif
2533 : :
5284 2534 : 1599 : context.paramids = bms_add_members(context.paramids,
2535 : : scan_params);
2536 : : }
5671 2537 : 1599 : break;
2538 : :
2539 : 403 : case T_WorkTableScan:
2540 : 403 : context.paramids =
2541 : 403 : bms_add_member(context.paramids,
2542 : : ((WorkTableScan *) plan)->wtParam);
5284 2543 : 403 : context.paramids = bms_add_members(context.paramids, scan_params);
5671 2544 : 403 : break;
2545 : :
2571 kgrittn@postgresql.o 2546 : 185 : case T_NamedTuplestoreScan:
2547 : 185 : context.paramids = bms_add_members(context.paramids, scan_params);
2548 : 185 : break;
2549 : :
4802 tgl@sss.pgh.pa.us 2550 : 380 : case T_ForeignScan:
2551 : : {
3104 rhaas@postgresql.org 2552 : 380 : ForeignScan *fscan = (ForeignScan *) plan;
2553 : :
2554 : 380 : finalize_primnode((Node *) fscan->fdw_exprs,
2555 : : &context);
2556 : 380 : finalize_primnode((Node *) fscan->fdw_recheck_quals,
2557 : : &context);
2558 : :
2559 : : /* We assume fdw_scan_tlist cannot contain Params */
2560 : 380 : context.paramids = bms_add_members(context.paramids,
2561 : : scan_params);
2562 : : }
4802 tgl@sss.pgh.pa.us 2563 : 380 : break;
2564 : :
3446 rhaas@postgresql.org 2565 :UBC 0 : case T_CustomScan:
2566 : : {
3215 2567 : 0 : CustomScan *cscan = (CustomScan *) plan;
2568 : : ListCell *lc;
2569 : :
2570 : 0 : finalize_primnode((Node *) cscan->custom_exprs,
2571 : : &context);
2572 : : /* We assume custom_scan_tlist cannot contain Params */
2573 : 0 : context.paramids =
2574 : 0 : bms_add_members(context.paramids, scan_params);
2575 : :
2576 : : /* child nodes if any */
3186 tgl@sss.pgh.pa.us 2577 [ # # # # : 0 : foreach(lc, cscan->custom_plans)
# # ]
2578 : : {
3215 rhaas@postgresql.org 2579 : 0 : context.paramids =
2580 : 0 : bms_add_members(context.paramids,
2581 : 0 : finalize_plan(root,
2582 : 0 : (Plan *) lfirst(lc),
2583 : : gather_param,
2584 : : valid_params,
2585 : : scan_params));
2586 : : }
2587 : : }
3446 2588 : 0 : break;
2589 : :
5300 tgl@sss.pgh.pa.us 2590 :CBC 43654 : case T_ModifyTable:
2591 : : {
5284 2592 : 43654 : ModifyTable *mtplan = (ModifyTable *) plan;
2593 : :
2594 : : /* Force descendant scan nodes to reference epqParam */
2595 : 43654 : locally_added_param = mtplan->epqParam;
2596 : 43654 : valid_params = bms_add_member(bms_copy(valid_params),
2597 : : locally_added_param);
2598 : 43654 : scan_params = bms_add_member(bms_copy(scan_params),
2599 : : locally_added_param);
2600 : 43654 : finalize_primnode((Node *) mtplan->returningLists,
2601 : : &context);
3264 andres@anarazel.de 2602 : 43654 : finalize_primnode((Node *) mtplan->onConflictSet,
2603 : : &context);
2604 : 43654 : finalize_primnode((Node *) mtplan->onConflictWhere,
2605 : : &context);
2606 : : /* exclRelTlist contains only Vars, doesn't need examination */
2607 : : }
5300 tgl@sss.pgh.pa.us 2608 : 43654 : break;
2609 : :
8002 2610 : 4552 : case T_Append:
2611 : : {
7263 neilc@samurai.com 2612 [ + - + + : 15754 : foreach(l, ((Append *) plan)->appendplans)
+ + ]
2613 : : {
2614 : 11202 : context.paramids =
2615 : 11202 : bms_add_members(context.paramids,
6261 tgl@sss.pgh.pa.us 2616 : 11202 : finalize_plan(root,
2617 : 11202 : (Plan *) lfirst(l),
2618 : : gather_param,
2619 : : valid_params,
2620 : : scan_params));
2621 : : }
2622 : : }
9557 vadim4o@yahoo.com 2623 : 4552 : break;
2624 : :
4931 tgl@sss.pgh.pa.us 2625 : 54 : case T_MergeAppend:
2626 : : {
2627 [ + - + + : 228 : foreach(l, ((MergeAppend *) plan)->mergeplans)
+ + ]
2628 : : {
2629 : 174 : context.paramids =
2630 : 174 : bms_add_members(context.paramids,
2631 : 174 : finalize_plan(root,
2632 : 174 : (Plan *) lfirst(l),
2633 : : gather_param,
2634 : : valid_params,
2635 : : scan_params));
2636 : : }
2637 : : }
2638 : 54 : break;
2639 : :
6935 2640 : 41 : case T_BitmapAnd:
2641 : : {
2642 [ + - + + : 123 : foreach(l, ((BitmapAnd *) plan)->bitmapplans)
+ + ]
2643 : : {
2644 : 82 : context.paramids =
2645 : 82 : bms_add_members(context.paramids,
6261 2646 : 82 : finalize_plan(root,
2647 : 82 : (Plan *) lfirst(l),
2648 : : gather_param,
2649 : : valid_params,
2650 : : scan_params));
2651 : : }
2652 : : }
6935 2653 : 41 : break;
2654 : :
2655 : 65 : case T_BitmapOr:
2656 : : {
2657 [ + - + + : 195 : foreach(l, ((BitmapOr *) plan)->bitmapplans)
+ + ]
2658 : : {
2659 : 130 : context.paramids =
2660 : 130 : bms_add_members(context.paramids,
6261 2661 : 130 : finalize_plan(root,
2662 : 130 : (Plan *) lfirst(l),
2663 : : gather_param,
2664 : : valid_params,
2665 : : scan_params));
2666 : : }
2667 : : }
6935 2668 : 65 : break;
2669 : :
8615 2670 : 30151 : case T_NestLoop:
2671 : : {
5025 2672 : 30151 : finalize_primnode((Node *) ((Join *) plan)->joinqual,
2673 : : &context);
2674 : : /* collect set of params that will be passed to right child */
2675 [ + + + + : 52826 : foreach(l, ((NestLoop *) plan)->nestParams)
+ + ]
2676 : : {
2677 : 22675 : NestLoopParam *nlp = (NestLoopParam *) lfirst(l);
2678 : :
2679 : 22675 : nestloop_params = bms_add_member(nestloop_params,
2680 : : nlp->paramno);
2681 : : }
2682 : : }
8615 2683 : 30151 : break;
2684 : :
9557 vadim4o@yahoo.com 2685 : 1384 : case T_MergeJoin:
8615 tgl@sss.pgh.pa.us 2686 : 1384 : finalize_primnode((Node *) ((Join *) plan)->joinqual,
2687 : : &context);
8917 2688 : 1384 : finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
2689 : : &context);
9557 vadim4o@yahoo.com 2690 : 1384 : break;
2691 : :
2692 : 7671 : case T_HashJoin:
8615 tgl@sss.pgh.pa.us 2693 : 7671 : finalize_primnode((Node *) ((Join *) plan)->joinqual,
2694 : : &context);
8917 2695 : 7671 : finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
2696 : : &context);
9557 vadim4o@yahoo.com 2697 : 7671 : break;
2698 : :
299 tgl@sss.pgh.pa.us 2699 : 7671 : case T_Hash:
2700 : 7671 : finalize_primnode((Node *) ((Hash *) plan)->hashkeys,
2701 : : &context);
2702 : 7671 : break;
2703 : :
7278 2704 : 1053 : case T_Limit:
2705 : 1053 : finalize_primnode(((Limit *) plan)->limitOffset,
2706 : : &context);
2707 : 1053 : finalize_primnode(((Limit *) plan)->limitCount,
2708 : : &context);
2709 : 1053 : break;
2710 : :
5671 2711 : 403 : case T_RecursiveUnion:
2712 : : /* child nodes are allowed to reference wtParam */
5284 2713 : 403 : locally_added_param = ((RecursiveUnion *) plan)->wtParam;
2714 : 403 : valid_params = bms_add_member(bms_copy(valid_params),
2715 : : locally_added_param);
2716 : : /* wtParam does *not* get added to scan_params */
2717 : 403 : break;
2718 : :
2719 : 3800 : case T_LockRows:
2720 : : /* Force descendant scan nodes to reference epqParam */
2721 : 3800 : locally_added_param = ((LockRows *) plan)->epqParam;
2722 : 3800 : valid_params = bms_add_member(bms_copy(valid_params),
2723 : : locally_added_param);
2724 : 3800 : scan_params = bms_add_member(bms_copy(scan_params),
2725 : : locally_added_param);
2726 : 3800 : break;
2727 : :
2790 2728 : 4334 : case T_Agg:
2729 : : {
2730 : 4334 : Agg *agg = (Agg *) plan;
2731 : :
2732 : : /*
2733 : : * AGG_HASHED plans need to know which Params are referenced
2734 : : * in aggregate calls. Do a separate scan to identify them.
2735 : : */
2736 [ + + ]: 4334 : if (agg->aggstrategy == AGG_HASHED)
2737 : : {
2738 : : finalize_primnode_context aggcontext;
2739 : :
2740 : 549 : aggcontext.root = root;
2741 : 549 : aggcontext.paramids = NULL;
2742 : 549 : finalize_agg_primnode((Node *) agg->plan.targetlist,
2743 : : &aggcontext);
2744 : 549 : finalize_agg_primnode((Node *) agg->plan.qual,
2745 : : &aggcontext);
2746 : 549 : agg->aggParams = aggcontext.paramids;
2747 : : }
2748 : : }
2749 : 4334 : break;
2750 : :
5175 2751 : 55 : case T_WindowAgg:
2752 : 55 : finalize_primnode(((WindowAgg *) plan)->startOffset,
2753 : : &context);
2754 : 55 : finalize_primnode(((WindowAgg *) plan)->endOffset,
2755 : : &context);
2756 : 55 : break;
2757 : :
2419 2758 : 458 : case T_Gather:
2759 : : /* child nodes are allowed to reference rescan_param, if any */
2760 : 458 : locally_added_param = ((Gather *) plan)->rescan_param;
2761 [ + - ]: 458 : if (locally_added_param >= 0)
2762 : : {
2763 : 458 : valid_params = bms_add_member(bms_copy(valid_params),
2764 : : locally_added_param);
2765 : :
2766 : : /*
2767 : : * We currently don't support nested Gathers. The issue so
2768 : : * far as this function is concerned would be how to identify
2769 : : * which child nodes depend on which Gather.
2770 : : */
2771 [ - + ]: 458 : Assert(gather_param < 0);
2772 : : /* Pass down rescan_param to child parallel-aware nodes */
2773 : 458 : gather_param = locally_added_param;
2774 : : }
2775 : : /* rescan_param does *not* get added to scan_params */
2776 : 458 : break;
2777 : :
2778 : 159 : case T_GatherMerge:
2779 : : /* child nodes are allowed to reference rescan_param, if any */
2780 : 159 : locally_added_param = ((GatherMerge *) plan)->rescan_param;
2781 [ + - ]: 159 : if (locally_added_param >= 0)
2782 : : {
2783 : 159 : valid_params = bms_add_member(bms_copy(valid_params),
2784 : : locally_added_param);
2785 : :
2786 : : /*
2787 : : * We currently don't support nested Gathers. The issue so
2788 : : * far as this function is concerned would be how to identify
2789 : : * which child nodes depend on which Gather.
2790 : : */
2791 [ - + ]: 159 : Assert(gather_param < 0);
2792 : : /* Pass down rescan_param to child parallel-aware nodes */
2793 : 159 : gather_param = locally_added_param;
2794 : : }
2795 : : /* rescan_param does *not* get added to scan_params */
2796 : 159 : break;
2797 : :
1005 drowley@postgresql.o 2798 : 670 : case T_Memoize:
2799 : 670 : finalize_primnode((Node *) ((Memoize *) plan)->param_exprs,
2800 : : &context);
1108 2801 : 670 : break;
2802 : :
2643 andres@anarazel.de 2803 : 17850 : case T_ProjectSet:
2804 : : case T_Material:
2805 : : case T_Sort:
2806 : : case T_IncrementalSort:
2807 : : case T_Unique:
2808 : : case T_SetOp:
2809 : : case T_Group:
2810 : : /* no node-type-specific fields need fixing */
9557 vadim4o@yahoo.com 2811 : 17850 : break;
2812 : :
9557 vadim4o@yahoo.com 2813 :UBC 0 : default:
7569 tgl@sss.pgh.pa.us 2814 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
2815 : : (int) nodeTag(plan));
2816 : : }
2817 : :
2818 : : /* Process left and right child plans, if any */
5025 tgl@sss.pgh.pa.us 2819 :CBC 276910 : child_params = finalize_plan(root,
2820 : 276910 : plan->lefttree,
2821 : : gather_param,
2822 : : valid_params,
2823 : : scan_params);
2824 : 276910 : context.paramids = bms_add_members(context.paramids, child_params);
2825 : :
2826 [ + + ]: 276910 : if (nestloop_params)
2827 : : {
2828 : : /* right child can reference nestloop_params as well as valid_params */
2829 : 20910 : child_params = finalize_plan(root,
2830 : 20910 : plan->righttree,
2831 : : gather_param,
2832 : : bms_union(nestloop_params, valid_params),
2833 : : scan_params);
2834 : : /* ... and they don't count as parameters used at my level */
2835 : 20910 : child_params = bms_difference(child_params, nestloop_params);
2836 : 20910 : bms_free(nestloop_params);
2837 : : }
2838 : : else
2839 : : {
2840 : : /* easy case */
2841 : 256000 : child_params = finalize_plan(root,
2842 : 256000 : plan->righttree,
2843 : : gather_param,
2844 : : valid_params,
2845 : : scan_params);
2846 : : }
2847 : 276910 : context.paramids = bms_add_members(context.paramids, child_params);
2848 : :
2849 : : /*
2850 : : * Any locally generated parameter doesn't count towards its generating
2851 : : * plan node's external dependencies. (Note: if we changed valid_params
2852 : : * and/or scan_params, we leak those bitmapsets; not worth the notational
2853 : : * trouble to clean them up.)
2854 : : */
5284 2855 [ + + ]: 276910 : if (locally_added_param >= 0)
2856 : : {
5671 2857 : 48474 : context.paramids = bms_del_member(context.paramids,
2858 : : locally_added_param);
2859 : : }
2860 : :
2861 : : /* Now we have all the paramids referenced in this node and children */
2862 : :
7735 2863 [ - + ]: 276910 : if (!bms_is_subset(context.paramids, valid_params))
7569 tgl@sss.pgh.pa.us 2864 [ # # ]:UBC 0 : elog(ERROR, "plan should not reference subplan's variable");
2865 : :
2866 : : /*
2867 : : * The plan node's allParam and extParam fields should include all its
2868 : : * referenced paramids, plus contributions from any child initPlans.
2869 : : * However, any setParams of the initPlans should not be present in the
2870 : : * parent node's extParams, only in its allParams. (It's possible that
2871 : : * some initPlans have extParams that are setParams of other initPlans.)
2872 : : */
2873 : :
2874 : : /* allParam must include initplans' extParams and setParams */
3169 tgl@sss.pgh.pa.us 2875 :CBC 276910 : plan->allParam = bms_union(context.paramids, initExtParam);
2876 : 276910 : plan->allParam = bms_add_members(plan->allParam, initSetParam);
2877 : : /* extParam must include any initplan extParams */
2878 : 276910 : plan->extParam = bms_union(context.paramids, initExtParam);
2879 : : /* but not any initplan setParams */
2880 : 276910 : plan->extParam = bms_del_members(plan->extParam, initSetParam);
2881 : :
7735 2882 : 276910 : return plan->allParam;
2883 : : }
2884 : :
2885 : : /*
2886 : : * finalize_primnode: add IDs of all PARAM_EXEC params that appear (or will
2887 : : * appear) in the given expression tree to the result set.
2888 : : */
2889 : : static bool
7555 bruce@momjian.us 2890 : 4641716 : finalize_primnode(Node *node, finalize_primnode_context *context)
2891 : : {
7792 tgl@sss.pgh.pa.us 2892 [ + + ]: 4641716 : if (node == NULL)
2893 : 570383 : return false;
2894 [ + + ]: 4071333 : if (IsA(node, Param))
2895 : : {
2896 [ + + ]: 56414 : if (((Param *) node)->paramkind == PARAM_EXEC)
2897 : : {
6567 2898 : 54839 : int paramid = ((Param *) node)->paramid;
2899 : :
7735 2900 : 54839 : context->paramids = bms_add_member(context->paramids, paramid);
2901 : : }
7792 2902 : 56414 : return false; /* no more to do here */
2903 : : }
276 tgl@sss.pgh.pa.us 2904 [ + + ]:GNC 4014919 : else if (IsA(node, Aggref))
2905 : : {
2906 : : /*
2907 : : * Check to see if the aggregate will be replaced by a Param
2908 : : * referencing a subquery output during setrefs.c. If so, we must
2909 : : * account for that Param here. (For various reasons, it's not
2910 : : * convenient to perform that substitution earlier than setrefs.c, nor
2911 : : * to perform this processing after setrefs.c. Thus we need a wart
2912 : : * here.)
2913 : : */
2914 : 6040 : Aggref *aggref = (Aggref *) node;
2915 : : Param *aggparam;
2916 : :
2917 : 6040 : aggparam = find_minmax_agg_replacement_param(context->root, aggref);
2918 [ + + ]: 6040 : if (aggparam != NULL)
2919 : 260 : context->paramids = bms_add_member(context->paramids,
2920 : : aggparam->paramid);
2921 : : /* Fall through to examine the agg's arguments */
2922 : : }
2923 [ + + ]: 4008879 : else if (IsA(node, SubPlan))
2924 : : {
7559 bruce@momjian.us 2925 :CBC 15187 : SubPlan *subplan = (SubPlan *) node;
6261 tgl@sss.pgh.pa.us 2926 : 15187 : Plan *plan = planner_subplan_get_plan(context->root, subplan);
2927 : : ListCell *lc;
2928 : : Bitmapset *subparamids;
2929 : :
2930 : : /* Recurse into the testexpr, but not into the Plan */
5757 2931 : 15187 : finalize_primnode(subplan->testexpr, context);
2932 : :
2933 : : /*
2934 : : * Remove any param IDs of output parameters of the subplan that were
2935 : : * referenced in the testexpr. These are not interesting for
2936 : : * parameter change signaling since we always re-evaluate the subplan.
2937 : : * Note that this wouldn't work too well if there might be uses of the
2938 : : * same param IDs elsewhere in the plan, but that can't happen because
2939 : : * generate_new_exec_param never tries to merge params.
2940 : : */
2941 [ + + + + : 16648 : foreach(lc, subplan->paramIds)
+ + ]
2942 : : {
2943 : 1461 : context->paramids = bms_del_member(context->paramids,
2944 : : lfirst_int(lc));
2945 : : }
2946 : :
2947 : : /* Also examine args list */
2948 : 15187 : finalize_primnode((Node *) subplan->args, context);
2949 : :
2950 : : /*
2951 : : * Add params needed by the subplan to paramids, but excluding those
2952 : : * we will pass down to it. (We assume SS_finalize_plan was run on
2953 : : * the subplan already.)
2954 : : */
2955 : 15187 : subparamids = bms_copy(plan->extParam);
2956 [ + + + + : 37054 : foreach(lc, subplan->parParam)
+ + ]
2957 : : {
2958 : 21867 : subparamids = bms_del_member(subparamids, lfirst_int(lc));
2959 : : }
2960 : 15187 : context->paramids = bms_join(context->paramids, subparamids);
2961 : :
2962 : 15187 : return false; /* no more to do here */
2963 : : }
7792 2964 : 3999732 : return expression_tree_walker(node, finalize_primnode,
2965 : : (void *) context);
2966 : : }
2967 : :
2968 : : /*
2969 : : * finalize_agg_primnode: find all Aggref nodes in the given expression tree,
2970 : : * and add IDs of all PARAM_EXEC params appearing within their aggregated
2971 : : * arguments to the result set.
2972 : : */
2973 : : static bool
2790 2974 : 4776 : finalize_agg_primnode(Node *node, finalize_primnode_context *context)
2975 : : {
2976 [ + + ]: 4776 : if (node == NULL)
2977 : 518 : return false;
2978 [ + + ]: 4258 : if (IsA(node, Aggref))
2979 : : {
2980 : 560 : Aggref *agg = (Aggref *) node;
2981 : :
2982 : : /* we should not consider the direct arguments, if any */
2983 : 560 : finalize_primnode((Node *) agg->args, context);
2984 : 560 : finalize_primnode((Node *) agg->aggfilter, context);
2985 : 560 : return false; /* there can't be any Aggrefs below here */
2986 : : }
2987 : 3698 : return expression_tree_walker(node, finalize_agg_primnode,
2988 : : (void *) context);
2989 : : }
2990 : :
2991 : : /*
2992 : : * SS_make_initplan_output_param - make a Param for an initPlan's output
2993 : : *
2994 : : * The plan is expected to return a scalar value of the given type/collation.
2995 : : *
2996 : : * Note that in some cases the initplan may not ever appear in the finished
2997 : : * plan tree. If that happens, we'll have wasted a PARAM_EXEC slot, which
2998 : : * is no big deal.
2999 : : */
3000 : : Param *
2960 3001 : 211 : SS_make_initplan_output_param(PlannerInfo *root,
3002 : : Oid resulttype, int32 resulttypmod,
3003 : : Oid resultcollation)
3004 : : {
1920 3005 : 211 : return generate_new_exec_param(root, resulttype,
3006 : : resulttypmod, resultcollation);
3007 : : }
3008 : :
3009 : : /*
3010 : : * SS_make_initplan_from_plan - given a plan tree, make it an InitPlan
3011 : : *
3012 : : * We build an EXPR_SUBLINK SubPlan node and put it into the initplan
3013 : : * list for the outer query level. A Param that represents the initplan's
3014 : : * output has already been assigned using SS_make_initplan_output_param.
3015 : : */
3016 : : void
3169 3017 : 188 : SS_make_initplan_from_plan(PlannerInfo *root,
3018 : : PlannerInfo *subroot, Plan *plan,
3019 : : Param *prm)
3020 : : {
3021 : : SubPlan *node;
3022 : :
3023 : : /*
3024 : : * Add the subplan and its PlannerInfo, as well as a dummy path entry, to
3025 : : * the global lists. Ideally we'd save a real path, but right now our
3026 : : * sole caller doesn't build a path that exactly matches the plan. Since
3027 : : * we're not currently going to need the path for an initplan, it's not
3028 : : * worth requiring construction of such a path.
3029 : : */
4607 3030 : 188 : root->glob->subplans = lappend(root->glob->subplans, plan);
19 tgl@sss.pgh.pa.us 3031 :GNC 188 : root->glob->subpaths = lappend(root->glob->subpaths, NULL);
3169 tgl@sss.pgh.pa.us 3032 :CBC 188 : root->glob->subroots = lappend(root->glob->subroots, subroot);
3033 : :
3034 : : /*
3035 : : * Create a SubPlan node and add it to the outer list of InitPlans. Note
3036 : : * it has to appear after any other InitPlans it might depend on (see
3037 : : * comments in ExecReScan).
3038 : : */
6943 3039 : 188 : node = makeNode(SubPlan);
3040 : 188 : node->subLinkType = EXPR_SUBLINK;
2960 3041 : 188 : node->plan_id = list_length(root->glob->subplans);
26 tgl@sss.pgh.pa.us 3042 :GNC 188 : node->plan_name = psprintf("InitPlan %d", node->plan_id);
4775 tgl@sss.pgh.pa.us 3043 :CBC 188 : get_first_col_type(plan, &node->firstColType, &node->firstColTypmod,
3044 : : &node->firstColCollation);
276 tgl@sss.pgh.pa.us 3045 :GNC 188 : node->parallel_safe = plan->parallel_safe;
2960 tgl@sss.pgh.pa.us 3046 :CBC 188 : node->setParam = list_make1_int(prm->paramid);
3047 : :
6264 3048 : 188 : root->init_plans = lappend(root->init_plans, node);
3049 : :
3050 : : /*
3051 : : * The node can't have any inputs (since it's an initplan), so the
3052 : : * parParam and args lists remain empty.
3053 : : */
3054 : :
3055 : : /* Set costs of SubPlan using info from the plan tree */
3169 3056 : 188 : cost_subplan(subroot, node, plan);
6943 3057 : 188 : }
|