Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * rewriteManip.c
4 : *
5 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
6 : * Portions Copyright (c) 1994, Regents of the University of California
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/rewrite/rewriteManip.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "catalog/pg_type.h"
17 : #include "nodes/makefuncs.h"
18 : #include "nodes/nodeFuncs.h"
19 : #include "nodes/pathnodes.h"
20 : #include "nodes/plannodes.h"
21 : #include "parser/parse_coerce.h"
22 : #include "parser/parse_relation.h"
23 : #include "parser/parsetree.h"
24 : #include "rewrite/rewriteManip.h"
25 :
26 :
27 : typedef struct
28 : {
29 : int sublevels_up;
30 : } contain_aggs_of_level_context;
31 :
32 : typedef struct
33 : {
34 : int agg_location;
35 : int sublevels_up;
36 : } locate_agg_of_level_context;
37 :
38 : typedef struct
39 : {
40 : int win_location;
41 : } locate_windowfunc_context;
42 :
43 : typedef struct
44 : {
45 : const Bitmapset *target_relids;
46 : const Bitmapset *added_relids;
47 : int sublevels_up;
48 : } add_nulling_relids_context;
49 :
50 : typedef struct
51 : {
52 : const Bitmapset *removable_relids;
53 : const Bitmapset *except_relids;
54 : int sublevels_up;
55 : } remove_nulling_relids_context;
56 :
57 : static bool contain_aggs_of_level_walker(Node *node,
58 : contain_aggs_of_level_context *context);
59 : static bool locate_agg_of_level_walker(Node *node,
60 : locate_agg_of_level_context *context);
61 : static bool contain_windowfuncs_walker(Node *node, void *context);
62 : static bool locate_windowfunc_walker(Node *node,
63 : locate_windowfunc_context *context);
64 : static bool checkExprHasSubLink_walker(Node *node, void *context);
65 : static Relids offset_relid_set(Relids relids, int offset);
66 : static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
67 : static Node *add_nulling_relids_mutator(Node *node,
68 : add_nulling_relids_context *context);
69 : static Node *remove_nulling_relids_mutator(Node *node,
70 : remove_nulling_relids_context *context);
71 :
72 :
73 : /*
74 : * contain_aggs_of_level -
75 : * Check if an expression contains an aggregate function call of a
76 : * specified query level.
77 : *
78 : * The objective of this routine is to detect whether there are aggregates
79 : * belonging to the given query level. Aggregates belonging to subqueries
80 : * or outer queries do NOT cause a true result. We must recurse into
81 : * subqueries to detect outer-reference aggregates that logically belong to
82 : * the specified query level.
83 : */
84 : bool
85 744 : contain_aggs_of_level(Node *node, int levelsup)
86 : {
87 : contain_aggs_of_level_context context;
88 :
89 744 : context.sublevels_up = levelsup;
90 :
91 : /*
92 : * Must be prepared to start with a Query or a bare expression tree; if
93 : * it's a Query, we don't want to increment sublevels_up.
94 : */
95 744 : return query_or_expression_tree_walker(node,
96 : contain_aggs_of_level_walker,
97 : &context,
98 : 0);
99 : }
100 :
101 : static bool
102 1748 : contain_aggs_of_level_walker(Node *node,
103 : contain_aggs_of_level_context *context)
104 : {
105 1748 : if (node == NULL)
106 450 : return false;
107 1298 : if (IsA(node, Aggref))
108 : {
109 0 : if (((Aggref *) node)->agglevelsup == context->sublevels_up)
110 0 : return true; /* abort the tree traversal and return true */
111 : /* else fall through to examine argument */
112 : }
113 1298 : if (IsA(node, GroupingFunc))
114 : {
115 0 : if (((GroupingFunc *) node)->agglevelsup == context->sublevels_up)
116 0 : return true;
117 : /* else fall through to examine argument */
118 : }
119 1298 : if (IsA(node, Query))
120 : {
121 : /* Recurse into subselects */
122 : bool result;
123 :
124 28 : context->sublevels_up++;
125 28 : result = query_tree_walker((Query *) node,
126 : contain_aggs_of_level_walker,
127 : context, 0);
128 28 : context->sublevels_up--;
129 28 : return result;
130 : }
131 1270 : return expression_tree_walker(node, contain_aggs_of_level_walker,
132 : context);
133 : }
134 :
135 : /*
136 : * locate_agg_of_level -
137 : * Find the parse location of any aggregate of the specified query level.
138 : *
139 : * Returns -1 if no such agg is in the querytree, or if they all have
140 : * unknown parse location. (The former case is probably caller error,
141 : * but we don't bother to distinguish it from the latter case.)
142 : *
143 : * Note: it might seem appropriate to merge this functionality into
144 : * contain_aggs_of_level, but that would complicate that function's API.
145 : * Currently, the only uses of this function are for error reporting,
146 : * and so shaving cycles probably isn't very important.
147 : */
148 : int
149 60 : locate_agg_of_level(Node *node, int levelsup)
150 : {
151 : locate_agg_of_level_context context;
152 :
153 60 : context.agg_location = -1; /* in case we find nothing */
154 60 : context.sublevels_up = levelsup;
155 :
156 : /*
157 : * Must be prepared to start with a Query or a bare expression tree; if
158 : * it's a Query, we don't want to increment sublevels_up.
159 : */
160 60 : (void) query_or_expression_tree_walker(node,
161 : locate_agg_of_level_walker,
162 : &context,
163 : 0);
164 :
165 60 : return context.agg_location;
166 : }
167 :
168 : static bool
169 240 : locate_agg_of_level_walker(Node *node,
170 : locate_agg_of_level_context *context)
171 : {
172 240 : if (node == NULL)
173 12 : return false;
174 228 : if (IsA(node, Aggref))
175 : {
176 54 : if (((Aggref *) node)->agglevelsup == context->sublevels_up &&
177 48 : ((Aggref *) node)->location >= 0)
178 : {
179 48 : context->agg_location = ((Aggref *) node)->location;
180 48 : return true; /* abort the tree traversal and return true */
181 : }
182 : /* else fall through to examine argument */
183 : }
184 180 : if (IsA(node, GroupingFunc))
185 : {
186 0 : if (((GroupingFunc *) node)->agglevelsup == context->sublevels_up &&
187 0 : ((GroupingFunc *) node)->location >= 0)
188 : {
189 0 : context->agg_location = ((GroupingFunc *) node)->location;
190 0 : return true; /* abort the tree traversal and return true */
191 : }
192 : }
193 180 : if (IsA(node, Query))
194 : {
195 : /* Recurse into subselects */
196 : bool result;
197 :
198 12 : context->sublevels_up++;
199 12 : result = query_tree_walker((Query *) node,
200 : locate_agg_of_level_walker,
201 : context, 0);
202 12 : context->sublevels_up--;
203 12 : return result;
204 : }
205 168 : return expression_tree_walker(node, locate_agg_of_level_walker, context);
206 : }
207 :
208 : /*
209 : * contain_windowfuncs -
210 : * Check if an expression contains a window function call of the
211 : * current query level.
212 : */
213 : bool
214 8960 : contain_windowfuncs(Node *node)
215 : {
216 : /*
217 : * Must be prepared to start with a Query or a bare expression tree; if
218 : * it's a Query, we don't want to increment sublevels_up.
219 : */
220 8960 : return query_or_expression_tree_walker(node,
221 : contain_windowfuncs_walker,
222 : NULL,
223 : 0);
224 : }
225 :
226 : static bool
227 9836 : contain_windowfuncs_walker(Node *node, void *context)
228 : {
229 9836 : if (node == NULL)
230 168 : return false;
231 9668 : if (IsA(node, WindowFunc))
232 12 : return true; /* abort the tree traversal and return true */
233 : /* Mustn't recurse into subselects */
234 9656 : return expression_tree_walker(node, contain_windowfuncs_walker, context);
235 : }
236 :
237 : /*
238 : * locate_windowfunc -
239 : * Find the parse location of any windowfunc of the current query level.
240 : *
241 : * Returns -1 if no such windowfunc is in the querytree, or if they all have
242 : * unknown parse location. (The former case is probably caller error,
243 : * but we don't bother to distinguish it from the latter case.)
244 : *
245 : * Note: it might seem appropriate to merge this functionality into
246 : * contain_windowfuncs, but that would complicate that function's API.
247 : * Currently, the only uses of this function are for error reporting,
248 : * and so shaving cycles probably isn't very important.
249 : */
250 : int
251 6 : locate_windowfunc(Node *node)
252 : {
253 : locate_windowfunc_context context;
254 :
255 6 : context.win_location = -1; /* in case we find nothing */
256 :
257 : /*
258 : * Must be prepared to start with a Query or a bare expression tree; if
259 : * it's a Query, we don't want to increment sublevels_up.
260 : */
261 6 : (void) query_or_expression_tree_walker(node,
262 : locate_windowfunc_walker,
263 : &context,
264 : 0);
265 :
266 6 : return context.win_location;
267 : }
268 :
269 : static bool
270 6 : locate_windowfunc_walker(Node *node, locate_windowfunc_context *context)
271 : {
272 6 : if (node == NULL)
273 0 : return false;
274 6 : if (IsA(node, WindowFunc))
275 : {
276 6 : if (((WindowFunc *) node)->location >= 0)
277 : {
278 6 : context->win_location = ((WindowFunc *) node)->location;
279 6 : return true; /* abort the tree traversal and return true */
280 : }
281 : /* else fall through to examine argument */
282 : }
283 : /* Mustn't recurse into subselects */
284 0 : return expression_tree_walker(node, locate_windowfunc_walker, context);
285 : }
286 :
287 : /*
288 : * checkExprHasSubLink -
289 : * Check if an expression contains a SubLink.
290 : */
291 : bool
292 110938 : checkExprHasSubLink(Node *node)
293 : {
294 : /*
295 : * If a Query is passed, examine it --- but we should not recurse into
296 : * sub-Queries that are in its rangetable or CTE list.
297 : */
298 110938 : return query_or_expression_tree_walker(node,
299 : checkExprHasSubLink_walker,
300 : NULL,
301 : QTW_IGNORE_RC_SUBQUERIES);
302 : }
303 :
304 : static bool
305 185018 : checkExprHasSubLink_walker(Node *node, void *context)
306 : {
307 185018 : if (node == NULL)
308 3588 : return false;
309 181430 : if (IsA(node, SubLink))
310 1708 : return true; /* abort the tree traversal and return true */
311 179722 : return expression_tree_walker(node, checkExprHasSubLink_walker, context);
312 : }
313 :
314 : /*
315 : * Check for MULTIEXPR Param within expression tree
316 : *
317 : * We intentionally don't descend into SubLinks: only Params at the current
318 : * query level are of interest.
319 : */
320 : static bool
321 12002 : contains_multiexpr_param(Node *node, void *context)
322 : {
323 12002 : if (node == NULL)
324 78 : return false;
325 11924 : if (IsA(node, Param))
326 : {
327 0 : if (((Param *) node)->paramkind == PARAM_MULTIEXPR)
328 0 : return true; /* abort the tree traversal and return true */
329 0 : return false;
330 : }
331 11924 : return expression_tree_walker(node, contains_multiexpr_param, context);
332 : }
333 :
334 : /*
335 : * CombineRangeTables
336 : * Adds the RTEs of 'src_rtable' into 'dst_rtable'
337 : *
338 : * This also adds the RTEPermissionInfos of 'src_perminfos' (belonging to the
339 : * RTEs in 'src_rtable') into *dst_perminfos and also updates perminfoindex of
340 : * the RTEs in 'src_rtable' to now point to the perminfos' indexes in
341 : * *dst_perminfos.
342 : *
343 : * Note that this changes both 'dst_rtable' and 'dst_perminfos' destructively,
344 : * so the caller should have better passed safe-to-modify copies.
345 : */
346 : void
347 36748 : CombineRangeTables(List **dst_rtable, List **dst_perminfos,
348 : List *src_rtable, List *src_perminfos)
349 : {
350 : ListCell *l;
351 36748 : int offset = list_length(*dst_perminfos);
352 :
353 36748 : if (offset > 0)
354 : {
355 89674 : foreach(l, src_rtable)
356 : {
357 60430 : RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
358 :
359 60430 : if (rte->perminfoindex > 0)
360 31652 : rte->perminfoindex += offset;
361 : }
362 : }
363 :
364 36748 : *dst_perminfos = list_concat(*dst_perminfos, src_perminfos);
365 36748 : *dst_rtable = list_concat(*dst_rtable, src_rtable);
366 36748 : }
367 :
368 : /*
369 : * OffsetVarNodes - adjust Vars when appending one query's RT to another
370 : *
371 : * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
372 : * and increment their varno fields (rangetable indexes) by 'offset'.
373 : * The varnosyn fields are adjusted similarly. Also, adjust other nodes
374 : * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
375 : *
376 : * NOTE: although this has the form of a walker, we cheat and modify the
377 : * nodes in-place. The given expression tree should have been copied
378 : * earlier to ensure that no unwanted side-effects occur!
379 : */
380 :
381 : typedef struct
382 : {
383 : int offset;
384 : int sublevels_up;
385 : } OffsetVarNodes_context;
386 :
387 : static bool
388 1933850 : OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
389 : {
390 1933850 : if (node == NULL)
391 576510 : return false;
392 1357340 : if (IsA(node, Var))
393 : {
394 713694 : Var *var = (Var *) node;
395 :
396 713694 : if (var->varlevelsup == context->sublevels_up)
397 : {
398 691330 : var->varno += context->offset;
399 691330 : var->varnullingrels = offset_relid_set(var->varnullingrels,
400 : context->offset);
401 691330 : if (var->varnosyn > 0)
402 691330 : var->varnosyn += context->offset;
403 : }
404 713694 : return false;
405 : }
406 643646 : if (IsA(node, CurrentOfExpr))
407 : {
408 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
409 :
410 0 : if (context->sublevels_up == 0)
411 0 : cexpr->cvarno += context->offset;
412 0 : return false;
413 : }
414 643646 : if (IsA(node, RangeTblRef))
415 : {
416 54008 : RangeTblRef *rtr = (RangeTblRef *) node;
417 :
418 54008 : if (context->sublevels_up == 0)
419 49456 : rtr->rtindex += context->offset;
420 : /* the subquery itself is visited separately */
421 54008 : return false;
422 : }
423 589638 : if (IsA(node, JoinExpr))
424 : {
425 13286 : JoinExpr *j = (JoinExpr *) node;
426 :
427 13286 : if (j->rtindex && context->sublevels_up == 0)
428 12600 : j->rtindex += context->offset;
429 : /* fall through to examine children */
430 : }
431 589638 : if (IsA(node, PlaceHolderVar))
432 : {
433 440 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
434 :
435 440 : if (phv->phlevelsup == context->sublevels_up)
436 : {
437 332 : phv->phrels = offset_relid_set(phv->phrels,
438 : context->offset);
439 332 : phv->phnullingrels = offset_relid_set(phv->phnullingrels,
440 : context->offset);
441 : }
442 : /* fall through to examine children */
443 : }
444 589638 : if (IsA(node, AppendRelInfo))
445 : {
446 584 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
447 :
448 584 : if (context->sublevels_up == 0)
449 : {
450 584 : appinfo->parent_relid += context->offset;
451 584 : appinfo->child_relid += context->offset;
452 : }
453 : /* fall through to examine children */
454 : }
455 : /* Shouldn't need to handle other planner auxiliary nodes here */
456 : Assert(!IsA(node, PlanRowMark));
457 : Assert(!IsA(node, SpecialJoinInfo));
458 : Assert(!IsA(node, PlaceHolderInfo));
459 : Assert(!IsA(node, MinMaxAggInfo));
460 :
461 589638 : if (IsA(node, Query))
462 : {
463 : /* Recurse into subselects */
464 : bool result;
465 :
466 4034 : context->sublevels_up++;
467 4034 : result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
468 : context, 0);
469 4034 : context->sublevels_up--;
470 4034 : return result;
471 : }
472 585604 : return expression_tree_walker(node, OffsetVarNodes_walker, context);
473 : }
474 :
475 : void
476 70512 : OffsetVarNodes(Node *node, int offset, int sublevels_up)
477 : {
478 : OffsetVarNodes_context context;
479 :
480 70512 : context.offset = offset;
481 70512 : context.sublevels_up = sublevels_up;
482 :
483 : /*
484 : * Must be prepared to start with a Query or a bare expression tree; if
485 : * it's a Query, go straight to query_tree_walker to make sure that
486 : * sublevels_up doesn't get incremented prematurely.
487 : */
488 70512 : if (node && IsA(node, Query))
489 35256 : {
490 35256 : Query *qry = (Query *) node;
491 :
492 : /*
493 : * If we are starting at a Query, and sublevels_up is zero, then we
494 : * must also fix rangetable indexes in the Query itself --- namely
495 : * resultRelation, mergeTargetRelation, exclRelIndex and rowMarks
496 : * entries. sublevels_up cannot be zero when recursing into a
497 : * subquery, so there's no need to have the same logic inside
498 : * OffsetVarNodes_walker.
499 : */
500 35256 : if (sublevels_up == 0)
501 : {
502 : ListCell *l;
503 :
504 35256 : if (qry->resultRelation)
505 1260 : qry->resultRelation += offset;
506 :
507 35256 : if (qry->mergeTargetRelation)
508 0 : qry->mergeTargetRelation += offset;
509 :
510 35256 : if (qry->onConflict && qry->onConflict->exclRelIndex)
511 36 : qry->onConflict->exclRelIndex += offset;
512 :
513 35404 : foreach(l, qry->rowMarks)
514 : {
515 148 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
516 :
517 148 : rc->rti += offset;
518 : }
519 : }
520 35256 : query_tree_walker(qry, OffsetVarNodes_walker, &context, 0);
521 : }
522 : else
523 35256 : OffsetVarNodes_walker(node, &context);
524 70512 : }
525 :
526 : static Relids
527 691994 : offset_relid_set(Relids relids, int offset)
528 : {
529 691994 : Relids result = NULL;
530 : int rtindex;
531 :
532 691994 : rtindex = -1;
533 794758 : while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
534 102764 : result = bms_add_member(result, rtindex + offset);
535 691994 : return result;
536 : }
537 :
538 : /*
539 : * ChangeVarNodes - adjust Var nodes for a specific change of RT index
540 : *
541 : * Find all Var nodes in the given tree belonging to a specific relation
542 : * (identified by sublevels_up and rt_index), and change their varno fields
543 : * to 'new_index'. The varnosyn fields are changed too. Also, adjust other
544 : * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
545 : *
546 : * NOTE: although this has the form of a walker, we cheat and modify the
547 : * nodes in-place. The given expression tree should have been copied
548 : * earlier to ensure that no unwanted side-effects occur!
549 : */
550 :
551 : typedef struct
552 : {
553 : int rt_index;
554 : int new_index;
555 : int sublevels_up;
556 : } ChangeVarNodes_context;
557 :
558 : static bool
559 274714 : ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
560 : {
561 274714 : if (node == NULL)
562 102002 : return false;
563 172712 : if (IsA(node, Var))
564 : {
565 46852 : Var *var = (Var *) node;
566 :
567 46852 : if (var->varlevelsup == context->sublevels_up)
568 : {
569 43762 : if (var->varno == context->rt_index)
570 31674 : var->varno = context->new_index;
571 43762 : var->varnullingrels = adjust_relid_set(var->varnullingrels,
572 : context->rt_index,
573 : context->new_index);
574 43762 : if (var->varnosyn == context->rt_index)
575 31674 : var->varnosyn = context->new_index;
576 : }
577 46852 : return false;
578 : }
579 125860 : if (IsA(node, CurrentOfExpr))
580 : {
581 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
582 :
583 0 : if (context->sublevels_up == 0 &&
584 0 : cexpr->cvarno == context->rt_index)
585 0 : cexpr->cvarno = context->new_index;
586 0 : return false;
587 : }
588 125860 : if (IsA(node, RangeTblRef))
589 : {
590 5206 : RangeTblRef *rtr = (RangeTblRef *) node;
591 :
592 5206 : if (context->sublevels_up == 0 &&
593 3112 : rtr->rtindex == context->rt_index)
594 1694 : rtr->rtindex = context->new_index;
595 : /* the subquery itself is visited separately */
596 5206 : return false;
597 : }
598 120654 : if (IsA(node, JoinExpr))
599 : {
600 0 : JoinExpr *j = (JoinExpr *) node;
601 :
602 0 : if (context->sublevels_up == 0 &&
603 0 : j->rtindex == context->rt_index)
604 0 : j->rtindex = context->new_index;
605 : /* fall through to examine children */
606 : }
607 120654 : if (IsA(node, PlaceHolderVar))
608 : {
609 0 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
610 :
611 0 : if (phv->phlevelsup == context->sublevels_up)
612 : {
613 0 : phv->phrels = adjust_relid_set(phv->phrels,
614 : context->rt_index,
615 : context->new_index);
616 0 : phv->phnullingrels = adjust_relid_set(phv->phnullingrels,
617 : context->rt_index,
618 : context->new_index);
619 : }
620 : /* fall through to examine children */
621 : }
622 120654 : if (IsA(node, PlanRowMark))
623 : {
624 0 : PlanRowMark *rowmark = (PlanRowMark *) node;
625 :
626 0 : if (context->sublevels_up == 0)
627 : {
628 0 : if (rowmark->rti == context->rt_index)
629 0 : rowmark->rti = context->new_index;
630 0 : if (rowmark->prti == context->rt_index)
631 0 : rowmark->prti = context->new_index;
632 : }
633 0 : return false;
634 : }
635 120654 : if (IsA(node, AppendRelInfo))
636 : {
637 0 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
638 :
639 0 : if (context->sublevels_up == 0)
640 : {
641 0 : if (appinfo->parent_relid == context->rt_index)
642 0 : appinfo->parent_relid = context->new_index;
643 0 : if (appinfo->child_relid == context->rt_index)
644 0 : appinfo->child_relid = context->new_index;
645 : }
646 : /* fall through to examine children */
647 : }
648 : /* Shouldn't need to handle other planner auxiliary nodes here */
649 : Assert(!IsA(node, SpecialJoinInfo));
650 : Assert(!IsA(node, PlaceHolderInfo));
651 : Assert(!IsA(node, MinMaxAggInfo));
652 :
653 120654 : if (IsA(node, Query))
654 : {
655 : /* Recurse into subselects */
656 : bool result;
657 :
658 2286 : context->sublevels_up++;
659 2286 : result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
660 : context, 0);
661 2286 : context->sublevels_up--;
662 2286 : return result;
663 : }
664 118368 : return expression_tree_walker(node, ChangeVarNodes_walker, context);
665 : }
666 :
667 : void
668 22768 : ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
669 : {
670 : ChangeVarNodes_context context;
671 :
672 22768 : context.rt_index = rt_index;
673 22768 : context.new_index = new_index;
674 22768 : context.sublevels_up = sublevels_up;
675 :
676 : /*
677 : * Must be prepared to start with a Query or a bare expression tree; if
678 : * it's a Query, go straight to query_tree_walker to make sure that
679 : * sublevels_up doesn't get incremented prematurely.
680 : */
681 22768 : if (node && IsA(node, Query))
682 4484 : {
683 4484 : Query *qry = (Query *) node;
684 :
685 : /*
686 : * If we are starting at a Query, and sublevels_up is zero, then we
687 : * must also fix rangetable indexes in the Query itself --- namely
688 : * resultRelation, mergeTargetRelation, exclRelIndex and rowMarks
689 : * entries. sublevels_up cannot be zero when recursing into a
690 : * subquery, so there's no need to have the same logic inside
691 : * ChangeVarNodes_walker.
692 : */
693 4484 : if (sublevels_up == 0)
694 : {
695 : ListCell *l;
696 :
697 4484 : if (qry->resultRelation == rt_index)
698 3110 : qry->resultRelation = new_index;
699 :
700 4484 : if (qry->mergeTargetRelation == rt_index)
701 804 : qry->mergeTargetRelation = new_index;
702 :
703 : /* this is unlikely to ever be used, but ... */
704 4484 : if (qry->onConflict && qry->onConflict->exclRelIndex == rt_index)
705 0 : qry->onConflict->exclRelIndex = new_index;
706 :
707 4496 : foreach(l, qry->rowMarks)
708 : {
709 12 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
710 :
711 12 : if (rc->rti == rt_index)
712 0 : rc->rti = new_index;
713 : }
714 : }
715 4484 : query_tree_walker(qry, ChangeVarNodes_walker, &context, 0);
716 : }
717 : else
718 18284 : ChangeVarNodes_walker(node, &context);
719 22768 : }
720 :
721 : /*
722 : * Substitute newrelid for oldrelid in a Relid set
723 : *
724 : * Note: some extensions may pass a special varno such as INDEX_VAR for
725 : * oldrelid. bms_is_member won't like that, but we should tolerate it.
726 : * (Perhaps newrelid could also be a special varno, but there had better
727 : * not be a reason to inject that into a nullingrels or phrels set.)
728 : */
729 : static Relids
730 43762 : adjust_relid_set(Relids relids, int oldrelid, int newrelid)
731 : {
732 43762 : if (!IS_SPECIAL_VARNO(oldrelid) && bms_is_member(oldrelid, relids))
733 : {
734 : /* Ensure we have a modifiable copy */
735 0 : relids = bms_copy(relids);
736 : /* Remove old, add new */
737 0 : relids = bms_del_member(relids, oldrelid);
738 0 : relids = bms_add_member(relids, newrelid);
739 : }
740 43762 : return relids;
741 : }
742 :
743 : /*
744 : * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
745 : *
746 : * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
747 : * and add delta_sublevels_up to their varlevelsup value. This is needed when
748 : * an expression that's correct for some nesting level is inserted into a
749 : * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
750 : * all Vars are affected. The point of min_sublevels_up is that we can
751 : * increment it when we recurse into a sublink, so that local variables in
752 : * that sublink are not affected, only outer references to vars that belong
753 : * to the expression's original query level or parents thereof.
754 : *
755 : * Likewise for other nodes containing levelsup fields, such as Aggref.
756 : *
757 : * NOTE: although this has the form of a walker, we cheat and modify the
758 : * Var nodes in-place. The given expression tree should have been copied
759 : * earlier to ensure that no unwanted side-effects occur!
760 : */
761 :
762 : typedef struct
763 : {
764 : int delta_sublevels_up;
765 : int min_sublevels_up;
766 : } IncrementVarSublevelsUp_context;
767 :
768 : static bool
769 2220922 : IncrementVarSublevelsUp_walker(Node *node,
770 : IncrementVarSublevelsUp_context *context)
771 : {
772 2220922 : if (node == NULL)
773 652298 : return false;
774 1568624 : if (IsA(node, Var))
775 : {
776 769706 : Var *var = (Var *) node;
777 :
778 769706 : if (var->varlevelsup >= context->min_sublevels_up)
779 9296 : var->varlevelsup += context->delta_sublevels_up;
780 769706 : return false; /* done here */
781 : }
782 798918 : if (IsA(node, CurrentOfExpr))
783 : {
784 : /* this should not happen */
785 0 : if (context->min_sublevels_up == 0)
786 0 : elog(ERROR, "cannot push down CurrentOfExpr");
787 0 : return false;
788 : }
789 798918 : if (IsA(node, Aggref))
790 : {
791 2394 : Aggref *agg = (Aggref *) node;
792 :
793 2394 : if (agg->agglevelsup >= context->min_sublevels_up)
794 70 : agg->agglevelsup += context->delta_sublevels_up;
795 : /* fall through to recurse into argument */
796 : }
797 798918 : if (IsA(node, GroupingFunc))
798 : {
799 64 : GroupingFunc *grp = (GroupingFunc *) node;
800 :
801 64 : if (grp->agglevelsup >= context->min_sublevels_up)
802 64 : grp->agglevelsup += context->delta_sublevels_up;
803 : /* fall through to recurse into argument */
804 : }
805 798918 : if (IsA(node, PlaceHolderVar))
806 : {
807 820 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
808 :
809 820 : if (phv->phlevelsup >= context->min_sublevels_up)
810 488 : phv->phlevelsup += context->delta_sublevels_up;
811 : /* fall through to recurse into argument */
812 : }
813 798918 : if (IsA(node, ReturningExpr))
814 : {
815 72 : ReturningExpr *rexpr = (ReturningExpr *) node;
816 :
817 72 : if (rexpr->retlevelsup >= context->min_sublevels_up)
818 72 : rexpr->retlevelsup += context->delta_sublevels_up;
819 : /* fall through to recurse into argument */
820 : }
821 798918 : if (IsA(node, RangeTblEntry))
822 : {
823 80620 : RangeTblEntry *rte = (RangeTblEntry *) node;
824 :
825 80620 : if (rte->rtekind == RTE_CTE)
826 : {
827 3542 : if (rte->ctelevelsup >= context->min_sublevels_up)
828 3512 : rte->ctelevelsup += context->delta_sublevels_up;
829 : }
830 80620 : return false; /* allow range_table_walker to continue */
831 : }
832 718298 : if (IsA(node, Query))
833 : {
834 : /* Recurse into subselects */
835 : bool result;
836 :
837 9206 : context->min_sublevels_up++;
838 9206 : result = query_tree_walker((Query *) node,
839 : IncrementVarSublevelsUp_walker,
840 : context,
841 : QTW_EXAMINE_RTES_BEFORE);
842 9206 : context->min_sublevels_up--;
843 9206 : return result;
844 : }
845 709092 : return expression_tree_walker(node, IncrementVarSublevelsUp_walker, context);
846 : }
847 :
848 : void
849 74236 : IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
850 : int min_sublevels_up)
851 : {
852 : IncrementVarSublevelsUp_context context;
853 :
854 74236 : context.delta_sublevels_up = delta_sublevels_up;
855 74236 : context.min_sublevels_up = min_sublevels_up;
856 :
857 : /*
858 : * Must be prepared to start with a Query or a bare expression tree; if
859 : * it's a Query, we don't want to increment sublevels_up.
860 : */
861 74236 : query_or_expression_tree_walker(node,
862 : IncrementVarSublevelsUp_walker,
863 : &context,
864 : QTW_EXAMINE_RTES_BEFORE);
865 74236 : }
866 :
867 : /*
868 : * IncrementVarSublevelsUp_rtable -
869 : * Same as IncrementVarSublevelsUp, but to be invoked on a range table.
870 : */
871 : void
872 1530 : IncrementVarSublevelsUp_rtable(List *rtable, int delta_sublevels_up,
873 : int min_sublevels_up)
874 : {
875 : IncrementVarSublevelsUp_context context;
876 :
877 1530 : context.delta_sublevels_up = delta_sublevels_up;
878 1530 : context.min_sublevels_up = min_sublevels_up;
879 :
880 1530 : range_table_walker(rtable,
881 : IncrementVarSublevelsUp_walker,
882 : &context,
883 : QTW_EXAMINE_RTES_BEFORE);
884 1530 : }
885 :
886 : /*
887 : * SetVarReturningType - adjust Var nodes for a specified varreturningtype.
888 : *
889 : * Find all Var nodes referring to the specified result relation in the given
890 : * expression and set their varreturningtype to the specified value.
891 : *
892 : * NOTE: although this has the form of a walker, we cheat and modify the
893 : * Var nodes in-place. The given expression tree should have been copied
894 : * earlier to ensure that no unwanted side-effects occur!
895 : */
896 :
897 : typedef struct
898 : {
899 : int result_relation;
900 : int sublevels_up;
901 : VarReturningType returning_type;
902 : } SetVarReturningType_context;
903 :
904 : static bool
905 1998 : SetVarReturningType_walker(Node *node, SetVarReturningType_context *context)
906 : {
907 1998 : if (node == NULL)
908 576 : return false;
909 1422 : if (IsA(node, Var))
910 : {
911 894 : Var *var = (Var *) node;
912 :
913 894 : if (var->varno == context->result_relation &&
914 834 : var->varlevelsup == context->sublevels_up)
915 834 : var->varreturningtype = context->returning_type;
916 :
917 894 : return false;
918 : }
919 :
920 528 : if (IsA(node, Query))
921 : {
922 : /* Recurse into subselects */
923 : bool result;
924 :
925 48 : context->sublevels_up++;
926 48 : result = query_tree_walker((Query *) node, SetVarReturningType_walker,
927 : context, 0);
928 48 : context->sublevels_up--;
929 48 : return result;
930 : }
931 480 : return expression_tree_walker(node, SetVarReturningType_walker, context);
932 : }
933 :
934 : static void
935 1086 : SetVarReturningType(Node *node, int result_relation, int sublevels_up,
936 : VarReturningType returning_type)
937 : {
938 : SetVarReturningType_context context;
939 :
940 1086 : context.result_relation = result_relation;
941 1086 : context.sublevels_up = sublevels_up;
942 1086 : context.returning_type = returning_type;
943 :
944 : /* Expect to start with an expression */
945 1086 : SetVarReturningType_walker(node, &context);
946 1086 : }
947 :
948 : /*
949 : * rangeTableEntry_used - detect whether an RTE is referenced somewhere
950 : * in var nodes or join or setOp trees of a query or expression.
951 : */
952 :
953 : typedef struct
954 : {
955 : int rt_index;
956 : int sublevels_up;
957 : } rangeTableEntry_used_context;
958 :
959 : static bool
960 3536594 : rangeTableEntry_used_walker(Node *node,
961 : rangeTableEntry_used_context *context)
962 : {
963 3536594 : if (node == NULL)
964 672228 : return false;
965 2864366 : if (IsA(node, Var))
966 : {
967 821496 : Var *var = (Var *) node;
968 :
969 821496 : if (var->varlevelsup == context->sublevels_up &&
970 1304674 : (var->varno == context->rt_index ||
971 514876 : bms_is_member(context->rt_index, var->varnullingrels)))
972 274922 : return true;
973 546574 : return false;
974 : }
975 2042870 : if (IsA(node, CurrentOfExpr))
976 : {
977 12 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
978 :
979 12 : if (context->sublevels_up == 0 &&
980 12 : cexpr->cvarno == context->rt_index)
981 0 : return true;
982 12 : return false;
983 : }
984 2042858 : if (IsA(node, RangeTblRef))
985 : {
986 125370 : RangeTblRef *rtr = (RangeTblRef *) node;
987 :
988 125370 : if (rtr->rtindex == context->rt_index &&
989 67146 : context->sublevels_up == 0)
990 64290 : return true;
991 : /* the subquery itself is visited separately */
992 61080 : return false;
993 : }
994 1917488 : if (IsA(node, JoinExpr))
995 : {
996 42678 : JoinExpr *j = (JoinExpr *) node;
997 :
998 42678 : if (j->rtindex == context->rt_index &&
999 78 : context->sublevels_up == 0)
1000 0 : return true;
1001 : /* fall through to examine children */
1002 : }
1003 : /* Shouldn't need to handle planner auxiliary nodes here */
1004 : Assert(!IsA(node, PlaceHolderVar));
1005 : Assert(!IsA(node, PlanRowMark));
1006 : Assert(!IsA(node, SpecialJoinInfo));
1007 : Assert(!IsA(node, AppendRelInfo));
1008 : Assert(!IsA(node, PlaceHolderInfo));
1009 : Assert(!IsA(node, MinMaxAggInfo));
1010 :
1011 1917488 : if (IsA(node, Query))
1012 : {
1013 : /* Recurse into subselects */
1014 : bool result;
1015 :
1016 13450 : context->sublevels_up++;
1017 13450 : result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
1018 : context, 0);
1019 13450 : context->sublevels_up--;
1020 13450 : return result;
1021 : }
1022 1904038 : return expression_tree_walker(node, rangeTableEntry_used_walker, context);
1023 : }
1024 :
1025 : bool
1026 354556 : rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
1027 : {
1028 : rangeTableEntry_used_context context;
1029 :
1030 354556 : context.rt_index = rt_index;
1031 354556 : context.sublevels_up = sublevels_up;
1032 :
1033 : /*
1034 : * Must be prepared to start with a Query or a bare expression tree; if
1035 : * it's a Query, we don't want to increment sublevels_up.
1036 : */
1037 354556 : return query_or_expression_tree_walker(node,
1038 : rangeTableEntry_used_walker,
1039 : &context,
1040 : 0);
1041 : }
1042 :
1043 :
1044 : /*
1045 : * If the given Query is an INSERT ... SELECT construct, extract and
1046 : * return the sub-Query node that represents the SELECT part. Otherwise
1047 : * return the given Query.
1048 : *
1049 : * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
1050 : * of the link to the SELECT subquery inside parsetree, or NULL if not an
1051 : * INSERT ... SELECT.
1052 : *
1053 : * This is a hack needed because transformations on INSERT ... SELECTs that
1054 : * appear in rule actions should be applied to the source SELECT, not to the
1055 : * INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
1056 : */
1057 : Query *
1058 3476 : getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
1059 : {
1060 : Query *selectquery;
1061 : RangeTblEntry *selectrte;
1062 : RangeTblRef *rtr;
1063 :
1064 3476 : if (subquery_ptr)
1065 1374 : *subquery_ptr = NULL;
1066 :
1067 3476 : if (parsetree == NULL)
1068 0 : return parsetree;
1069 3476 : if (parsetree->commandType != CMD_INSERT)
1070 1512 : return parsetree;
1071 :
1072 : /*
1073 : * Currently, this is ONLY applied to rule-action queries, and so we
1074 : * expect to find the OLD and NEW placeholder entries in the given query.
1075 : * If they're not there, it must be an INSERT/SELECT in which they've been
1076 : * pushed down to the SELECT.
1077 : */
1078 1964 : if (list_length(parsetree->rtable) >= 2 &&
1079 1964 : strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
1080 1788 : "old") == 0 &&
1081 1788 : strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
1082 : "new") == 0)
1083 1788 : return parsetree;
1084 : Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
1085 176 : if (list_length(parsetree->jointree->fromlist) != 1)
1086 0 : elog(ERROR, "expected to find SELECT subquery");
1087 176 : rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
1088 176 : if (!IsA(rtr, RangeTblRef))
1089 0 : elog(ERROR, "expected to find SELECT subquery");
1090 176 : selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
1091 176 : if (!(selectrte->rtekind == RTE_SUBQUERY &&
1092 176 : selectrte->subquery &&
1093 176 : IsA(selectrte->subquery, Query) &&
1094 176 : selectrte->subquery->commandType == CMD_SELECT))
1095 0 : elog(ERROR, "expected to find SELECT subquery");
1096 176 : selectquery = selectrte->subquery;
1097 176 : if (list_length(selectquery->rtable) >= 2 &&
1098 176 : strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
1099 176 : "old") == 0 &&
1100 176 : strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
1101 : "new") == 0)
1102 : {
1103 176 : if (subquery_ptr)
1104 60 : *subquery_ptr = &(selectrte->subquery);
1105 176 : return selectquery;
1106 : }
1107 0 : elog(ERROR, "could not find rule placeholders");
1108 : return NULL; /* not reached */
1109 : }
1110 :
1111 :
1112 : /*
1113 : * Add the given qualifier condition to the query's WHERE clause
1114 : */
1115 : void
1116 3650 : AddQual(Query *parsetree, Node *qual)
1117 : {
1118 : Node *copy;
1119 :
1120 3650 : if (qual == NULL)
1121 1716 : return;
1122 :
1123 1934 : if (parsetree->commandType == CMD_UTILITY)
1124 : {
1125 : /*
1126 : * There's noplace to put the qual on a utility statement.
1127 : *
1128 : * If it's a NOTIFY, silently ignore the qual; this means that the
1129 : * NOTIFY will execute, whether or not there are any qualifying rows.
1130 : * While clearly wrong, this is much more useful than refusing to
1131 : * execute the rule at all, and extra NOTIFY events are harmless for
1132 : * typical uses of NOTIFY.
1133 : *
1134 : * If it isn't a NOTIFY, error out, since unconditional execution of
1135 : * other utility stmts is unlikely to be wanted. (This case is not
1136 : * currently allowed anyway, but keep the test for safety.)
1137 : */
1138 0 : if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
1139 0 : return;
1140 : else
1141 0 : ereport(ERROR,
1142 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1143 : errmsg("conditional utility statements are not implemented")));
1144 : }
1145 :
1146 1934 : if (parsetree->setOperations != NULL)
1147 : {
1148 : /*
1149 : * There's noplace to put the qual on a setop statement, either. (This
1150 : * could be fixed, but right now the planner simply ignores any qual
1151 : * condition on a setop query.)
1152 : */
1153 0 : ereport(ERROR,
1154 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1155 : errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
1156 : }
1157 :
1158 : /* INTERSECT wants the original, but we need to copy - Jan */
1159 1934 : copy = copyObject(qual);
1160 :
1161 1934 : parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
1162 : copy);
1163 :
1164 : /*
1165 : * We had better not have stuck an aggregate into the WHERE clause.
1166 : */
1167 : Assert(!contain_aggs_of_level(copy, 0));
1168 :
1169 : /*
1170 : * Make sure query is marked correctly if added qual has sublinks. Need
1171 : * not search qual when query is already marked.
1172 : */
1173 1934 : if (!parsetree->hasSubLinks)
1174 1892 : parsetree->hasSubLinks = checkExprHasSubLink(copy);
1175 : }
1176 :
1177 :
1178 : /*
1179 : * Invert the given clause and add it to the WHERE qualifications of the
1180 : * given querytree. Inversion means "x IS NOT TRUE", not just "NOT x",
1181 : * else we will do the wrong thing when x evaluates to NULL.
1182 : */
1183 : void
1184 444 : AddInvertedQual(Query *parsetree, Node *qual)
1185 : {
1186 : BooleanTest *invqual;
1187 :
1188 444 : if (qual == NULL)
1189 0 : return;
1190 :
1191 : /* Need not copy input qual, because AddQual will... */
1192 444 : invqual = makeNode(BooleanTest);
1193 444 : invqual->arg = (Expr *) qual;
1194 444 : invqual->booltesttype = IS_NOT_TRUE;
1195 444 : invqual->location = -1;
1196 :
1197 444 : AddQual(parsetree, (Node *) invqual);
1198 : }
1199 :
1200 :
1201 : /*
1202 : * add_nulling_relids() finds Vars and PlaceHolderVars that belong to any
1203 : * of the target_relids, and adds added_relids to their varnullingrels
1204 : * and phnullingrels fields. If target_relids is NULL, all level-zero
1205 : * Vars and PHVs are modified.
1206 : */
1207 : Node *
1208 7406 : add_nulling_relids(Node *node,
1209 : const Bitmapset *target_relids,
1210 : const Bitmapset *added_relids)
1211 : {
1212 : add_nulling_relids_context context;
1213 :
1214 7406 : context.target_relids = target_relids;
1215 7406 : context.added_relids = added_relids;
1216 7406 : context.sublevels_up = 0;
1217 7406 : return query_or_expression_tree_mutator(node,
1218 : add_nulling_relids_mutator,
1219 : &context,
1220 : 0);
1221 : }
1222 :
1223 : static Node *
1224 31792 : add_nulling_relids_mutator(Node *node,
1225 : add_nulling_relids_context *context)
1226 : {
1227 31792 : if (node == NULL)
1228 1092 : return NULL;
1229 30700 : if (IsA(node, Var))
1230 : {
1231 12092 : Var *var = (Var *) node;
1232 :
1233 12092 : if (var->varlevelsup == context->sublevels_up &&
1234 24064 : (context->target_relids == NULL ||
1235 11978 : bms_is_member(var->varno, context->target_relids)))
1236 : {
1237 6846 : Relids newnullingrels = bms_union(var->varnullingrels,
1238 : context->added_relids);
1239 :
1240 : /* Copy the Var ... */
1241 6846 : var = copyObject(var);
1242 : /* ... and replace the copy's varnullingrels field */
1243 6846 : var->varnullingrels = newnullingrels;
1244 6846 : return (Node *) var;
1245 : }
1246 : /* Otherwise fall through to copy the Var normally */
1247 : }
1248 18608 : else if (IsA(node, PlaceHolderVar))
1249 : {
1250 248 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
1251 :
1252 248 : if (phv->phlevelsup == context->sublevels_up &&
1253 496 : (context->target_relids == NULL ||
1254 248 : bms_overlap(phv->phrels, context->target_relids)))
1255 : {
1256 248 : Relids newnullingrels = bms_union(phv->phnullingrels,
1257 : context->added_relids);
1258 :
1259 : /*
1260 : * We don't modify the contents of the PHV's expression, only add
1261 : * to phnullingrels. This corresponds to assuming that the PHV
1262 : * will be evaluated at the same level as before, then perhaps be
1263 : * nulled as it bubbles up. Hence, just flat-copy the node ...
1264 : */
1265 248 : phv = makeNode(PlaceHolderVar);
1266 248 : memcpy(phv, node, sizeof(PlaceHolderVar));
1267 : /* ... and replace the copy's phnullingrels field */
1268 248 : phv->phnullingrels = newnullingrels;
1269 248 : return (Node *) phv;
1270 : }
1271 : /* Otherwise fall through to copy the PlaceHolderVar normally */
1272 : }
1273 18360 : else if (IsA(node, Query))
1274 : {
1275 : /* Recurse into RTE or sublink subquery */
1276 : Query *newnode;
1277 :
1278 48 : context->sublevels_up++;
1279 48 : newnode = query_tree_mutator((Query *) node,
1280 : add_nulling_relids_mutator,
1281 : context,
1282 : 0);
1283 48 : context->sublevels_up--;
1284 48 : return (Node *) newnode;
1285 : }
1286 23558 : return expression_tree_mutator(node, add_nulling_relids_mutator, context);
1287 : }
1288 :
1289 : /*
1290 : * remove_nulling_relids() removes mentions of the specified RT index(es)
1291 : * in Var.varnullingrels and PlaceHolderVar.phnullingrels fields within
1292 : * the given expression, except in nodes belonging to rels listed in
1293 : * except_relids.
1294 : */
1295 : Node *
1296 317270 : remove_nulling_relids(Node *node,
1297 : const Bitmapset *removable_relids,
1298 : const Bitmapset *except_relids)
1299 : {
1300 : remove_nulling_relids_context context;
1301 :
1302 317270 : context.removable_relids = removable_relids;
1303 317270 : context.except_relids = except_relids;
1304 317270 : context.sublevels_up = 0;
1305 317270 : return query_or_expression_tree_mutator(node,
1306 : remove_nulling_relids_mutator,
1307 : &context,
1308 : 0);
1309 : }
1310 :
1311 : static Node *
1312 566968 : remove_nulling_relids_mutator(Node *node,
1313 : remove_nulling_relids_context *context)
1314 : {
1315 566968 : if (node == NULL)
1316 54900 : return NULL;
1317 512068 : if (IsA(node, Var))
1318 : {
1319 358732 : Var *var = (Var *) node;
1320 :
1321 358732 : if (var->varlevelsup == context->sublevels_up &&
1322 711102 : !bms_is_member(var->varno, context->except_relids) &&
1323 355504 : bms_overlap(var->varnullingrels, context->removable_relids))
1324 : {
1325 : /* Copy the Var ... */
1326 15818 : var = copyObject(var);
1327 : /* ... and replace the copy's varnullingrels field */
1328 15818 : var->varnullingrels = bms_difference(var->varnullingrels,
1329 : context->removable_relids);
1330 15818 : return (Node *) var;
1331 : }
1332 : /* Otherwise fall through to copy the Var normally */
1333 : }
1334 153336 : else if (IsA(node, PlaceHolderVar))
1335 : {
1336 1880 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
1337 :
1338 1880 : if (phv->phlevelsup == context->sublevels_up &&
1339 1880 : !bms_overlap(phv->phrels, context->except_relids))
1340 : {
1341 : /*
1342 : * Note: it might seem desirable to remove the PHV altogether if
1343 : * phnullingrels goes to empty. Currently we dare not do that
1344 : * because we use PHVs in some cases to enforce separate identity
1345 : * of subexpressions; see wrap_non_vars usages in prepjointree.c.
1346 : */
1347 : /* Copy the PlaceHolderVar and mutate what's below ... */
1348 : phv = (PlaceHolderVar *)
1349 1880 : expression_tree_mutator(node,
1350 : remove_nulling_relids_mutator,
1351 : context);
1352 : /* ... and replace the copy's phnullingrels field */
1353 1880 : phv->phnullingrels = bms_difference(phv->phnullingrels,
1354 : context->removable_relids);
1355 : /* We must also update phrels, if it contains a removable RTI */
1356 1880 : phv->phrels = bms_difference(phv->phrels,
1357 : context->removable_relids);
1358 : Assert(!bms_is_empty(phv->phrels));
1359 1880 : return (Node *) phv;
1360 : }
1361 : /* Otherwise fall through to copy the PlaceHolderVar normally */
1362 : }
1363 151456 : else if (IsA(node, Query))
1364 : {
1365 : /* Recurse into RTE or sublink subquery */
1366 : Query *newnode;
1367 :
1368 166 : context->sublevels_up++;
1369 166 : newnode = query_tree_mutator((Query *) node,
1370 : remove_nulling_relids_mutator,
1371 : context,
1372 : 0);
1373 166 : context->sublevels_up--;
1374 166 : return (Node *) newnode;
1375 : }
1376 494204 : return expression_tree_mutator(node, remove_nulling_relids_mutator, context);
1377 : }
1378 :
1379 :
1380 : /*
1381 : * replace_rte_variables() finds all Vars in an expression tree
1382 : * that reference a particular RTE, and replaces them with substitute
1383 : * expressions obtained from a caller-supplied callback function.
1384 : *
1385 : * When invoking replace_rte_variables on a portion of a Query, pass the
1386 : * address of the containing Query's hasSubLinks field as outer_hasSubLinks.
1387 : * Otherwise, pass NULL, but inserting a SubLink into a non-Query expression
1388 : * will then cause an error.
1389 : *
1390 : * Note: the business with inserted_sublink is needed to update hasSubLinks
1391 : * in subqueries when the replacement adds a subquery inside a subquery.
1392 : * Messy, isn't it? We do not need to do similar pushups for hasAggs,
1393 : * because it isn't possible for this transformation to insert a level-zero
1394 : * aggregate reference into a subquery --- it could only insert outer aggs.
1395 : * Likewise for hasWindowFuncs.
1396 : *
1397 : * Note: usually, we'd not expose the mutator function or context struct
1398 : * for a function like this. We do so because callbacks often find it
1399 : * convenient to recurse directly to the mutator on sub-expressions of
1400 : * what they will return.
1401 : */
1402 : Node *
1403 179262 : replace_rte_variables(Node *node, int target_varno, int sublevels_up,
1404 : replace_rte_variables_callback callback,
1405 : void *callback_arg,
1406 : bool *outer_hasSubLinks)
1407 : {
1408 : Node *result;
1409 : replace_rte_variables_context context;
1410 :
1411 179262 : context.callback = callback;
1412 179262 : context.callback_arg = callback_arg;
1413 179262 : context.target_varno = target_varno;
1414 179262 : context.sublevels_up = sublevels_up;
1415 :
1416 : /*
1417 : * We try to initialize inserted_sublink to true if there is no need to
1418 : * detect new sublinks because the query already has some.
1419 : */
1420 179262 : if (node && IsA(node, Query))
1421 4682 : context.inserted_sublink = ((Query *) node)->hasSubLinks;
1422 174580 : else if (outer_hasSubLinks)
1423 174580 : context.inserted_sublink = *outer_hasSubLinks;
1424 : else
1425 0 : context.inserted_sublink = false;
1426 :
1427 : /*
1428 : * Must be prepared to start with a Query or a bare expression tree; if
1429 : * it's a Query, we don't want to increment sublevels_up.
1430 : */
1431 179262 : result = query_or_expression_tree_mutator(node,
1432 : replace_rte_variables_mutator,
1433 : &context,
1434 : 0);
1435 :
1436 179256 : if (context.inserted_sublink)
1437 : {
1438 18580 : if (result && IsA(result, Query))
1439 132 : ((Query *) result)->hasSubLinks = true;
1440 18448 : else if (outer_hasSubLinks)
1441 18448 : *outer_hasSubLinks = true;
1442 : else
1443 0 : elog(ERROR, "replace_rte_variables inserted a SubLink, but has noplace to record it");
1444 : }
1445 :
1446 179256 : return result;
1447 : }
1448 :
1449 : Node *
1450 797854 : replace_rte_variables_mutator(Node *node,
1451 : replace_rte_variables_context *context)
1452 : {
1453 797854 : if (node == NULL)
1454 242384 : return NULL;
1455 555470 : if (IsA(node, Var))
1456 : {
1457 204442 : Var *var = (Var *) node;
1458 :
1459 204442 : if (var->varno == context->target_varno &&
1460 119668 : var->varlevelsup == context->sublevels_up)
1461 : {
1462 : /* Found a matching variable, make the substitution */
1463 : Node *newnode;
1464 :
1465 112152 : newnode = context->callback(var, context);
1466 : /* Detect if we are adding a sublink to query */
1467 112152 : if (!context->inserted_sublink)
1468 103008 : context->inserted_sublink = checkExprHasSubLink(newnode);
1469 112152 : return newnode;
1470 : }
1471 : /* otherwise fall through to copy the var normally */
1472 : }
1473 351028 : else if (IsA(node, CurrentOfExpr))
1474 : {
1475 6 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
1476 :
1477 6 : if (cexpr->cvarno == context->target_varno &&
1478 6 : context->sublevels_up == 0)
1479 : {
1480 : /*
1481 : * We get here if a WHERE CURRENT OF expression turns out to apply
1482 : * to a view. Someday we might be able to translate the
1483 : * expression to apply to an underlying table of the view, but
1484 : * right now it's not implemented.
1485 : */
1486 6 : ereport(ERROR,
1487 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1488 : errmsg("WHERE CURRENT OF on a view is not implemented")));
1489 : }
1490 : /* otherwise fall through to copy the expr normally */
1491 : }
1492 351022 : else if (IsA(node, Query))
1493 : {
1494 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1495 : Query *newnode;
1496 : bool save_inserted_sublink;
1497 :
1498 2626 : context->sublevels_up++;
1499 2626 : save_inserted_sublink = context->inserted_sublink;
1500 2626 : context->inserted_sublink = ((Query *) node)->hasSubLinks;
1501 2626 : newnode = query_tree_mutator((Query *) node,
1502 : replace_rte_variables_mutator,
1503 : context,
1504 : 0);
1505 2626 : newnode->hasSubLinks |= context->inserted_sublink;
1506 2626 : context->inserted_sublink = save_inserted_sublink;
1507 2626 : context->sublevels_up--;
1508 2626 : return (Node *) newnode;
1509 : }
1510 440686 : return expression_tree_mutator(node, replace_rte_variables_mutator, context);
1511 : }
1512 :
1513 :
1514 : /*
1515 : * map_variable_attnos() finds all user-column Vars in an expression tree
1516 : * that reference a particular RTE, and adjusts their varattnos according
1517 : * to the given mapping array (varattno n is replaced by attno_map[n-1]).
1518 : * Vars for system columns are not modified.
1519 : *
1520 : * A zero in the mapping array represents a dropped column, which should not
1521 : * appear in the expression.
1522 : *
1523 : * If the expression tree contains a whole-row Var for the target RTE,
1524 : * *found_whole_row is set to true. In addition, if to_rowtype is
1525 : * not InvalidOid, we replace the Var with a Var of that vartype, inserting
1526 : * a ConvertRowtypeExpr to map back to the rowtype expected by the expression.
1527 : * (Therefore, to_rowtype had better be a child rowtype of the rowtype of the
1528 : * RTE we're changing references to.) Callers that don't provide to_rowtype
1529 : * should report an error if *found_whole_row is true; we don't do that here
1530 : * because we don't know exactly what wording for the error message would
1531 : * be most appropriate. The caller will be aware of the context.
1532 : *
1533 : * This could be built using replace_rte_variables and a callback function,
1534 : * but since we don't ever need to insert sublinks, replace_rte_variables is
1535 : * overly complicated.
1536 : */
1537 :
1538 : typedef struct
1539 : {
1540 : int target_varno; /* RTE index to search for */
1541 : int sublevels_up; /* (current) nesting depth */
1542 : const AttrMap *attno_map; /* map array for user attnos */
1543 : Oid to_rowtype; /* change whole-row Vars to this type */
1544 : bool *found_whole_row; /* output flag */
1545 : } map_variable_attnos_context;
1546 :
1547 : static Node *
1548 104986 : map_variable_attnos_mutator(Node *node,
1549 : map_variable_attnos_context *context)
1550 : {
1551 104986 : if (node == NULL)
1552 154 : return NULL;
1553 104832 : if (IsA(node, Var))
1554 : {
1555 24750 : Var *var = (Var *) node;
1556 :
1557 24750 : if (var->varno == context->target_varno &&
1558 24534 : var->varlevelsup == context->sublevels_up)
1559 : {
1560 : /* Found a matching variable, make the substitution */
1561 24534 : Var *newvar = (Var *) palloc(sizeof(Var));
1562 24534 : int attno = var->varattno;
1563 :
1564 24534 : *newvar = *var; /* initially copy all fields of the Var */
1565 :
1566 24534 : if (attno > 0)
1567 : {
1568 : /* user-defined column, replace attno */
1569 24216 : if (attno > context->attno_map->maplen ||
1570 24216 : context->attno_map->attnums[attno - 1] == 0)
1571 0 : elog(ERROR, "unexpected varattno %d in expression to be mapped",
1572 : attno);
1573 24216 : newvar->varattno = context->attno_map->attnums[attno - 1];
1574 : /* If the syntactic referent is same RTE, fix it too */
1575 24216 : if (newvar->varnosyn == context->target_varno)
1576 24138 : newvar->varattnosyn = newvar->varattno;
1577 : }
1578 318 : else if (attno == 0)
1579 : {
1580 : /* whole-row variable, warn caller */
1581 54 : *(context->found_whole_row) = true;
1582 :
1583 : /* If the caller expects us to convert the Var, do so. */
1584 54 : if (OidIsValid(context->to_rowtype) &&
1585 48 : context->to_rowtype != var->vartype)
1586 : {
1587 : ConvertRowtypeExpr *r;
1588 :
1589 : /* This certainly won't work for a RECORD variable. */
1590 : Assert(var->vartype != RECORDOID);
1591 :
1592 : /* Var itself is changed to the requested type. */
1593 48 : newvar->vartype = context->to_rowtype;
1594 :
1595 : /*
1596 : * Add a conversion node on top to convert back to the
1597 : * original type expected by the expression.
1598 : */
1599 48 : r = makeNode(ConvertRowtypeExpr);
1600 48 : r->arg = (Expr *) newvar;
1601 48 : r->resulttype = var->vartype;
1602 48 : r->convertformat = COERCE_IMPLICIT_CAST;
1603 48 : r->location = -1;
1604 :
1605 48 : return (Node *) r;
1606 : }
1607 : }
1608 24486 : return (Node *) newvar;
1609 : }
1610 : /* otherwise fall through to copy the var normally */
1611 : }
1612 80082 : else if (IsA(node, ConvertRowtypeExpr))
1613 : {
1614 48 : ConvertRowtypeExpr *r = (ConvertRowtypeExpr *) node;
1615 48 : Var *var = (Var *) r->arg;
1616 :
1617 : /*
1618 : * If this is coercing a whole-row Var that we need to convert, then
1619 : * just convert the Var without adding an extra ConvertRowtypeExpr.
1620 : * Effectively we're simplifying var::parenttype::grandparenttype into
1621 : * just var::grandparenttype. This avoids building stacks of CREs if
1622 : * this function is applied repeatedly.
1623 : */
1624 48 : if (IsA(var, Var) &&
1625 36 : var->varno == context->target_varno &&
1626 30 : var->varlevelsup == context->sublevels_up &&
1627 30 : var->varattno == 0 &&
1628 30 : OidIsValid(context->to_rowtype) &&
1629 30 : context->to_rowtype != var->vartype)
1630 : {
1631 : ConvertRowtypeExpr *newnode;
1632 30 : Var *newvar = (Var *) palloc(sizeof(Var));
1633 :
1634 : /* whole-row variable, warn caller */
1635 30 : *(context->found_whole_row) = true;
1636 :
1637 30 : *newvar = *var; /* initially copy all fields of the Var */
1638 :
1639 : /* This certainly won't work for a RECORD variable. */
1640 : Assert(var->vartype != RECORDOID);
1641 :
1642 : /* Var itself is changed to the requested type. */
1643 30 : newvar->vartype = context->to_rowtype;
1644 :
1645 30 : newnode = (ConvertRowtypeExpr *) palloc(sizeof(ConvertRowtypeExpr));
1646 30 : *newnode = *r; /* initially copy all fields of the CRE */
1647 30 : newnode->arg = (Expr *) newvar;
1648 :
1649 30 : return (Node *) newnode;
1650 : }
1651 : /* otherwise fall through to process the expression normally */
1652 : }
1653 80034 : else if (IsA(node, Query))
1654 : {
1655 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1656 : Query *newnode;
1657 :
1658 0 : context->sublevels_up++;
1659 0 : newnode = query_tree_mutator((Query *) node,
1660 : map_variable_attnos_mutator,
1661 : context,
1662 : 0);
1663 0 : context->sublevels_up--;
1664 0 : return (Node *) newnode;
1665 : }
1666 80268 : return expression_tree_mutator(node, map_variable_attnos_mutator, context);
1667 : }
1668 :
1669 : Node *
1670 8632 : map_variable_attnos(Node *node,
1671 : int target_varno, int sublevels_up,
1672 : const AttrMap *attno_map,
1673 : Oid to_rowtype, bool *found_whole_row)
1674 : {
1675 : map_variable_attnos_context context;
1676 :
1677 8632 : context.target_varno = target_varno;
1678 8632 : context.sublevels_up = sublevels_up;
1679 8632 : context.attno_map = attno_map;
1680 8632 : context.to_rowtype = to_rowtype;
1681 8632 : context.found_whole_row = found_whole_row;
1682 :
1683 8632 : *found_whole_row = false;
1684 :
1685 : /*
1686 : * Must be prepared to start with a Query or a bare expression tree; if
1687 : * it's a Query, we don't want to increment sublevels_up.
1688 : */
1689 8632 : return query_or_expression_tree_mutator(node,
1690 : map_variable_attnos_mutator,
1691 : &context,
1692 : 0);
1693 : }
1694 :
1695 :
1696 : /*
1697 : * ReplaceVarsFromTargetList - replace Vars with items from a targetlist
1698 : *
1699 : * Vars matching target_varno and sublevels_up are replaced by the
1700 : * entry with matching resno from targetlist, if there is one.
1701 : *
1702 : * If there is no matching resno for such a Var, the action depends on the
1703 : * nomatch_option:
1704 : * REPLACEVARS_REPORT_ERROR: throw an error
1705 : * REPLACEVARS_CHANGE_VARNO: change Var's varno to nomatch_varno
1706 : * REPLACEVARS_SUBSTITUTE_NULL: replace Var with a NULL Const of same type
1707 : *
1708 : * The caller must also provide target_rte, the RTE describing the target
1709 : * relation. This is needed to handle whole-row Vars referencing the target.
1710 : * We expand such Vars into RowExpr constructs.
1711 : *
1712 : * In addition, for INSERT/UPDATE/DELETE/MERGE queries, the caller must
1713 : * provide result_relation, the index of the result relation in the rewritten
1714 : * query. This is needed to handle OLD/NEW RETURNING list Vars referencing
1715 : * target_varno. When such Vars are expanded, their varreturningtype is
1716 : * copied onto any replacement Vars referencing result_relation. In addition,
1717 : * if the replacement expression from the targetlist is not simply a Var
1718 : * referencing result_relation, it is wrapped in a ReturningExpr node (causing
1719 : * the executor to return NULL if the OLD/NEW row doesn't exist).
1720 : *
1721 : * outer_hasSubLinks works the same as for replace_rte_variables().
1722 : */
1723 :
1724 : typedef struct
1725 : {
1726 : RangeTblEntry *target_rte;
1727 : List *targetlist;
1728 : int result_relation;
1729 : ReplaceVarsNoMatchOption nomatch_option;
1730 : int nomatch_varno;
1731 : } ReplaceVarsFromTargetList_context;
1732 :
1733 : static Node *
1734 10854 : ReplaceVarsFromTargetList_callback(Var *var,
1735 : replace_rte_variables_context *context)
1736 : {
1737 10854 : ReplaceVarsFromTargetList_context *rcon = (ReplaceVarsFromTargetList_context *) context->callback_arg;
1738 : TargetEntry *tle;
1739 :
1740 10854 : if (var->varattno == InvalidAttrNumber)
1741 : {
1742 : /* Must expand whole-tuple reference into RowExpr */
1743 : RowExpr *rowexpr;
1744 : List *colnames;
1745 : List *fields;
1746 :
1747 : /*
1748 : * If generating an expansion for a var of a named rowtype (ie, this
1749 : * is a plain relation RTE), then we must include dummy items for
1750 : * dropped columns. If the var is RECORD (ie, this is a JOIN), then
1751 : * omit dropped columns. In the latter case, attach column names to
1752 : * the RowExpr for use of the executor and ruleutils.c.
1753 : *
1754 : * The varreturningtype is copied onto each individual field Var, so
1755 : * that it is handled correctly when we recurse.
1756 : */
1757 132 : expandRTE(rcon->target_rte,
1758 132 : var->varno, var->varlevelsup, var->varreturningtype,
1759 132 : var->location, (var->vartype != RECORDOID),
1760 : &colnames, &fields);
1761 : /* Adjust the generated per-field Vars... */
1762 132 : fields = (List *) replace_rte_variables_mutator((Node *) fields,
1763 : context);
1764 132 : rowexpr = makeNode(RowExpr);
1765 132 : rowexpr->args = fields;
1766 132 : rowexpr->row_typeid = var->vartype;
1767 132 : rowexpr->row_format = COERCE_IMPLICIT_CAST;
1768 132 : rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
1769 132 : rowexpr->location = var->location;
1770 :
1771 : /* Wrap it in a ReturningExpr, if needed, per comments above */
1772 132 : if (var->varreturningtype != VAR_RETURNING_DEFAULT)
1773 : {
1774 78 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1775 :
1776 78 : rexpr->retlevelsup = var->varlevelsup;
1777 78 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1778 78 : rexpr->retexpr = (Expr *) rowexpr;
1779 :
1780 78 : return (Node *) rexpr;
1781 : }
1782 :
1783 54 : return (Node *) rowexpr;
1784 : }
1785 :
1786 : /* Normal case referencing one targetlist element */
1787 10722 : tle = get_tle_by_resno(rcon->targetlist, var->varattno);
1788 :
1789 10722 : if (tle == NULL || tle->resjunk)
1790 : {
1791 : /* Failed to find column in targetlist */
1792 396 : switch (rcon->nomatch_option)
1793 : {
1794 0 : case REPLACEVARS_REPORT_ERROR:
1795 : /* fall through, throw error below */
1796 0 : break;
1797 :
1798 246 : case REPLACEVARS_CHANGE_VARNO:
1799 246 : var = copyObject(var);
1800 246 : var->varno = rcon->nomatch_varno;
1801 : /* we leave the syntactic referent alone */
1802 246 : return (Node *) var;
1803 :
1804 150 : case REPLACEVARS_SUBSTITUTE_NULL:
1805 :
1806 : /*
1807 : * If Var is of domain type, we should add a CoerceToDomain
1808 : * node, in case there is a NOT NULL domain constraint.
1809 : */
1810 150 : return coerce_to_domain((Node *) makeNullConst(var->vartype,
1811 : var->vartypmod,
1812 : var->varcollid),
1813 : InvalidOid, -1,
1814 : var->vartype,
1815 : COERCION_IMPLICIT,
1816 : COERCE_IMPLICIT_CAST,
1817 : -1,
1818 : false);
1819 : }
1820 0 : elog(ERROR, "could not find replacement targetlist entry for attno %d",
1821 : var->varattno);
1822 : return NULL; /* keep compiler quiet */
1823 : }
1824 : else
1825 : {
1826 : /* Make a copy of the tlist item to return */
1827 10326 : Expr *newnode = copyObject(tle->expr);
1828 :
1829 : /* Must adjust varlevelsup if tlist item is from higher query */
1830 10326 : if (var->varlevelsup > 0)
1831 330 : IncrementVarSublevelsUp((Node *) newnode, var->varlevelsup, 0);
1832 :
1833 : /*
1834 : * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
1835 : * and throw error if so. This case could only happen when expanding
1836 : * an ON UPDATE rule's NEW variable and the referenced tlist item in
1837 : * the original UPDATE command is part of a multiple assignment. There
1838 : * seems no practical way to handle such cases without multiple
1839 : * evaluation of the multiple assignment's sub-select, which would
1840 : * create semantic oddities that users of rules would probably prefer
1841 : * not to cope with. So treat it as an unimplemented feature.
1842 : */
1843 10326 : if (contains_multiexpr_param((Node *) newnode, NULL))
1844 0 : ereport(ERROR,
1845 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1846 : errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
1847 :
1848 : /* Handle any OLD/NEW RETURNING list Vars */
1849 10326 : if (var->varreturningtype != VAR_RETURNING_DEFAULT)
1850 : {
1851 : /*
1852 : * Copy varreturningtype onto any Vars in the tlist item that
1853 : * refer to result_relation (which had better be non-zero).
1854 : */
1855 1086 : if (rcon->result_relation == 0)
1856 0 : elog(ERROR, "variable returning old/new found outside RETURNING list");
1857 :
1858 1086 : SetVarReturningType((Node *) newnode, rcon->result_relation,
1859 1086 : var->varlevelsup, var->varreturningtype);
1860 :
1861 : /* Wrap it in a ReturningExpr, if needed, per comments above */
1862 1086 : if (!IsA(newnode, Var) ||
1863 846 : ((Var *) newnode)->varno != rcon->result_relation ||
1864 786 : ((Var *) newnode)->varlevelsup != var->varlevelsup)
1865 : {
1866 300 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1867 :
1868 300 : rexpr->retlevelsup = var->varlevelsup;
1869 300 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1870 300 : rexpr->retexpr = newnode;
1871 :
1872 300 : newnode = (Expr *) rexpr;
1873 : }
1874 : }
1875 :
1876 10326 : return (Node *) newnode;
1877 : }
1878 : }
1879 :
1880 : Node *
1881 6712 : ReplaceVarsFromTargetList(Node *node,
1882 : int target_varno, int sublevels_up,
1883 : RangeTblEntry *target_rte,
1884 : List *targetlist,
1885 : int result_relation,
1886 : ReplaceVarsNoMatchOption nomatch_option,
1887 : int nomatch_varno,
1888 : bool *outer_hasSubLinks)
1889 : {
1890 : ReplaceVarsFromTargetList_context context;
1891 :
1892 6712 : context.target_rte = target_rte;
1893 6712 : context.targetlist = targetlist;
1894 6712 : context.result_relation = result_relation;
1895 6712 : context.nomatch_option = nomatch_option;
1896 6712 : context.nomatch_varno = nomatch_varno;
1897 :
1898 6712 : return replace_rte_variables(node, target_varno, sublevels_up,
1899 : ReplaceVarsFromTargetList_callback,
1900 : &context,
1901 : outer_hasSubLinks);
1902 : }
|