/src/postgres/src/backend/optimizer/util/var.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * var.c |
4 | | * Var node manipulation routines |
5 | | * |
6 | | * Note: for most purposes, PlaceHolderVar is considered a Var too, |
7 | | * even if its contained expression is variable-free. Also, CurrentOfExpr |
8 | | * is treated as a Var for purposes of determining whether an expression |
9 | | * contains variables. |
10 | | * |
11 | | * |
12 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
13 | | * Portions Copyright (c) 1994, Regents of the University of California |
14 | | * |
15 | | * |
16 | | * IDENTIFICATION |
17 | | * src/backend/optimizer/util/var.c |
18 | | * |
19 | | *------------------------------------------------------------------------- |
20 | | */ |
21 | | #include "postgres.h" |
22 | | |
23 | | #include "access/sysattr.h" |
24 | | #include "nodes/nodeFuncs.h" |
25 | | #include "optimizer/clauses.h" |
26 | | #include "optimizer/optimizer.h" |
27 | | #include "optimizer/placeholder.h" |
28 | | #include "optimizer/prep.h" |
29 | | #include "parser/parsetree.h" |
30 | | #include "rewrite/rewriteManip.h" |
31 | | |
32 | | |
33 | | typedef struct |
34 | | { |
35 | | Relids varnos; |
36 | | PlannerInfo *root; |
37 | | int sublevels_up; |
38 | | } pull_varnos_context; |
39 | | |
40 | | typedef struct |
41 | | { |
42 | | Bitmapset *varattnos; |
43 | | Index varno; |
44 | | } pull_varattnos_context; |
45 | | |
46 | | typedef struct |
47 | | { |
48 | | List *vars; |
49 | | int sublevels_up; |
50 | | } pull_vars_context; |
51 | | |
52 | | typedef struct |
53 | | { |
54 | | int var_location; |
55 | | int sublevels_up; |
56 | | } locate_var_of_level_context; |
57 | | |
58 | | typedef struct |
59 | | { |
60 | | List *varlist; |
61 | | int flags; |
62 | | } pull_var_clause_context; |
63 | | |
64 | | typedef struct |
65 | | { |
66 | | PlannerInfo *root; /* could be NULL! */ |
67 | | Query *query; /* outer Query */ |
68 | | int sublevels_up; |
69 | | bool possible_sublink; /* could aliases include a SubLink? */ |
70 | | bool inserted_sublink; /* have we inserted a SubLink? */ |
71 | | } flatten_join_alias_vars_context; |
72 | | |
73 | | static bool pull_varnos_walker(Node *node, |
74 | | pull_varnos_context *context); |
75 | | static bool pull_varattnos_walker(Node *node, pull_varattnos_context *context); |
76 | | static bool pull_vars_walker(Node *node, pull_vars_context *context); |
77 | | static bool contain_var_clause_walker(Node *node, void *context); |
78 | | static bool contain_vars_of_level_walker(Node *node, int *sublevels_up); |
79 | | static bool contain_vars_returning_old_or_new_walker(Node *node, void *context); |
80 | | static bool locate_var_of_level_walker(Node *node, |
81 | | locate_var_of_level_context *context); |
82 | | static bool pull_var_clause_walker(Node *node, |
83 | | pull_var_clause_context *context); |
84 | | static Node *flatten_join_alias_vars_mutator(Node *node, |
85 | | flatten_join_alias_vars_context *context); |
86 | | static Node *flatten_group_exprs_mutator(Node *node, |
87 | | flatten_join_alias_vars_context *context); |
88 | | static Node *mark_nullable_by_grouping(PlannerInfo *root, Node *newnode, |
89 | | Var *oldvar); |
90 | | static Node *add_nullingrels_if_needed(PlannerInfo *root, Node *newnode, |
91 | | Var *oldvar); |
92 | | static bool is_standard_join_alias_expression(Node *newnode, Var *oldvar); |
93 | | static void adjust_standard_join_alias_expression(Node *newnode, Var *oldvar); |
94 | | static Relids alias_relid_set(Query *query, Relids relids); |
95 | | |
96 | | |
97 | | /* |
98 | | * pull_varnos |
99 | | * Create a set of all the distinct varnos present in a parsetree. |
100 | | * Only varnos that reference level-zero rtable entries are considered. |
101 | | * |
102 | | * The result includes outer-join relids mentioned in Var.varnullingrels and |
103 | | * PlaceHolderVar.phnullingrels fields in the parsetree. |
104 | | * |
105 | | * "root" can be passed as NULL if it is not necessary to process |
106 | | * PlaceHolderVars. |
107 | | * |
108 | | * NOTE: this is used on not-yet-planned expressions. It may therefore find |
109 | | * bare SubLinks, and if so it needs to recurse into them to look for uplevel |
110 | | * references to the desired rtable level! But when we find a completed |
111 | | * SubPlan, we only need to look at the parameters passed to the subplan. |
112 | | */ |
113 | | Relids |
114 | | pull_varnos(PlannerInfo *root, Node *node) |
115 | 0 | { |
116 | 0 | pull_varnos_context context; |
117 | |
|
118 | 0 | context.varnos = NULL; |
119 | 0 | context.root = root; |
120 | 0 | context.sublevels_up = 0; |
121 | | |
122 | | /* |
123 | | * Must be prepared to start with a Query or a bare expression tree; if |
124 | | * it's a Query, we don't want to increment sublevels_up. |
125 | | */ |
126 | 0 | query_or_expression_tree_walker(node, |
127 | 0 | pull_varnos_walker, |
128 | 0 | &context, |
129 | 0 | 0); |
130 | |
|
131 | 0 | return context.varnos; |
132 | 0 | } |
133 | | |
134 | | /* |
135 | | * pull_varnos_of_level |
136 | | * Create a set of all the distinct varnos present in a parsetree. |
137 | | * Only Vars of the specified level are considered. |
138 | | */ |
139 | | Relids |
140 | | pull_varnos_of_level(PlannerInfo *root, Node *node, int levelsup) |
141 | 0 | { |
142 | 0 | pull_varnos_context context; |
143 | |
|
144 | 0 | context.varnos = NULL; |
145 | 0 | context.root = root; |
146 | 0 | context.sublevels_up = levelsup; |
147 | | |
148 | | /* |
149 | | * Must be prepared to start with a Query or a bare expression tree; if |
150 | | * it's a Query, we don't want to increment sublevels_up. |
151 | | */ |
152 | 0 | query_or_expression_tree_walker(node, |
153 | 0 | pull_varnos_walker, |
154 | 0 | &context, |
155 | 0 | 0); |
156 | |
|
157 | 0 | return context.varnos; |
158 | 0 | } |
159 | | |
160 | | static bool |
161 | | pull_varnos_walker(Node *node, pull_varnos_context *context) |
162 | 0 | { |
163 | 0 | if (node == NULL) |
164 | 0 | return false; |
165 | 0 | if (IsA(node, Var)) |
166 | 0 | { |
167 | 0 | Var *var = (Var *) node; |
168 | |
|
169 | 0 | if (var->varlevelsup == context->sublevels_up) |
170 | 0 | { |
171 | 0 | context->varnos = bms_add_member(context->varnos, var->varno); |
172 | 0 | context->varnos = bms_add_members(context->varnos, |
173 | 0 | var->varnullingrels); |
174 | 0 | } |
175 | 0 | return false; |
176 | 0 | } |
177 | 0 | if (IsA(node, CurrentOfExpr)) |
178 | 0 | { |
179 | 0 | CurrentOfExpr *cexpr = (CurrentOfExpr *) node; |
180 | |
|
181 | 0 | if (context->sublevels_up == 0) |
182 | 0 | context->varnos = bms_add_member(context->varnos, cexpr->cvarno); |
183 | 0 | return false; |
184 | 0 | } |
185 | 0 | if (IsA(node, PlaceHolderVar)) |
186 | 0 | { |
187 | 0 | PlaceHolderVar *phv = (PlaceHolderVar *) node; |
188 | | |
189 | | /* |
190 | | * If a PlaceHolderVar is not of the target query level, ignore it, |
191 | | * instead recursing into its expression to see if it contains any |
192 | | * vars that are of the target level. We'll also do that when the |
193 | | * caller doesn't pass a "root" pointer. (We probably shouldn't see |
194 | | * PlaceHolderVars at all in such cases, but if we do, this is a |
195 | | * reasonable behavior.) |
196 | | */ |
197 | 0 | if (phv->phlevelsup == context->sublevels_up && |
198 | 0 | context->root != NULL) |
199 | 0 | { |
200 | | /* |
201 | | * Ideally, the PHV's contribution to context->varnos is its |
202 | | * ph_eval_at set. However, this code can be invoked before |
203 | | * that's been computed. If we cannot find a PlaceHolderInfo, |
204 | | * fall back to the conservative assumption that the PHV will be |
205 | | * evaluated at its syntactic level (phv->phrels). |
206 | | * |
207 | | * Another problem is that a PlaceHolderVar can appear in quals or |
208 | | * tlists that have been translated for use in a child appendrel. |
209 | | * Typically such a PHV is a parameter expression sourced by some |
210 | | * other relation, so that the translation from parent appendrel |
211 | | * to child doesn't change its phrels, and we should still take |
212 | | * ph_eval_at at face value. But in corner cases, the PHV's |
213 | | * original phrels can include the parent appendrel itself, in |
214 | | * which case the translated PHV will have the child appendrel in |
215 | | * phrels, and we must translate ph_eval_at to match. |
216 | | */ |
217 | 0 | PlaceHolderInfo *phinfo = NULL; |
218 | |
|
219 | 0 | if (phv->phlevelsup == 0) |
220 | 0 | { |
221 | 0 | if (phv->phid < context->root->placeholder_array_size) |
222 | 0 | phinfo = context->root->placeholder_array[phv->phid]; |
223 | 0 | } |
224 | 0 | if (phinfo == NULL) |
225 | 0 | { |
226 | | /* No PlaceHolderInfo yet, use phrels */ |
227 | 0 | context->varnos = bms_add_members(context->varnos, |
228 | 0 | phv->phrels); |
229 | 0 | } |
230 | 0 | else if (bms_equal(phv->phrels, phinfo->ph_var->phrels)) |
231 | 0 | { |
232 | | /* Normal case: use ph_eval_at */ |
233 | 0 | context->varnos = bms_add_members(context->varnos, |
234 | 0 | phinfo->ph_eval_at); |
235 | 0 | } |
236 | 0 | else |
237 | 0 | { |
238 | | /* Translated PlaceHolderVar: translate ph_eval_at to match */ |
239 | 0 | Relids newevalat, |
240 | 0 | delta; |
241 | | |
242 | | /* remove what was removed from phv->phrels ... */ |
243 | 0 | delta = bms_difference(phinfo->ph_var->phrels, phv->phrels); |
244 | 0 | newevalat = bms_difference(phinfo->ph_eval_at, delta); |
245 | | /* ... then if that was in fact part of ph_eval_at ... */ |
246 | 0 | if (!bms_equal(newevalat, phinfo->ph_eval_at)) |
247 | 0 | { |
248 | | /* ... add what was added */ |
249 | 0 | delta = bms_difference(phv->phrels, phinfo->ph_var->phrels); |
250 | 0 | newevalat = bms_join(newevalat, delta); |
251 | 0 | } |
252 | 0 | context->varnos = bms_join(context->varnos, |
253 | 0 | newevalat); |
254 | 0 | } |
255 | | |
256 | | /* |
257 | | * In all three cases, include phnullingrels in the result. We |
258 | | * don't worry about possibly needing to translate it, because |
259 | | * appendrels only translate varnos of baserels, not outer joins. |
260 | | */ |
261 | 0 | context->varnos = bms_add_members(context->varnos, |
262 | 0 | phv->phnullingrels); |
263 | 0 | return false; /* don't recurse into expression */ |
264 | 0 | } |
265 | 0 | } |
266 | 0 | else if (IsA(node, Query)) |
267 | 0 | { |
268 | | /* Recurse into RTE subquery or not-yet-planned sublink subquery */ |
269 | 0 | bool result; |
270 | |
|
271 | 0 | context->sublevels_up++; |
272 | 0 | result = query_tree_walker((Query *) node, pull_varnos_walker, |
273 | 0 | context, 0); |
274 | 0 | context->sublevels_up--; |
275 | 0 | return result; |
276 | 0 | } |
277 | 0 | return expression_tree_walker(node, pull_varnos_walker, context); |
278 | 0 | } |
279 | | |
280 | | |
281 | | /* |
282 | | * pull_varattnos |
283 | | * Find all the distinct attribute numbers present in an expression tree, |
284 | | * and add them to the initial contents of *varattnos. |
285 | | * Only Vars of the given varno and rtable level zero are considered. |
286 | | * |
287 | | * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that |
288 | | * we can include system attributes (e.g., OID) in the bitmap representation. |
289 | | * |
290 | | * Currently, this does not support unplanned subqueries; that is not needed |
291 | | * for current uses. It will handle already-planned SubPlan nodes, though, |
292 | | * looking into only the "testexpr" and the "args" list. (The subplan cannot |
293 | | * contain any other references to Vars of the current level.) |
294 | | */ |
295 | | void |
296 | | pull_varattnos(Node *node, Index varno, Bitmapset **varattnos) |
297 | 0 | { |
298 | 0 | pull_varattnos_context context; |
299 | |
|
300 | 0 | context.varattnos = *varattnos; |
301 | 0 | context.varno = varno; |
302 | |
|
303 | 0 | (void) pull_varattnos_walker(node, &context); |
304 | |
|
305 | 0 | *varattnos = context.varattnos; |
306 | 0 | } |
307 | | |
308 | | static bool |
309 | | pull_varattnos_walker(Node *node, pull_varattnos_context *context) |
310 | 0 | { |
311 | 0 | if (node == NULL) |
312 | 0 | return false; |
313 | 0 | if (IsA(node, Var)) |
314 | 0 | { |
315 | 0 | Var *var = (Var *) node; |
316 | |
|
317 | 0 | if (var->varno == context->varno && var->varlevelsup == 0) |
318 | 0 | context->varattnos = |
319 | 0 | bms_add_member(context->varattnos, |
320 | 0 | var->varattno - FirstLowInvalidHeapAttributeNumber); |
321 | 0 | return false; |
322 | 0 | } |
323 | | |
324 | | /* Should not find an unplanned subquery */ |
325 | 0 | Assert(!IsA(node, Query)); |
326 | |
|
327 | 0 | return expression_tree_walker(node, pull_varattnos_walker, context); |
328 | 0 | } |
329 | | |
330 | | |
331 | | /* |
332 | | * pull_vars_of_level |
333 | | * Create a list of all Vars (and PlaceHolderVars) referencing the |
334 | | * specified query level in the given parsetree. |
335 | | * |
336 | | * Caution: the Vars are not copied, only linked into the list. |
337 | | */ |
338 | | List * |
339 | | pull_vars_of_level(Node *node, int levelsup) |
340 | 0 | { |
341 | 0 | pull_vars_context context; |
342 | |
|
343 | 0 | context.vars = NIL; |
344 | 0 | context.sublevels_up = levelsup; |
345 | | |
346 | | /* |
347 | | * Must be prepared to start with a Query or a bare expression tree; if |
348 | | * it's a Query, we don't want to increment sublevels_up. |
349 | | */ |
350 | 0 | query_or_expression_tree_walker(node, |
351 | 0 | pull_vars_walker, |
352 | 0 | &context, |
353 | 0 | 0); |
354 | |
|
355 | 0 | return context.vars; |
356 | 0 | } |
357 | | |
358 | | static bool |
359 | | pull_vars_walker(Node *node, pull_vars_context *context) |
360 | 0 | { |
361 | 0 | if (node == NULL) |
362 | 0 | return false; |
363 | 0 | if (IsA(node, Var)) |
364 | 0 | { |
365 | 0 | Var *var = (Var *) node; |
366 | |
|
367 | 0 | if (var->varlevelsup == context->sublevels_up) |
368 | 0 | context->vars = lappend(context->vars, var); |
369 | 0 | return false; |
370 | 0 | } |
371 | 0 | if (IsA(node, PlaceHolderVar)) |
372 | 0 | { |
373 | 0 | PlaceHolderVar *phv = (PlaceHolderVar *) node; |
374 | |
|
375 | 0 | if (phv->phlevelsup == context->sublevels_up) |
376 | 0 | context->vars = lappend(context->vars, phv); |
377 | | /* we don't want to look into the contained expression */ |
378 | 0 | return false; |
379 | 0 | } |
380 | 0 | if (IsA(node, Query)) |
381 | 0 | { |
382 | | /* Recurse into RTE subquery or not-yet-planned sublink subquery */ |
383 | 0 | bool result; |
384 | |
|
385 | 0 | context->sublevels_up++; |
386 | 0 | result = query_tree_walker((Query *) node, pull_vars_walker, |
387 | 0 | context, 0); |
388 | 0 | context->sublevels_up--; |
389 | 0 | return result; |
390 | 0 | } |
391 | 0 | return expression_tree_walker(node, pull_vars_walker, context); |
392 | 0 | } |
393 | | |
394 | | |
395 | | /* |
396 | | * contain_var_clause |
397 | | * Recursively scan a clause to discover whether it contains any Var nodes |
398 | | * (of the current query level). |
399 | | * |
400 | | * Returns true if any varnode found. |
401 | | * |
402 | | * Does not examine subqueries, therefore must only be used after reduction |
403 | | * of sublinks to subplans! |
404 | | */ |
405 | | bool |
406 | | contain_var_clause(Node *node) |
407 | 0 | { |
408 | 0 | return contain_var_clause_walker(node, NULL); |
409 | 0 | } |
410 | | |
411 | | static bool |
412 | | contain_var_clause_walker(Node *node, void *context) |
413 | 0 | { |
414 | 0 | if (node == NULL) |
415 | 0 | return false; |
416 | 0 | if (IsA(node, Var)) |
417 | 0 | { |
418 | 0 | if (((Var *) node)->varlevelsup == 0) |
419 | 0 | return true; /* abort the tree traversal and return true */ |
420 | 0 | return false; |
421 | 0 | } |
422 | 0 | if (IsA(node, CurrentOfExpr)) |
423 | 0 | return true; |
424 | 0 | if (IsA(node, PlaceHolderVar)) |
425 | 0 | { |
426 | 0 | if (((PlaceHolderVar *) node)->phlevelsup == 0) |
427 | 0 | return true; /* abort the tree traversal and return true */ |
428 | | /* else fall through to check the contained expr */ |
429 | 0 | } |
430 | 0 | return expression_tree_walker(node, contain_var_clause_walker, context); |
431 | 0 | } |
432 | | |
433 | | |
434 | | /* |
435 | | * contain_vars_of_level |
436 | | * Recursively scan a clause to discover whether it contains any Var nodes |
437 | | * of the specified query level. |
438 | | * |
439 | | * Returns true if any such Var found. |
440 | | * |
441 | | * Will recurse into sublinks. Also, may be invoked directly on a Query. |
442 | | */ |
443 | | bool |
444 | | contain_vars_of_level(Node *node, int levelsup) |
445 | 0 | { |
446 | 0 | int sublevels_up = levelsup; |
447 | |
|
448 | 0 | return query_or_expression_tree_walker(node, |
449 | 0 | contain_vars_of_level_walker, |
450 | 0 | &sublevels_up, |
451 | 0 | 0); |
452 | 0 | } |
453 | | |
454 | | static bool |
455 | | contain_vars_of_level_walker(Node *node, int *sublevels_up) |
456 | 0 | { |
457 | 0 | if (node == NULL) |
458 | 0 | return false; |
459 | 0 | if (IsA(node, Var)) |
460 | 0 | { |
461 | 0 | if (((Var *) node)->varlevelsup == *sublevels_up) |
462 | 0 | return true; /* abort tree traversal and return true */ |
463 | 0 | return false; |
464 | 0 | } |
465 | 0 | if (IsA(node, CurrentOfExpr)) |
466 | 0 | { |
467 | 0 | if (*sublevels_up == 0) |
468 | 0 | return true; |
469 | 0 | return false; |
470 | 0 | } |
471 | 0 | if (IsA(node, PlaceHolderVar)) |
472 | 0 | { |
473 | 0 | if (((PlaceHolderVar *) node)->phlevelsup == *sublevels_up) |
474 | 0 | return true; /* abort the tree traversal and return true */ |
475 | | /* else fall through to check the contained expr */ |
476 | 0 | } |
477 | 0 | if (IsA(node, Query)) |
478 | 0 | { |
479 | | /* Recurse into subselects */ |
480 | 0 | bool result; |
481 | |
|
482 | 0 | (*sublevels_up)++; |
483 | 0 | result = query_tree_walker((Query *) node, |
484 | 0 | contain_vars_of_level_walker, |
485 | 0 | sublevels_up, |
486 | 0 | 0); |
487 | 0 | (*sublevels_up)--; |
488 | 0 | return result; |
489 | 0 | } |
490 | 0 | return expression_tree_walker(node, |
491 | 0 | contain_vars_of_level_walker, |
492 | 0 | sublevels_up); |
493 | 0 | } |
494 | | |
495 | | |
496 | | /* |
497 | | * contain_vars_returning_old_or_new |
498 | | * Recursively scan a clause to discover whether it contains any Var nodes |
499 | | * (of the current query level) whose varreturningtype is VAR_RETURNING_OLD |
500 | | * or VAR_RETURNING_NEW. |
501 | | * |
502 | | * Returns true if any found. |
503 | | * |
504 | | * Any ReturningExprs are also detected --- if an OLD/NEW Var was rewritten, |
505 | | * we still regard this as a clause that returns OLD/NEW values. |
506 | | * |
507 | | * Does not examine subqueries, therefore must only be used after reduction |
508 | | * of sublinks to subplans! |
509 | | */ |
510 | | bool |
511 | | contain_vars_returning_old_or_new(Node *node) |
512 | 0 | { |
513 | 0 | return contain_vars_returning_old_or_new_walker(node, NULL); |
514 | 0 | } |
515 | | |
516 | | static bool |
517 | | contain_vars_returning_old_or_new_walker(Node *node, void *context) |
518 | 0 | { |
519 | 0 | if (node == NULL) |
520 | 0 | return false; |
521 | 0 | if (IsA(node, Var)) |
522 | 0 | { |
523 | 0 | if (((Var *) node)->varlevelsup == 0 && |
524 | 0 | ((Var *) node)->varreturningtype != VAR_RETURNING_DEFAULT) |
525 | 0 | return true; /* abort the tree traversal and return true */ |
526 | 0 | return false; |
527 | 0 | } |
528 | 0 | if (IsA(node, ReturningExpr)) |
529 | 0 | { |
530 | 0 | if (((ReturningExpr *) node)->retlevelsup == 0) |
531 | 0 | return true; /* abort the tree traversal and return true */ |
532 | 0 | return false; |
533 | 0 | } |
534 | 0 | return expression_tree_walker(node, contain_vars_returning_old_or_new_walker, |
535 | 0 | context); |
536 | 0 | } |
537 | | |
538 | | |
539 | | /* |
540 | | * locate_var_of_level |
541 | | * Find the parse location of any Var of the specified query level. |
542 | | * |
543 | | * Returns -1 if no such Var is in the querytree, or if they all have |
544 | | * unknown parse location. (The former case is probably caller error, |
545 | | * but we don't bother to distinguish it from the latter case.) |
546 | | * |
547 | | * Will recurse into sublinks. Also, may be invoked directly on a Query. |
548 | | * |
549 | | * Note: it might seem appropriate to merge this functionality into |
550 | | * contain_vars_of_level, but that would complicate that function's API. |
551 | | * Currently, the only uses of this function are for error reporting, |
552 | | * and so shaving cycles probably isn't very important. |
553 | | */ |
554 | | int |
555 | | locate_var_of_level(Node *node, int levelsup) |
556 | 0 | { |
557 | 0 | locate_var_of_level_context context; |
558 | |
|
559 | 0 | context.var_location = -1; /* in case we find nothing */ |
560 | 0 | context.sublevels_up = levelsup; |
561 | |
|
562 | 0 | (void) query_or_expression_tree_walker(node, |
563 | 0 | locate_var_of_level_walker, |
564 | 0 | &context, |
565 | 0 | 0); |
566 | |
|
567 | 0 | return context.var_location; |
568 | 0 | } |
569 | | |
570 | | static bool |
571 | | locate_var_of_level_walker(Node *node, |
572 | | locate_var_of_level_context *context) |
573 | 0 | { |
574 | 0 | if (node == NULL) |
575 | 0 | return false; |
576 | 0 | if (IsA(node, Var)) |
577 | 0 | { |
578 | 0 | Var *var = (Var *) node; |
579 | |
|
580 | 0 | if (var->varlevelsup == context->sublevels_up && |
581 | 0 | var->location >= 0) |
582 | 0 | { |
583 | 0 | context->var_location = var->location; |
584 | 0 | return true; /* abort tree traversal and return true */ |
585 | 0 | } |
586 | 0 | return false; |
587 | 0 | } |
588 | 0 | if (IsA(node, CurrentOfExpr)) |
589 | 0 | { |
590 | | /* since CurrentOfExpr doesn't carry location, nothing we can do */ |
591 | 0 | return false; |
592 | 0 | } |
593 | | /* No extra code needed for PlaceHolderVar; just look in contained expr */ |
594 | 0 | if (IsA(node, Query)) |
595 | 0 | { |
596 | | /* Recurse into subselects */ |
597 | 0 | bool result; |
598 | |
|
599 | 0 | context->sublevels_up++; |
600 | 0 | result = query_tree_walker((Query *) node, |
601 | 0 | locate_var_of_level_walker, |
602 | 0 | context, |
603 | 0 | 0); |
604 | 0 | context->sublevels_up--; |
605 | 0 | return result; |
606 | 0 | } |
607 | 0 | return expression_tree_walker(node, |
608 | 0 | locate_var_of_level_walker, |
609 | 0 | context); |
610 | 0 | } |
611 | | |
612 | | |
613 | | /* |
614 | | * pull_var_clause |
615 | | * Recursively pulls all Var nodes from an expression clause. |
616 | | * |
617 | | * Aggrefs are handled according to these bits in 'flags': |
618 | | * PVC_INCLUDE_AGGREGATES include Aggrefs in output list |
619 | | * PVC_RECURSE_AGGREGATES recurse into Aggref arguments |
620 | | * neither flag throw error if Aggref found |
621 | | * Vars within an Aggref's expression are included in the result only |
622 | | * when PVC_RECURSE_AGGREGATES is specified. |
623 | | * |
624 | | * WindowFuncs are handled according to these bits in 'flags': |
625 | | * PVC_INCLUDE_WINDOWFUNCS include WindowFuncs in output list |
626 | | * PVC_RECURSE_WINDOWFUNCS recurse into WindowFunc arguments |
627 | | * neither flag throw error if WindowFunc found |
628 | | * Vars within a WindowFunc's expression are included in the result only |
629 | | * when PVC_RECURSE_WINDOWFUNCS is specified. |
630 | | * |
631 | | * PlaceHolderVars are handled according to these bits in 'flags': |
632 | | * PVC_INCLUDE_PLACEHOLDERS include PlaceHolderVars in output list |
633 | | * PVC_RECURSE_PLACEHOLDERS recurse into PlaceHolderVar arguments |
634 | | * neither flag throw error if PlaceHolderVar found |
635 | | * Vars within a PHV's expression are included in the result only |
636 | | * when PVC_RECURSE_PLACEHOLDERS is specified. |
637 | | * |
638 | | * GroupingFuncs are treated exactly like Aggrefs, and so do not need |
639 | | * their own flag bits. |
640 | | * |
641 | | * CurrentOfExpr nodes are ignored in all cases. |
642 | | * |
643 | | * Upper-level vars (with varlevelsup > 0) should not be seen here, |
644 | | * likewise for upper-level Aggrefs and PlaceHolderVars. |
645 | | * |
646 | | * Returns list of nodes found. Note the nodes themselves are not |
647 | | * copied, only referenced. |
648 | | * |
649 | | * Does not examine subqueries, therefore must only be used after reduction |
650 | | * of sublinks to subplans! |
651 | | */ |
652 | | List * |
653 | | pull_var_clause(Node *node, int flags) |
654 | 0 | { |
655 | 0 | pull_var_clause_context context; |
656 | | |
657 | | /* Assert that caller has not specified inconsistent flags */ |
658 | 0 | Assert((flags & (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES)) |
659 | 0 | != (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES)); |
660 | 0 | Assert((flags & (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS)) |
661 | 0 | != (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS)); |
662 | 0 | Assert((flags & (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS)) |
663 | 0 | != (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS)); |
664 | |
|
665 | 0 | context.varlist = NIL; |
666 | 0 | context.flags = flags; |
667 | |
|
668 | 0 | pull_var_clause_walker(node, &context); |
669 | 0 | return context.varlist; |
670 | 0 | } |
671 | | |
672 | | static bool |
673 | | pull_var_clause_walker(Node *node, pull_var_clause_context *context) |
674 | 0 | { |
675 | 0 | if (node == NULL) |
676 | 0 | return false; |
677 | 0 | if (IsA(node, Var)) |
678 | 0 | { |
679 | 0 | if (((Var *) node)->varlevelsup != 0) |
680 | 0 | elog(ERROR, "Upper-level Var found where not expected"); |
681 | 0 | context->varlist = lappend(context->varlist, node); |
682 | 0 | return false; |
683 | 0 | } |
684 | 0 | else if (IsA(node, Aggref)) |
685 | 0 | { |
686 | 0 | if (((Aggref *) node)->agglevelsup != 0) |
687 | 0 | elog(ERROR, "Upper-level Aggref found where not expected"); |
688 | 0 | if (context->flags & PVC_INCLUDE_AGGREGATES) |
689 | 0 | { |
690 | 0 | context->varlist = lappend(context->varlist, node); |
691 | | /* we do NOT descend into the contained expression */ |
692 | 0 | return false; |
693 | 0 | } |
694 | 0 | else if (context->flags & PVC_RECURSE_AGGREGATES) |
695 | 0 | { |
696 | | /* fall through to recurse into the aggregate's arguments */ |
697 | 0 | } |
698 | 0 | else |
699 | 0 | elog(ERROR, "Aggref found where not expected"); |
700 | 0 | } |
701 | 0 | else if (IsA(node, GroupingFunc)) |
702 | 0 | { |
703 | 0 | if (((GroupingFunc *) node)->agglevelsup != 0) |
704 | 0 | elog(ERROR, "Upper-level GROUPING found where not expected"); |
705 | 0 | if (context->flags & PVC_INCLUDE_AGGREGATES) |
706 | 0 | { |
707 | 0 | context->varlist = lappend(context->varlist, node); |
708 | | /* we do NOT descend into the contained expression */ |
709 | 0 | return false; |
710 | 0 | } |
711 | 0 | else if (context->flags & PVC_RECURSE_AGGREGATES) |
712 | 0 | { |
713 | | /* fall through to recurse into the GroupingFunc's arguments */ |
714 | 0 | } |
715 | 0 | else |
716 | 0 | elog(ERROR, "GROUPING found where not expected"); |
717 | 0 | } |
718 | 0 | else if (IsA(node, WindowFunc)) |
719 | 0 | { |
720 | | /* WindowFuncs have no levelsup field to check ... */ |
721 | 0 | if (context->flags & PVC_INCLUDE_WINDOWFUNCS) |
722 | 0 | { |
723 | 0 | context->varlist = lappend(context->varlist, node); |
724 | | /* we do NOT descend into the contained expressions */ |
725 | 0 | return false; |
726 | 0 | } |
727 | 0 | else if (context->flags & PVC_RECURSE_WINDOWFUNCS) |
728 | 0 | { |
729 | | /* fall through to recurse into the windowfunc's arguments */ |
730 | 0 | } |
731 | 0 | else |
732 | 0 | elog(ERROR, "WindowFunc found where not expected"); |
733 | 0 | } |
734 | 0 | else if (IsA(node, PlaceHolderVar)) |
735 | 0 | { |
736 | 0 | if (((PlaceHolderVar *) node)->phlevelsup != 0) |
737 | 0 | elog(ERROR, "Upper-level PlaceHolderVar found where not expected"); |
738 | 0 | if (context->flags & PVC_INCLUDE_PLACEHOLDERS) |
739 | 0 | { |
740 | 0 | context->varlist = lappend(context->varlist, node); |
741 | | /* we do NOT descend into the contained expression */ |
742 | 0 | return false; |
743 | 0 | } |
744 | 0 | else if (context->flags & PVC_RECURSE_PLACEHOLDERS) |
745 | 0 | { |
746 | | /* fall through to recurse into the placeholder's expression */ |
747 | 0 | } |
748 | 0 | else |
749 | 0 | elog(ERROR, "PlaceHolderVar found where not expected"); |
750 | 0 | } |
751 | 0 | return expression_tree_walker(node, pull_var_clause_walker, context); |
752 | 0 | } |
753 | | |
754 | | |
755 | | /* |
756 | | * flatten_join_alias_vars |
757 | | * Replace Vars that reference JOIN outputs with references to the original |
758 | | * relation variables instead. This allows quals involving such vars to be |
759 | | * pushed down. Whole-row Vars that reference JOIN relations are expanded |
760 | | * into RowExpr constructs that name the individual output Vars. This |
761 | | * is necessary since we will not scan the JOIN as a base relation, which |
762 | | * is the only way that the executor can directly handle whole-row Vars. |
763 | | * |
764 | | * This also adjusts relid sets found in some expression node types to |
765 | | * substitute the contained base+OJ rels for any join relid. |
766 | | * |
767 | | * If a JOIN contains sub-selects that have been flattened, its join alias |
768 | | * entries might now be arbitrary expressions, not just Vars. This affects |
769 | | * this function in two important ways. First, we might find ourselves |
770 | | * inserting SubLink expressions into subqueries, and we must make sure that |
771 | | * their Query.hasSubLinks fields get set to true if so. If there are any |
772 | | * SubLinks in the join alias lists, the outer Query should already have |
773 | | * hasSubLinks = true, so this is only relevant to un-flattened subqueries. |
774 | | * Second, we have to preserve any varnullingrels info attached to the |
775 | | * alias Vars we're replacing. If the replacement expression is a Var or |
776 | | * PlaceHolderVar or constructed from those, we can just add the |
777 | | * varnullingrels bits to the existing nullingrels field(s); otherwise |
778 | | * we have to add a PlaceHolderVar wrapper. |
779 | | */ |
780 | | Node * |
781 | | flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node) |
782 | 0 | { |
783 | 0 | flatten_join_alias_vars_context context; |
784 | | |
785 | | /* |
786 | | * We do not expect this to be applied to the whole Query, only to |
787 | | * expressions or LATERAL subqueries. Hence, if the top node is a Query, |
788 | | * it's okay to immediately increment sublevels_up. |
789 | | */ |
790 | 0 | Assert(node != (Node *) query); |
791 | |
|
792 | 0 | context.root = root; |
793 | 0 | context.query = query; |
794 | 0 | context.sublevels_up = 0; |
795 | | /* flag whether join aliases could possibly contain SubLinks */ |
796 | 0 | context.possible_sublink = query->hasSubLinks; |
797 | | /* if hasSubLinks is already true, no need to work hard */ |
798 | 0 | context.inserted_sublink = query->hasSubLinks; |
799 | |
|
800 | 0 | return flatten_join_alias_vars_mutator(node, &context); |
801 | 0 | } |
802 | | |
803 | | /* |
804 | | * flatten_join_alias_for_parser |
805 | | * |
806 | | * This variant of flatten_join_alias_vars is used by the parser, to expand |
807 | | * join alias Vars before checking GROUP BY validity. In that case we lack |
808 | | * a root structure. Fortunately, we'd only need the root for making |
809 | | * PlaceHolderVars. We can avoid making PlaceHolderVars in the parser's |
810 | | * usage because it won't be dealing with arbitrary expressions: so long as |
811 | | * adjust_standard_join_alias_expression can handle everything the parser |
812 | | * would make as a join alias expression, we're OK. |
813 | | * |
814 | | * The "node" might be part of a sub-query of the Query whose join alias |
815 | | * Vars are to be expanded. "sublevels_up" indicates how far below the |
816 | | * given query we are starting. |
817 | | */ |
818 | | Node * |
819 | | flatten_join_alias_for_parser(Query *query, Node *node, int sublevels_up) |
820 | 0 | { |
821 | 0 | flatten_join_alias_vars_context context; |
822 | | |
823 | | /* |
824 | | * We do not expect this to be applied to the whole Query, only to |
825 | | * expressions or LATERAL subqueries. Hence, if the top node is a Query, |
826 | | * it's okay to immediately increment sublevels_up. |
827 | | */ |
828 | 0 | Assert(node != (Node *) query); |
829 | |
|
830 | 0 | context.root = NULL; |
831 | 0 | context.query = query; |
832 | 0 | context.sublevels_up = sublevels_up; |
833 | | /* flag whether join aliases could possibly contain SubLinks */ |
834 | 0 | context.possible_sublink = query->hasSubLinks; |
835 | | /* if hasSubLinks is already true, no need to work hard */ |
836 | 0 | context.inserted_sublink = query->hasSubLinks; |
837 | |
|
838 | 0 | return flatten_join_alias_vars_mutator(node, &context); |
839 | 0 | } |
840 | | |
841 | | static Node * |
842 | | flatten_join_alias_vars_mutator(Node *node, |
843 | | flatten_join_alias_vars_context *context) |
844 | 0 | { |
845 | 0 | if (node == NULL) |
846 | 0 | return NULL; |
847 | 0 | if (IsA(node, Var)) |
848 | 0 | { |
849 | 0 | Var *var = (Var *) node; |
850 | 0 | RangeTblEntry *rte; |
851 | 0 | Node *newvar; |
852 | | |
853 | | /* No change unless Var belongs to a JOIN of the target level */ |
854 | 0 | if (var->varlevelsup != context->sublevels_up) |
855 | 0 | return node; /* no need to copy, really */ |
856 | 0 | rte = rt_fetch(var->varno, context->query->rtable); |
857 | 0 | if (rte->rtekind != RTE_JOIN) |
858 | 0 | return node; |
859 | 0 | if (var->varattno == InvalidAttrNumber) |
860 | 0 | { |
861 | | /* Must expand whole-row reference */ |
862 | 0 | RowExpr *rowexpr; |
863 | 0 | List *fields = NIL; |
864 | 0 | List *colnames = NIL; |
865 | 0 | ListCell *lv; |
866 | 0 | ListCell *ln; |
867 | |
|
868 | 0 | Assert(list_length(rte->joinaliasvars) == list_length(rte->eref->colnames)); |
869 | 0 | forboth(lv, rte->joinaliasvars, ln, rte->eref->colnames) |
870 | 0 | { |
871 | 0 | newvar = (Node *) lfirst(lv); |
872 | | /* Ignore dropped columns */ |
873 | 0 | if (newvar == NULL) |
874 | 0 | continue; |
875 | 0 | newvar = copyObject(newvar); |
876 | | |
877 | | /* |
878 | | * If we are expanding an alias carried down from an upper |
879 | | * query, must adjust its varlevelsup fields. |
880 | | */ |
881 | 0 | if (context->sublevels_up != 0) |
882 | 0 | IncrementVarSublevelsUp(newvar, context->sublevels_up, 0); |
883 | | /* Preserve original Var's location, if possible */ |
884 | 0 | if (IsA(newvar, Var)) |
885 | 0 | ((Var *) newvar)->location = var->location; |
886 | | /* Recurse in case join input is itself a join */ |
887 | | /* (also takes care of setting inserted_sublink if needed) */ |
888 | 0 | newvar = flatten_join_alias_vars_mutator(newvar, context); |
889 | 0 | fields = lappend(fields, newvar); |
890 | | /* We need the names of non-dropped columns, too */ |
891 | 0 | colnames = lappend(colnames, copyObject((Node *) lfirst(ln))); |
892 | 0 | } |
893 | 0 | rowexpr = makeNode(RowExpr); |
894 | 0 | rowexpr->args = fields; |
895 | 0 | rowexpr->row_typeid = var->vartype; |
896 | 0 | rowexpr->row_format = COERCE_IMPLICIT_CAST; |
897 | | /* vartype will always be RECORDOID, so we always need colnames */ |
898 | 0 | rowexpr->colnames = colnames; |
899 | 0 | rowexpr->location = var->location; |
900 | | |
901 | | /* Lastly, add any varnullingrels to the replacement expression */ |
902 | 0 | return add_nullingrels_if_needed(context->root, (Node *) rowexpr, |
903 | 0 | var); |
904 | 0 | } |
905 | | |
906 | | /* Expand join alias reference */ |
907 | 0 | Assert(var->varattno > 0); |
908 | 0 | newvar = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1); |
909 | 0 | Assert(newvar != NULL); |
910 | 0 | newvar = copyObject(newvar); |
911 | | |
912 | | /* |
913 | | * If we are expanding an alias carried down from an upper query, must |
914 | | * adjust its varlevelsup fields. |
915 | | */ |
916 | 0 | if (context->sublevels_up != 0) |
917 | 0 | IncrementVarSublevelsUp(newvar, context->sublevels_up, 0); |
918 | | |
919 | | /* Preserve original Var's location, if possible */ |
920 | 0 | if (IsA(newvar, Var)) |
921 | 0 | ((Var *) newvar)->location = var->location; |
922 | | |
923 | | /* Recurse in case join input is itself a join */ |
924 | 0 | newvar = flatten_join_alias_vars_mutator(newvar, context); |
925 | | |
926 | | /* Detect if we are adding a sublink to query */ |
927 | 0 | if (context->possible_sublink && !context->inserted_sublink) |
928 | 0 | context->inserted_sublink = checkExprHasSubLink(newvar); |
929 | | |
930 | | /* Lastly, add any varnullingrels to the replacement expression */ |
931 | 0 | return add_nullingrels_if_needed(context->root, newvar, var); |
932 | 0 | } |
933 | 0 | if (IsA(node, PlaceHolderVar)) |
934 | 0 | { |
935 | | /* Copy the PlaceHolderVar node with correct mutation of subnodes */ |
936 | 0 | PlaceHolderVar *phv; |
937 | |
|
938 | 0 | phv = (PlaceHolderVar *) expression_tree_mutator(node, |
939 | 0 | flatten_join_alias_vars_mutator, |
940 | 0 | context); |
941 | | /* now fix PlaceHolderVar's relid sets */ |
942 | 0 | if (phv->phlevelsup == context->sublevels_up) |
943 | 0 | { |
944 | 0 | phv->phrels = alias_relid_set(context->query, |
945 | 0 | phv->phrels); |
946 | | /* we *don't* change phnullingrels */ |
947 | 0 | } |
948 | 0 | return (Node *) phv; |
949 | 0 | } |
950 | | |
951 | 0 | if (IsA(node, Query)) |
952 | 0 | { |
953 | | /* Recurse into RTE subquery or not-yet-planned sublink subquery */ |
954 | 0 | Query *newnode; |
955 | 0 | bool save_inserted_sublink; |
956 | |
|
957 | 0 | context->sublevels_up++; |
958 | 0 | save_inserted_sublink = context->inserted_sublink; |
959 | 0 | context->inserted_sublink = ((Query *) node)->hasSubLinks; |
960 | 0 | newnode = query_tree_mutator((Query *) node, |
961 | 0 | flatten_join_alias_vars_mutator, |
962 | 0 | context, |
963 | 0 | QTW_IGNORE_JOINALIASES); |
964 | 0 | newnode->hasSubLinks |= context->inserted_sublink; |
965 | 0 | context->inserted_sublink = save_inserted_sublink; |
966 | 0 | context->sublevels_up--; |
967 | 0 | return (Node *) newnode; |
968 | 0 | } |
969 | | /* Already-planned tree not supported */ |
970 | 0 | Assert(!IsA(node, SubPlan)); |
971 | 0 | Assert(!IsA(node, AlternativeSubPlan)); |
972 | | /* Shouldn't need to handle these planner auxiliary nodes here */ |
973 | 0 | Assert(!IsA(node, SpecialJoinInfo)); |
974 | 0 | Assert(!IsA(node, PlaceHolderInfo)); |
975 | 0 | Assert(!IsA(node, MinMaxAggInfo)); |
976 | |
|
977 | 0 | return expression_tree_mutator(node, flatten_join_alias_vars_mutator, context); |
978 | 0 | } |
979 | | |
980 | | /* |
981 | | * flatten_group_exprs |
982 | | * Replace Vars that reference GROUP outputs with the underlying grouping |
983 | | * expressions. |
984 | | * |
985 | | * We have to preserve any varnullingrels info attached to the group Vars we're |
986 | | * replacing. If the replacement expression is a Var or PlaceHolderVar or |
987 | | * constructed from those, we can just add the varnullingrels bits to the |
988 | | * existing nullingrels field(s); otherwise we have to add a PlaceHolderVar |
989 | | * wrapper. |
990 | | * |
991 | | * NOTE: root may be passed as NULL, which is why we have to pass the Query |
992 | | * separately. We need the root itself only for preserving varnullingrels. |
993 | | * Callers can safely pass NULL if preserving varnullingrels is unnecessary for |
994 | | * their specific use case (e.g., deparsing source text, or scanning for |
995 | | * volatile functions), or if it is already guaranteed that the query cannot |
996 | | * contain grouping sets. |
997 | | */ |
998 | | Node * |
999 | | flatten_group_exprs(PlannerInfo *root, Query *query, Node *node) |
1000 | 0 | { |
1001 | 0 | flatten_join_alias_vars_context context; |
1002 | | |
1003 | | /* |
1004 | | * We do not expect this to be applied to the whole Query, only to |
1005 | | * expressions or LATERAL subqueries. Hence, if the top node is a Query, |
1006 | | * it's okay to immediately increment sublevels_up. |
1007 | | */ |
1008 | 0 | Assert(node != (Node *) query); |
1009 | |
|
1010 | 0 | context.root = root; |
1011 | 0 | context.query = query; |
1012 | 0 | context.sublevels_up = 0; |
1013 | | /* flag whether grouping expressions could possibly contain SubLinks */ |
1014 | 0 | context.possible_sublink = query->hasSubLinks; |
1015 | | /* if hasSubLinks is already true, no need to work hard */ |
1016 | 0 | context.inserted_sublink = query->hasSubLinks; |
1017 | |
|
1018 | 0 | return flatten_group_exprs_mutator(node, &context); |
1019 | 0 | } |
1020 | | |
1021 | | static Node * |
1022 | | flatten_group_exprs_mutator(Node *node, |
1023 | | flatten_join_alias_vars_context *context) |
1024 | 0 | { |
1025 | 0 | if (node == NULL) |
1026 | 0 | return NULL; |
1027 | 0 | if (IsA(node, Var)) |
1028 | 0 | { |
1029 | 0 | Var *var = (Var *) node; |
1030 | 0 | RangeTblEntry *rte; |
1031 | 0 | Node *newvar; |
1032 | | |
1033 | | /* No change unless Var belongs to the GROUP of the target level */ |
1034 | 0 | if (var->varlevelsup != context->sublevels_up) |
1035 | 0 | return node; /* no need to copy, really */ |
1036 | 0 | rte = rt_fetch(var->varno, context->query->rtable); |
1037 | 0 | if (rte->rtekind != RTE_GROUP) |
1038 | 0 | return node; |
1039 | | |
1040 | | /* Expand group exprs reference */ |
1041 | 0 | Assert(var->varattno > 0); |
1042 | 0 | newvar = (Node *) list_nth(rte->groupexprs, var->varattno - 1); |
1043 | 0 | Assert(newvar != NULL); |
1044 | 0 | newvar = copyObject(newvar); |
1045 | | |
1046 | | /* |
1047 | | * If we are expanding an expr carried down from an upper query, must |
1048 | | * adjust its varlevelsup fields. |
1049 | | */ |
1050 | 0 | if (context->sublevels_up != 0) |
1051 | 0 | IncrementVarSublevelsUp(newvar, context->sublevels_up, 0); |
1052 | | |
1053 | | /* Preserve original Var's location, if possible */ |
1054 | 0 | if (IsA(newvar, Var)) |
1055 | 0 | ((Var *) newvar)->location = var->location; |
1056 | | |
1057 | | /* Detect if we are adding a sublink to query */ |
1058 | 0 | if (context->possible_sublink && !context->inserted_sublink) |
1059 | 0 | context->inserted_sublink = checkExprHasSubLink(newvar); |
1060 | | |
1061 | | /* Lastly, add any varnullingrels to the replacement expression */ |
1062 | 0 | return mark_nullable_by_grouping(context->root, newvar, var); |
1063 | 0 | } |
1064 | | |
1065 | 0 | if (IsA(node, Aggref)) |
1066 | 0 | { |
1067 | 0 | Aggref *agg = (Aggref *) node; |
1068 | |
|
1069 | 0 | if ((int) agg->agglevelsup == context->sublevels_up) |
1070 | 0 | { |
1071 | | /* |
1072 | | * If we find an aggregate call of the original level, do not |
1073 | | * recurse into its normal arguments, ORDER BY arguments, or |
1074 | | * filter; there are no grouped vars there. But we should check |
1075 | | * direct arguments as though they weren't in an aggregate. |
1076 | | */ |
1077 | 0 | agg = copyObject(agg); |
1078 | 0 | agg->aggdirectargs = (List *) |
1079 | 0 | flatten_group_exprs_mutator((Node *) agg->aggdirectargs, context); |
1080 | |
|
1081 | 0 | return (Node *) agg; |
1082 | 0 | } |
1083 | | |
1084 | | /* |
1085 | | * We can skip recursing into aggregates of higher levels altogether, |
1086 | | * since they could not possibly contain Vars of concern to us (see |
1087 | | * transformAggregateCall). We do need to look at aggregates of lower |
1088 | | * levels, however. |
1089 | | */ |
1090 | 0 | if ((int) agg->agglevelsup > context->sublevels_up) |
1091 | 0 | return node; |
1092 | 0 | } |
1093 | | |
1094 | 0 | if (IsA(node, GroupingFunc)) |
1095 | 0 | { |
1096 | 0 | GroupingFunc *grp = (GroupingFunc *) node; |
1097 | | |
1098 | | /* |
1099 | | * If we find a GroupingFunc node of the original or higher level, do |
1100 | | * not recurse into its arguments; there are no grouped vars there. |
1101 | | */ |
1102 | 0 | if ((int) grp->agglevelsup >= context->sublevels_up) |
1103 | 0 | return node; |
1104 | 0 | } |
1105 | | |
1106 | 0 | if (IsA(node, Query)) |
1107 | 0 | { |
1108 | | /* Recurse into RTE subquery or not-yet-planned sublink subquery */ |
1109 | 0 | Query *newnode; |
1110 | 0 | bool save_inserted_sublink; |
1111 | |
|
1112 | 0 | context->sublevels_up++; |
1113 | 0 | save_inserted_sublink = context->inserted_sublink; |
1114 | 0 | context->inserted_sublink = ((Query *) node)->hasSubLinks; |
1115 | 0 | newnode = query_tree_mutator((Query *) node, |
1116 | 0 | flatten_group_exprs_mutator, |
1117 | 0 | context, |
1118 | 0 | QTW_IGNORE_GROUPEXPRS); |
1119 | 0 | newnode->hasSubLinks |= context->inserted_sublink; |
1120 | 0 | context->inserted_sublink = save_inserted_sublink; |
1121 | 0 | context->sublevels_up--; |
1122 | 0 | return (Node *) newnode; |
1123 | 0 | } |
1124 | | |
1125 | 0 | return expression_tree_mutator(node, flatten_group_exprs_mutator, |
1126 | 0 | context); |
1127 | 0 | } |
1128 | | |
1129 | | /* |
1130 | | * Add oldvar's varnullingrels, if any, to a flattened grouping expression. |
1131 | | * The newnode has been copied, so we can modify it freely. |
1132 | | */ |
1133 | | static Node * |
1134 | | mark_nullable_by_grouping(PlannerInfo *root, Node *newnode, Var *oldvar) |
1135 | 0 | { |
1136 | 0 | Relids relids; |
1137 | |
|
1138 | 0 | if (root == NULL) |
1139 | 0 | return newnode; |
1140 | 0 | if (oldvar->varnullingrels == NULL) |
1141 | 0 | return newnode; /* nothing to do */ |
1142 | | |
1143 | 0 | Assert(bms_equal(oldvar->varnullingrels, |
1144 | 0 | bms_make_singleton(root->group_rtindex))); |
1145 | |
|
1146 | 0 | relids = pull_varnos_of_level(root, newnode, oldvar->varlevelsup); |
1147 | |
|
1148 | 0 | if (!bms_is_empty(relids)) |
1149 | 0 | { |
1150 | | /* |
1151 | | * If the newnode is not variable-free, we set the nullingrels of Vars |
1152 | | * or PHVs that are contained in the expression. This is not really |
1153 | | * 'correct' in theory, because it is the whole expression that can be |
1154 | | * nullable by grouping sets, not its individual vars. But it works |
1155 | | * in practice, because what we need is that the expression can be |
1156 | | * somehow distinguished from the same expression in ECs, and marking |
1157 | | * its vars is sufficient for this purpose. |
1158 | | */ |
1159 | 0 | newnode = add_nulling_relids(newnode, |
1160 | 0 | relids, |
1161 | 0 | oldvar->varnullingrels); |
1162 | 0 | } |
1163 | 0 | else /* variable-free? */ |
1164 | 0 | { |
1165 | | /* |
1166 | | * If the newnode is variable-free and does not contain volatile |
1167 | | * functions or set-returning functions, it can be treated as a member |
1168 | | * of EC that is redundant. So wrap it in a new PlaceHolderVar to |
1169 | | * carry the nullingrels. Otherwise we do not bother to make any |
1170 | | * changes. |
1171 | | * |
1172 | | * Aggregate functions and window functions are not allowed in |
1173 | | * grouping expressions. |
1174 | | */ |
1175 | 0 | Assert(!contain_agg_clause(newnode)); |
1176 | 0 | Assert(!contain_window_function(newnode)); |
1177 | |
|
1178 | 0 | if (!contain_volatile_functions(newnode) && |
1179 | 0 | !expression_returns_set(newnode)) |
1180 | 0 | { |
1181 | 0 | PlaceHolderVar *newphv; |
1182 | 0 | Relids phrels; |
1183 | |
|
1184 | 0 | phrels = get_relids_in_jointree((Node *) root->parse->jointree, |
1185 | 0 | true, false); |
1186 | 0 | Assert(!bms_is_empty(phrels)); |
1187 | |
|
1188 | 0 | newphv = make_placeholder_expr(root, (Expr *) newnode, phrels); |
1189 | | /* newphv has zero phlevelsup and NULL phnullingrels; fix it */ |
1190 | 0 | newphv->phlevelsup = oldvar->varlevelsup; |
1191 | 0 | newphv->phnullingrels = bms_copy(oldvar->varnullingrels); |
1192 | 0 | newnode = (Node *) newphv; |
1193 | 0 | } |
1194 | 0 | } |
1195 | |
|
1196 | 0 | return newnode; |
1197 | 0 | } |
1198 | | |
1199 | | /* |
1200 | | * Add oldvar's varnullingrels, if any, to a flattened join alias expression. |
1201 | | * The newnode has been copied, so we can modify it freely. |
1202 | | */ |
1203 | | static Node * |
1204 | | add_nullingrels_if_needed(PlannerInfo *root, Node *newnode, Var *oldvar) |
1205 | 0 | { |
1206 | 0 | if (oldvar->varnullingrels == NULL) |
1207 | 0 | return newnode; /* nothing to do */ |
1208 | | /* If possible, do it by adding to existing nullingrel fields */ |
1209 | 0 | if (is_standard_join_alias_expression(newnode, oldvar)) |
1210 | 0 | adjust_standard_join_alias_expression(newnode, oldvar); |
1211 | 0 | else if (root) |
1212 | 0 | { |
1213 | | /* |
1214 | | * We can insert a PlaceHolderVar to carry the nullingrels. However, |
1215 | | * deciding where to evaluate the PHV is slightly tricky. We first |
1216 | | * try to evaluate it at the natural semantic level of the new |
1217 | | * expression; but if that expression is variable-free, fall back to |
1218 | | * evaluating it at the join that the oldvar is an alias Var for. |
1219 | | */ |
1220 | 0 | PlaceHolderVar *newphv; |
1221 | 0 | Index levelsup = oldvar->varlevelsup; |
1222 | 0 | Relids phrels = pull_varnos_of_level(root, newnode, levelsup); |
1223 | |
|
1224 | 0 | if (bms_is_empty(phrels)) /* variable-free? */ |
1225 | 0 | { |
1226 | 0 | if (levelsup != 0) /* this won't work otherwise */ |
1227 | 0 | elog(ERROR, "unsupported join alias expression"); |
1228 | 0 | phrels = get_relids_for_join(root->parse, oldvar->varno); |
1229 | | /* If it's an outer join, eval below not above the join */ |
1230 | 0 | phrels = bms_del_member(phrels, oldvar->varno); |
1231 | 0 | Assert(!bms_is_empty(phrels)); |
1232 | 0 | } |
1233 | 0 | newphv = make_placeholder_expr(root, (Expr *) newnode, phrels); |
1234 | | /* newphv has zero phlevelsup and NULL phnullingrels; fix it */ |
1235 | 0 | newphv->phlevelsup = levelsup; |
1236 | 0 | newphv->phnullingrels = bms_copy(oldvar->varnullingrels); |
1237 | 0 | newnode = (Node *) newphv; |
1238 | 0 | } |
1239 | 0 | else |
1240 | 0 | { |
1241 | | /* ooops, we're missing support for something the parser can make */ |
1242 | 0 | elog(ERROR, "unsupported join alias expression"); |
1243 | 0 | } |
1244 | 0 | return newnode; |
1245 | 0 | } |
1246 | | |
1247 | | /* |
1248 | | * Check to see if we can insert nullingrels into this join alias expression |
1249 | | * without use of a separate PlaceHolderVar. |
1250 | | * |
1251 | | * This will handle Vars, PlaceHolderVars, and implicit-coercion and COALESCE |
1252 | | * expressions built from those. This coverage needs to handle anything |
1253 | | * that the parser would put into joinaliasvars. |
1254 | | */ |
1255 | | static bool |
1256 | | is_standard_join_alias_expression(Node *newnode, Var *oldvar) |
1257 | 0 | { |
1258 | 0 | if (newnode == NULL) |
1259 | 0 | return false; |
1260 | 0 | if (IsA(newnode, Var) && |
1261 | 0 | ((Var *) newnode)->varlevelsup == oldvar->varlevelsup) |
1262 | 0 | return true; |
1263 | 0 | else if (IsA(newnode, PlaceHolderVar) && |
1264 | 0 | ((PlaceHolderVar *) newnode)->phlevelsup == oldvar->varlevelsup) |
1265 | 0 | return true; |
1266 | 0 | else if (IsA(newnode, FuncExpr)) |
1267 | 0 | { |
1268 | 0 | FuncExpr *fexpr = (FuncExpr *) newnode; |
1269 | | |
1270 | | /* |
1271 | | * We need to assume that the function wouldn't produce non-NULL from |
1272 | | * NULL, which is reasonable for implicit coercions but otherwise not |
1273 | | * so much. (Looking at its strictness is likely overkill, and anyway |
1274 | | * it would cause us to fail if someone forgot to mark an implicit |
1275 | | * coercion as strict.) |
1276 | | */ |
1277 | 0 | if (fexpr->funcformat != COERCE_IMPLICIT_CAST || |
1278 | 0 | fexpr->args == NIL) |
1279 | 0 | return false; |
1280 | | |
1281 | | /* |
1282 | | * Examine only the first argument --- coercions might have additional |
1283 | | * arguments that are constants. |
1284 | | */ |
1285 | 0 | return is_standard_join_alias_expression(linitial(fexpr->args), oldvar); |
1286 | 0 | } |
1287 | 0 | else if (IsA(newnode, RelabelType)) |
1288 | 0 | { |
1289 | 0 | RelabelType *relabel = (RelabelType *) newnode; |
1290 | | |
1291 | | /* This definitely won't produce non-NULL from NULL */ |
1292 | 0 | return is_standard_join_alias_expression((Node *) relabel->arg, oldvar); |
1293 | 0 | } |
1294 | 0 | else if (IsA(newnode, CoerceViaIO)) |
1295 | 0 | { |
1296 | 0 | CoerceViaIO *iocoerce = (CoerceViaIO *) newnode; |
1297 | | |
1298 | | /* This definitely won't produce non-NULL from NULL */ |
1299 | 0 | return is_standard_join_alias_expression((Node *) iocoerce->arg, oldvar); |
1300 | 0 | } |
1301 | 0 | else if (IsA(newnode, ArrayCoerceExpr)) |
1302 | 0 | { |
1303 | 0 | ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) newnode; |
1304 | | |
1305 | | /* This definitely won't produce non-NULL from NULL (at array level) */ |
1306 | 0 | return is_standard_join_alias_expression((Node *) acoerce->arg, oldvar); |
1307 | 0 | } |
1308 | 0 | else if (IsA(newnode, CoalesceExpr)) |
1309 | 0 | { |
1310 | 0 | CoalesceExpr *cexpr = (CoalesceExpr *) newnode; |
1311 | 0 | ListCell *lc; |
1312 | |
|
1313 | 0 | Assert(cexpr->args != NIL); |
1314 | 0 | foreach(lc, cexpr->args) |
1315 | 0 | { |
1316 | 0 | if (!is_standard_join_alias_expression(lfirst(lc), oldvar)) |
1317 | 0 | return false; |
1318 | 0 | } |
1319 | 0 | return true; |
1320 | 0 | } |
1321 | 0 | else |
1322 | 0 | return false; |
1323 | 0 | } |
1324 | | |
1325 | | /* |
1326 | | * Insert nullingrels into an expression accepted by |
1327 | | * is_standard_join_alias_expression. |
1328 | | */ |
1329 | | static void |
1330 | | adjust_standard_join_alias_expression(Node *newnode, Var *oldvar) |
1331 | 0 | { |
1332 | 0 | if (IsA(newnode, Var) && |
1333 | 0 | ((Var *) newnode)->varlevelsup == oldvar->varlevelsup) |
1334 | 0 | { |
1335 | 0 | Var *newvar = (Var *) newnode; |
1336 | |
|
1337 | 0 | newvar->varnullingrels = bms_add_members(newvar->varnullingrels, |
1338 | 0 | oldvar->varnullingrels); |
1339 | 0 | } |
1340 | 0 | else if (IsA(newnode, PlaceHolderVar) && |
1341 | 0 | ((PlaceHolderVar *) newnode)->phlevelsup == oldvar->varlevelsup) |
1342 | 0 | { |
1343 | 0 | PlaceHolderVar *newphv = (PlaceHolderVar *) newnode; |
1344 | |
|
1345 | 0 | newphv->phnullingrels = bms_add_members(newphv->phnullingrels, |
1346 | 0 | oldvar->varnullingrels); |
1347 | 0 | } |
1348 | 0 | else if (IsA(newnode, FuncExpr)) |
1349 | 0 | { |
1350 | 0 | FuncExpr *fexpr = (FuncExpr *) newnode; |
1351 | |
|
1352 | 0 | adjust_standard_join_alias_expression(linitial(fexpr->args), oldvar); |
1353 | 0 | } |
1354 | 0 | else if (IsA(newnode, RelabelType)) |
1355 | 0 | { |
1356 | 0 | RelabelType *relabel = (RelabelType *) newnode; |
1357 | |
|
1358 | 0 | adjust_standard_join_alias_expression((Node *) relabel->arg, oldvar); |
1359 | 0 | } |
1360 | 0 | else if (IsA(newnode, CoerceViaIO)) |
1361 | 0 | { |
1362 | 0 | CoerceViaIO *iocoerce = (CoerceViaIO *) newnode; |
1363 | |
|
1364 | 0 | adjust_standard_join_alias_expression((Node *) iocoerce->arg, oldvar); |
1365 | 0 | } |
1366 | 0 | else if (IsA(newnode, ArrayCoerceExpr)) |
1367 | 0 | { |
1368 | 0 | ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) newnode; |
1369 | |
|
1370 | 0 | adjust_standard_join_alias_expression((Node *) acoerce->arg, oldvar); |
1371 | 0 | } |
1372 | 0 | else if (IsA(newnode, CoalesceExpr)) |
1373 | 0 | { |
1374 | 0 | CoalesceExpr *cexpr = (CoalesceExpr *) newnode; |
1375 | 0 | ListCell *lc; |
1376 | |
|
1377 | 0 | Assert(cexpr->args != NIL); |
1378 | 0 | foreach(lc, cexpr->args) |
1379 | 0 | { |
1380 | 0 | adjust_standard_join_alias_expression(lfirst(lc), oldvar); |
1381 | 0 | } |
1382 | 0 | } |
1383 | 0 | else |
1384 | 0 | Assert(false); |
1385 | 0 | } |
1386 | | |
1387 | | /* |
1388 | | * alias_relid_set: in a set of RT indexes, replace joins by their |
1389 | | * underlying base+OJ relids |
1390 | | */ |
1391 | | static Relids |
1392 | | alias_relid_set(Query *query, Relids relids) |
1393 | 0 | { |
1394 | 0 | Relids result = NULL; |
1395 | 0 | int rtindex; |
1396 | |
|
1397 | 0 | rtindex = -1; |
1398 | 0 | while ((rtindex = bms_next_member(relids, rtindex)) >= 0) |
1399 | 0 | { |
1400 | 0 | RangeTblEntry *rte = rt_fetch(rtindex, query->rtable); |
1401 | |
|
1402 | 0 | if (rte->rtekind == RTE_JOIN) |
1403 | 0 | result = bms_join(result, get_relids_for_join(query, rtindex)); |
1404 | 0 | else |
1405 | 0 | result = bms_add_member(result, rtindex); |
1406 | 0 | } |
1407 | 0 | return result; |
1408 | 0 | } |