1 /*-------------------------------------------------------------------------
5 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994, Regents of the University of California
10 * src/backend/rewrite/rewriteManip.c
12 *-------------------------------------------------------------------------
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"
30 } contain_aggs_of_level_context
;
36 } locate_agg_of_level_context
;
41 } locate_windowfunc_context
;
45 const Bitmapset
*target_relids
;
46 const Bitmapset
*added_relids
;
48 } add_nulling_relids_context
;
52 const Bitmapset
*removable_relids
;
53 const Bitmapset
*except_relids
;
55 } remove_nulling_relids_context
;
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
);
74 * contain_aggs_of_level -
75 * Check if an expression contains an aggregate function call of a
76 * specified query level.
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.
85 contain_aggs_of_level(Node
*node
, int levelsup
)
87 contain_aggs_of_level_context context
;
89 context
.sublevels_up
= levelsup
;
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.
95 return query_or_expression_tree_walker(node
,
96 contain_aggs_of_level_walker
,
102 contain_aggs_of_level_walker(Node
*node
,
103 contain_aggs_of_level_context
*context
)
107 if (IsA(node
, Aggref
))
109 if (((Aggref
*) node
)->agglevelsup
== context
->sublevels_up
)
110 return true; /* abort the tree traversal and return true */
111 /* else fall through to examine argument */
113 if (IsA(node
, GroupingFunc
))
115 if (((GroupingFunc
*) node
)->agglevelsup
== context
->sublevels_up
)
117 /* else fall through to examine argument */
119 if (IsA(node
, Query
))
121 /* Recurse into subselects */
124 context
->sublevels_up
++;
125 result
= query_tree_walker((Query
*) node
,
126 contain_aggs_of_level_walker
,
127 (void *) context
, 0);
128 context
->sublevels_up
--;
131 return expression_tree_walker(node
, contain_aggs_of_level_walker
,
136 * locate_agg_of_level -
137 * Find the parse location of any aggregate of the specified query level.
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.)
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.
149 locate_agg_of_level(Node
*node
, int levelsup
)
151 locate_agg_of_level_context context
;
153 context
.agg_location
= -1; /* in case we find nothing */
154 context
.sublevels_up
= levelsup
;
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.
160 (void) query_or_expression_tree_walker(node
,
161 locate_agg_of_level_walker
,
165 return context
.agg_location
;
169 locate_agg_of_level_walker(Node
*node
,
170 locate_agg_of_level_context
*context
)
174 if (IsA(node
, Aggref
))
176 if (((Aggref
*) node
)->agglevelsup
== context
->sublevels_up
&&
177 ((Aggref
*) node
)->location
>= 0)
179 context
->agg_location
= ((Aggref
*) node
)->location
;
180 return true; /* abort the tree traversal and return true */
182 /* else fall through to examine argument */
184 if (IsA(node
, GroupingFunc
))
186 if (((GroupingFunc
*) node
)->agglevelsup
== context
->sublevels_up
&&
187 ((GroupingFunc
*) node
)->location
>= 0)
189 context
->agg_location
= ((GroupingFunc
*) node
)->location
;
190 return true; /* abort the tree traversal and return true */
193 if (IsA(node
, Query
))
195 /* Recurse into subselects */
198 context
->sublevels_up
++;
199 result
= query_tree_walker((Query
*) node
,
200 locate_agg_of_level_walker
,
201 (void *) context
, 0);
202 context
->sublevels_up
--;
205 return expression_tree_walker(node
, locate_agg_of_level_walker
,
210 * contain_windowfuncs -
211 * Check if an expression contains a window function call of the
212 * current query level.
215 contain_windowfuncs(Node
*node
)
218 * Must be prepared to start with a Query or a bare expression tree; if
219 * it's a Query, we don't want to increment sublevels_up.
221 return query_or_expression_tree_walker(node
,
222 contain_windowfuncs_walker
,
228 contain_windowfuncs_walker(Node
*node
, void *context
)
232 if (IsA(node
, WindowFunc
))
233 return true; /* abort the tree traversal and return true */
234 /* Mustn't recurse into subselects */
235 return expression_tree_walker(node
, contain_windowfuncs_walker
,
240 * locate_windowfunc -
241 * Find the parse location of any windowfunc of the current query level.
243 * Returns -1 if no such windowfunc is in the querytree, or if they all have
244 * unknown parse location. (The former case is probably caller error,
245 * but we don't bother to distinguish it from the latter case.)
247 * Note: it might seem appropriate to merge this functionality into
248 * contain_windowfuncs, but that would complicate that function's API.
249 * Currently, the only uses of this function are for error reporting,
250 * and so shaving cycles probably isn't very important.
253 locate_windowfunc(Node
*node
)
255 locate_windowfunc_context context
;
257 context
.win_location
= -1; /* in case we find nothing */
260 * Must be prepared to start with a Query or a bare expression tree; if
261 * it's a Query, we don't want to increment sublevels_up.
263 (void) query_or_expression_tree_walker(node
,
264 locate_windowfunc_walker
,
268 return context
.win_location
;
272 locate_windowfunc_walker(Node
*node
, locate_windowfunc_context
*context
)
276 if (IsA(node
, WindowFunc
))
278 if (((WindowFunc
*) node
)->location
>= 0)
280 context
->win_location
= ((WindowFunc
*) node
)->location
;
281 return true; /* abort the tree traversal and return true */
283 /* else fall through to examine argument */
285 /* Mustn't recurse into subselects */
286 return expression_tree_walker(node
, locate_windowfunc_walker
,
291 * checkExprHasSubLink -
292 * Check if an expression contains a SubLink.
295 checkExprHasSubLink(Node
*node
)
298 * If a Query is passed, examine it --- but we should not recurse into
299 * sub-Queries that are in its rangetable or CTE list.
301 return query_or_expression_tree_walker(node
,
302 checkExprHasSubLink_walker
,
304 QTW_IGNORE_RC_SUBQUERIES
);
308 checkExprHasSubLink_walker(Node
*node
, void *context
)
312 if (IsA(node
, SubLink
))
313 return true; /* abort the tree traversal and return true */
314 return expression_tree_walker(node
, checkExprHasSubLink_walker
, context
);
318 * Check for MULTIEXPR Param within expression tree
320 * We intentionally don't descend into SubLinks: only Params at the current
321 * query level are of interest.
324 contains_multiexpr_param(Node
*node
, void *context
)
328 if (IsA(node
, Param
))
330 if (((Param
*) node
)->paramkind
== PARAM_MULTIEXPR
)
331 return true; /* abort the tree traversal and return true */
334 return expression_tree_walker(node
, contains_multiexpr_param
, context
);
339 * Adds the RTEs of 'src_rtable' into 'dst_rtable'
341 * This also adds the RTEPermissionInfos of 'src_perminfos' (belonging to the
342 * RTEs in 'src_rtable') into *dst_perminfos and also updates perminfoindex of
343 * the RTEs in 'src_rtable' to now point to the perminfos' indexes in
346 * Note that this changes both 'dst_rtable' and 'dst_perminfos' destructively,
347 * so the caller should have better passed safe-to-modify copies.
350 CombineRangeTables(List
**dst_rtable
, List
**dst_perminfos
,
351 List
*src_rtable
, List
*src_perminfos
)
354 int offset
= list_length(*dst_perminfos
);
358 foreach(l
, src_rtable
)
360 RangeTblEntry
*rte
= lfirst_node(RangeTblEntry
, l
);
362 if (rte
->perminfoindex
> 0)
363 rte
->perminfoindex
+= offset
;
367 *dst_perminfos
= list_concat(*dst_perminfos
, src_perminfos
);
368 *dst_rtable
= list_concat(*dst_rtable
, src_rtable
);
372 * OffsetVarNodes - adjust Vars when appending one query's RT to another
374 * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
375 * and increment their varno fields (rangetable indexes) by 'offset'.
376 * The varnosyn fields are adjusted similarly. Also, adjust other nodes
377 * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
379 * NOTE: although this has the form of a walker, we cheat and modify the
380 * nodes in-place. The given expression tree should have been copied
381 * earlier to ensure that no unwanted side-effects occur!
388 } OffsetVarNodes_context
;
391 OffsetVarNodes_walker(Node
*node
, OffsetVarNodes_context
*context
)
397 Var
*var
= (Var
*) node
;
399 if (var
->varlevelsup
== context
->sublevels_up
)
401 var
->varno
+= context
->offset
;
402 var
->varnullingrels
= offset_relid_set(var
->varnullingrels
,
404 if (var
->varnosyn
> 0)
405 var
->varnosyn
+= context
->offset
;
409 if (IsA(node
, CurrentOfExpr
))
411 CurrentOfExpr
*cexpr
= (CurrentOfExpr
*) node
;
413 if (context
->sublevels_up
== 0)
414 cexpr
->cvarno
+= context
->offset
;
417 if (IsA(node
, RangeTblRef
))
419 RangeTblRef
*rtr
= (RangeTblRef
*) node
;
421 if (context
->sublevels_up
== 0)
422 rtr
->rtindex
+= context
->offset
;
423 /* the subquery itself is visited separately */
426 if (IsA(node
, JoinExpr
))
428 JoinExpr
*j
= (JoinExpr
*) node
;
430 if (j
->rtindex
&& context
->sublevels_up
== 0)
431 j
->rtindex
+= context
->offset
;
432 /* fall through to examine children */
434 if (IsA(node
, PlaceHolderVar
))
436 PlaceHolderVar
*phv
= (PlaceHolderVar
*) node
;
438 if (phv
->phlevelsup
== context
->sublevels_up
)
440 phv
->phrels
= offset_relid_set(phv
->phrels
,
442 phv
->phnullingrels
= offset_relid_set(phv
->phnullingrels
,
445 /* fall through to examine children */
447 if (IsA(node
, AppendRelInfo
))
449 AppendRelInfo
*appinfo
= (AppendRelInfo
*) node
;
451 if (context
->sublevels_up
== 0)
453 appinfo
->parent_relid
+= context
->offset
;
454 appinfo
->child_relid
+= context
->offset
;
456 /* fall through to examine children */
458 /* Shouldn't need to handle other planner auxiliary nodes here */
459 Assert(!IsA(node
, PlanRowMark
));
460 Assert(!IsA(node
, SpecialJoinInfo
));
461 Assert(!IsA(node
, PlaceHolderInfo
));
462 Assert(!IsA(node
, MinMaxAggInfo
));
464 if (IsA(node
, Query
))
466 /* Recurse into subselects */
469 context
->sublevels_up
++;
470 result
= query_tree_walker((Query
*) node
, OffsetVarNodes_walker
,
471 (void *) context
, 0);
472 context
->sublevels_up
--;
475 return expression_tree_walker(node
, OffsetVarNodes_walker
,
480 OffsetVarNodes(Node
*node
, int offset
, int sublevels_up
)
482 OffsetVarNodes_context context
;
484 context
.offset
= offset
;
485 context
.sublevels_up
= sublevels_up
;
488 * Must be prepared to start with a Query or a bare expression tree; if
489 * it's a Query, go straight to query_tree_walker to make sure that
490 * sublevels_up doesn't get incremented prematurely.
492 if (node
&& IsA(node
, Query
))
494 Query
*qry
= (Query
*) node
;
497 * If we are starting at a Query, and sublevels_up is zero, then we
498 * must also fix rangetable indexes in the Query itself --- namely
499 * resultRelation, mergeTargetRelation, exclRelIndex and rowMarks
500 * entries. sublevels_up cannot be zero when recursing into a
501 * subquery, so there's no need to have the same logic inside
502 * OffsetVarNodes_walker.
504 if (sublevels_up
== 0)
508 if (qry
->resultRelation
)
509 qry
->resultRelation
+= offset
;
511 if (qry
->mergeTargetRelation
)
512 qry
->mergeTargetRelation
+= offset
;
514 if (qry
->onConflict
&& qry
->onConflict
->exclRelIndex
)
515 qry
->onConflict
->exclRelIndex
+= offset
;
517 foreach(l
, qry
->rowMarks
)
519 RowMarkClause
*rc
= (RowMarkClause
*) lfirst(l
);
524 query_tree_walker(qry
, OffsetVarNodes_walker
,
525 (void *) &context
, 0);
528 OffsetVarNodes_walker(node
, &context
);
532 offset_relid_set(Relids relids
, int offset
)
534 Relids result
= NULL
;
538 while ((rtindex
= bms_next_member(relids
, rtindex
)) >= 0)
539 result
= bms_add_member(result
, rtindex
+ offset
);
544 * ChangeVarNodes - adjust Var nodes for a specific change of RT index
546 * Find all Var nodes in the given tree belonging to a specific relation
547 * (identified by sublevels_up and rt_index), and change their varno fields
548 * to 'new_index'. The varnosyn fields are changed too. Also, adjust other
549 * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
551 * NOTE: although this has the form of a walker, we cheat and modify the
552 * nodes in-place. The given expression tree should have been copied
553 * earlier to ensure that no unwanted side-effects occur!
561 } ChangeVarNodes_context
;
564 ChangeVarNodes_walker(Node
*node
, ChangeVarNodes_context
*context
)
570 Var
*var
= (Var
*) node
;
572 if (var
->varlevelsup
== context
->sublevels_up
)
574 if (var
->varno
== context
->rt_index
)
575 var
->varno
= context
->new_index
;
576 var
->varnullingrels
= adjust_relid_set(var
->varnullingrels
,
579 if (var
->varnosyn
== context
->rt_index
)
580 var
->varnosyn
= context
->new_index
;
584 if (IsA(node
, CurrentOfExpr
))
586 CurrentOfExpr
*cexpr
= (CurrentOfExpr
*) node
;
588 if (context
->sublevels_up
== 0 &&
589 cexpr
->cvarno
== context
->rt_index
)
590 cexpr
->cvarno
= context
->new_index
;
593 if (IsA(node
, RangeTblRef
))
595 RangeTblRef
*rtr
= (RangeTblRef
*) node
;
597 if (context
->sublevels_up
== 0 &&
598 rtr
->rtindex
== context
->rt_index
)
599 rtr
->rtindex
= context
->new_index
;
600 /* the subquery itself is visited separately */
603 if (IsA(node
, JoinExpr
))
605 JoinExpr
*j
= (JoinExpr
*) node
;
607 if (context
->sublevels_up
== 0 &&
608 j
->rtindex
== context
->rt_index
)
609 j
->rtindex
= context
->new_index
;
610 /* fall through to examine children */
612 if (IsA(node
, PlaceHolderVar
))
614 PlaceHolderVar
*phv
= (PlaceHolderVar
*) node
;
616 if (phv
->phlevelsup
== context
->sublevels_up
)
618 phv
->phrels
= adjust_relid_set(phv
->phrels
,
621 phv
->phnullingrels
= adjust_relid_set(phv
->phnullingrels
,
625 /* fall through to examine children */
627 if (IsA(node
, PlanRowMark
))
629 PlanRowMark
*rowmark
= (PlanRowMark
*) node
;
631 if (context
->sublevels_up
== 0)
633 if (rowmark
->rti
== context
->rt_index
)
634 rowmark
->rti
= context
->new_index
;
635 if (rowmark
->prti
== context
->rt_index
)
636 rowmark
->prti
= context
->new_index
;
640 if (IsA(node
, AppendRelInfo
))
642 AppendRelInfo
*appinfo
= (AppendRelInfo
*) node
;
644 if (context
->sublevels_up
== 0)
646 if (appinfo
->parent_relid
== context
->rt_index
)
647 appinfo
->parent_relid
= context
->new_index
;
648 if (appinfo
->child_relid
== context
->rt_index
)
649 appinfo
->child_relid
= context
->new_index
;
651 /* fall through to examine children */
653 /* Shouldn't need to handle other planner auxiliary nodes here */
654 Assert(!IsA(node
, SpecialJoinInfo
));
655 Assert(!IsA(node
, PlaceHolderInfo
));
656 Assert(!IsA(node
, MinMaxAggInfo
));
658 if (IsA(node
, Query
))
660 /* Recurse into subselects */
663 context
->sublevels_up
++;
664 result
= query_tree_walker((Query
*) node
, ChangeVarNodes_walker
,
665 (void *) context
, 0);
666 context
->sublevels_up
--;
669 return expression_tree_walker(node
, ChangeVarNodes_walker
,
674 ChangeVarNodes(Node
*node
, int rt_index
, int new_index
, int sublevels_up
)
676 ChangeVarNodes_context context
;
678 context
.rt_index
= rt_index
;
679 context
.new_index
= new_index
;
680 context
.sublevels_up
= sublevels_up
;
683 * Must be prepared to start with a Query or a bare expression tree; if
684 * it's a Query, go straight to query_tree_walker to make sure that
685 * sublevels_up doesn't get incremented prematurely.
687 if (node
&& IsA(node
, Query
))
689 Query
*qry
= (Query
*) node
;
692 * If we are starting at a Query, and sublevels_up is zero, then we
693 * must also fix rangetable indexes in the Query itself --- namely
694 * resultRelation, mergeTargetRelation, exclRelIndex and rowMarks
695 * entries. sublevels_up cannot be zero when recursing into a
696 * subquery, so there's no need to have the same logic inside
697 * ChangeVarNodes_walker.
699 if (sublevels_up
== 0)
703 if (qry
->resultRelation
== rt_index
)
704 qry
->resultRelation
= new_index
;
706 if (qry
->mergeTargetRelation
== rt_index
)
707 qry
->mergeTargetRelation
= new_index
;
709 /* this is unlikely to ever be used, but ... */
710 if (qry
->onConflict
&& qry
->onConflict
->exclRelIndex
== rt_index
)
711 qry
->onConflict
->exclRelIndex
= new_index
;
713 foreach(l
, qry
->rowMarks
)
715 RowMarkClause
*rc
= (RowMarkClause
*) lfirst(l
);
717 if (rc
->rti
== rt_index
)
721 query_tree_walker(qry
, ChangeVarNodes_walker
,
722 (void *) &context
, 0);
725 ChangeVarNodes_walker(node
, &context
);
729 * Substitute newrelid for oldrelid in a Relid set
731 * Note: some extensions may pass a special varno such as INDEX_VAR for
732 * oldrelid. bms_is_member won't like that, but we should tolerate it.
733 * (Perhaps newrelid could also be a special varno, but there had better
734 * not be a reason to inject that into a nullingrels or phrels set.)
737 adjust_relid_set(Relids relids
, int oldrelid
, int newrelid
)
739 if (!IS_SPECIAL_VARNO(oldrelid
) && bms_is_member(oldrelid
, relids
))
741 /* Ensure we have a modifiable copy */
742 relids
= bms_copy(relids
);
743 /* Remove old, add new */
744 relids
= bms_del_member(relids
, oldrelid
);
745 relids
= bms_add_member(relids
, newrelid
);
751 * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
753 * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
754 * and add delta_sublevels_up to their varlevelsup value. This is needed when
755 * an expression that's correct for some nesting level is inserted into a
756 * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
757 * all Vars are affected. The point of min_sublevels_up is that we can
758 * increment it when we recurse into a sublink, so that local variables in
759 * that sublink are not affected, only outer references to vars that belong
760 * to the expression's original query level or parents thereof.
762 * Likewise for other nodes containing levelsup fields, such as Aggref.
764 * NOTE: although this has the form of a walker, we cheat and modify the
765 * Var nodes in-place. The given expression tree should have been copied
766 * earlier to ensure that no unwanted side-effects occur!
771 int delta_sublevels_up
;
772 int min_sublevels_up
;
773 } IncrementVarSublevelsUp_context
;
776 IncrementVarSublevelsUp_walker(Node
*node
,
777 IncrementVarSublevelsUp_context
*context
)
783 Var
*var
= (Var
*) node
;
785 if (var
->varlevelsup
>= context
->min_sublevels_up
)
786 var
->varlevelsup
+= context
->delta_sublevels_up
;
787 return false; /* done here */
789 if (IsA(node
, CurrentOfExpr
))
791 /* this should not happen */
792 if (context
->min_sublevels_up
== 0)
793 elog(ERROR
, "cannot push down CurrentOfExpr");
796 if (IsA(node
, Aggref
))
798 Aggref
*agg
= (Aggref
*) node
;
800 if (agg
->agglevelsup
>= context
->min_sublevels_up
)
801 agg
->agglevelsup
+= context
->delta_sublevels_up
;
802 /* fall through to recurse into argument */
804 if (IsA(node
, GroupingFunc
))
806 GroupingFunc
*grp
= (GroupingFunc
*) node
;
808 if (grp
->agglevelsup
>= context
->min_sublevels_up
)
809 grp
->agglevelsup
+= context
->delta_sublevels_up
;
810 /* fall through to recurse into argument */
812 if (IsA(node
, PlaceHolderVar
))
814 PlaceHolderVar
*phv
= (PlaceHolderVar
*) node
;
816 if (phv
->phlevelsup
>= context
->min_sublevels_up
)
817 phv
->phlevelsup
+= context
->delta_sublevels_up
;
818 /* fall through to recurse into argument */
820 if (IsA(node
, RangeTblEntry
))
822 RangeTblEntry
*rte
= (RangeTblEntry
*) node
;
824 if (rte
->rtekind
== RTE_CTE
)
826 if (rte
->ctelevelsup
>= context
->min_sublevels_up
)
827 rte
->ctelevelsup
+= context
->delta_sublevels_up
;
829 return false; /* allow range_table_walker to continue */
831 if (IsA(node
, Query
))
833 /* Recurse into subselects */
836 context
->min_sublevels_up
++;
837 result
= query_tree_walker((Query
*) node
,
838 IncrementVarSublevelsUp_walker
,
840 QTW_EXAMINE_RTES_BEFORE
);
841 context
->min_sublevels_up
--;
844 return expression_tree_walker(node
, IncrementVarSublevelsUp_walker
,
849 IncrementVarSublevelsUp(Node
*node
, int delta_sublevels_up
,
850 int min_sublevels_up
)
852 IncrementVarSublevelsUp_context context
;
854 context
.delta_sublevels_up
= delta_sublevels_up
;
855 context
.min_sublevels_up
= min_sublevels_up
;
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.
861 query_or_expression_tree_walker(node
,
862 IncrementVarSublevelsUp_walker
,
864 QTW_EXAMINE_RTES_BEFORE
);
868 * IncrementVarSublevelsUp_rtable -
869 * Same as IncrementVarSublevelsUp, but to be invoked on a range table.
872 IncrementVarSublevelsUp_rtable(List
*rtable
, int delta_sublevels_up
,
873 int min_sublevels_up
)
875 IncrementVarSublevelsUp_context context
;
877 context
.delta_sublevels_up
= delta_sublevels_up
;
878 context
.min_sublevels_up
= min_sublevels_up
;
880 range_table_walker(rtable
,
881 IncrementVarSublevelsUp_walker
,
883 QTW_EXAMINE_RTES_BEFORE
);
888 * rangeTableEntry_used - detect whether an RTE is referenced somewhere
889 * in var nodes or join or setOp trees of a query or expression.
896 } rangeTableEntry_used_context
;
899 rangeTableEntry_used_walker(Node
*node
,
900 rangeTableEntry_used_context
*context
)
906 Var
*var
= (Var
*) node
;
908 if (var
->varlevelsup
== context
->sublevels_up
&&
909 (var
->varno
== context
->rt_index
||
910 bms_is_member(context
->rt_index
, var
->varnullingrels
)))
914 if (IsA(node
, CurrentOfExpr
))
916 CurrentOfExpr
*cexpr
= (CurrentOfExpr
*) node
;
918 if (context
->sublevels_up
== 0 &&
919 cexpr
->cvarno
== context
->rt_index
)
923 if (IsA(node
, RangeTblRef
))
925 RangeTblRef
*rtr
= (RangeTblRef
*) node
;
927 if (rtr
->rtindex
== context
->rt_index
&&
928 context
->sublevels_up
== 0)
930 /* the subquery itself is visited separately */
933 if (IsA(node
, JoinExpr
))
935 JoinExpr
*j
= (JoinExpr
*) node
;
937 if (j
->rtindex
== context
->rt_index
&&
938 context
->sublevels_up
== 0)
940 /* fall through to examine children */
942 /* Shouldn't need to handle planner auxiliary nodes here */
943 Assert(!IsA(node
, PlaceHolderVar
));
944 Assert(!IsA(node
, PlanRowMark
));
945 Assert(!IsA(node
, SpecialJoinInfo
));
946 Assert(!IsA(node
, AppendRelInfo
));
947 Assert(!IsA(node
, PlaceHolderInfo
));
948 Assert(!IsA(node
, MinMaxAggInfo
));
950 if (IsA(node
, Query
))
952 /* Recurse into subselects */
955 context
->sublevels_up
++;
956 result
= query_tree_walker((Query
*) node
, rangeTableEntry_used_walker
,
957 (void *) context
, 0);
958 context
->sublevels_up
--;
961 return expression_tree_walker(node
, rangeTableEntry_used_walker
,
966 rangeTableEntry_used(Node
*node
, int rt_index
, int sublevels_up
)
968 rangeTableEntry_used_context context
;
970 context
.rt_index
= rt_index
;
971 context
.sublevels_up
= sublevels_up
;
974 * Must be prepared to start with a Query or a bare expression tree; if
975 * it's a Query, we don't want to increment sublevels_up.
977 return query_or_expression_tree_walker(node
,
978 rangeTableEntry_used_walker
,
985 * If the given Query is an INSERT ... SELECT construct, extract and
986 * return the sub-Query node that represents the SELECT part. Otherwise
987 * return the given Query.
989 * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
990 * of the link to the SELECT subquery inside parsetree, or NULL if not an
993 * This is a hack needed because transformations on INSERT ... SELECTs that
994 * appear in rule actions should be applied to the source SELECT, not to the
995 * INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
998 getInsertSelectQuery(Query
*parsetree
, Query
***subquery_ptr
)
1001 RangeTblEntry
*selectrte
;
1005 *subquery_ptr
= NULL
;
1007 if (parsetree
== NULL
)
1009 if (parsetree
->commandType
!= CMD_INSERT
)
1013 * Currently, this is ONLY applied to rule-action queries, and so we
1014 * expect to find the OLD and NEW placeholder entries in the given query.
1015 * If they're not there, it must be an INSERT/SELECT in which they've been
1016 * pushed down to the SELECT.
1018 if (list_length(parsetree
->rtable
) >= 2 &&
1019 strcmp(rt_fetch(PRS2_OLD_VARNO
, parsetree
->rtable
)->eref
->aliasname
,
1021 strcmp(rt_fetch(PRS2_NEW_VARNO
, parsetree
->rtable
)->eref
->aliasname
,
1024 Assert(parsetree
->jointree
&& IsA(parsetree
->jointree
, FromExpr
));
1025 if (list_length(parsetree
->jointree
->fromlist
) != 1)
1026 elog(ERROR
, "expected to find SELECT subquery");
1027 rtr
= (RangeTblRef
*) linitial(parsetree
->jointree
->fromlist
);
1028 if (!IsA(rtr
, RangeTblRef
))
1029 elog(ERROR
, "expected to find SELECT subquery");
1030 selectrte
= rt_fetch(rtr
->rtindex
, parsetree
->rtable
);
1031 if (!(selectrte
->rtekind
== RTE_SUBQUERY
&&
1032 selectrte
->subquery
&&
1033 IsA(selectrte
->subquery
, Query
) &&
1034 selectrte
->subquery
->commandType
== CMD_SELECT
))
1035 elog(ERROR
, "expected to find SELECT subquery");
1036 selectquery
= selectrte
->subquery
;
1037 if (list_length(selectquery
->rtable
) >= 2 &&
1038 strcmp(rt_fetch(PRS2_OLD_VARNO
, selectquery
->rtable
)->eref
->aliasname
,
1040 strcmp(rt_fetch(PRS2_NEW_VARNO
, selectquery
->rtable
)->eref
->aliasname
,
1044 *subquery_ptr
= &(selectrte
->subquery
);
1047 elog(ERROR
, "could not find rule placeholders");
1048 return NULL
; /* not reached */
1053 * Add the given qualifier condition to the query's WHERE clause
1056 AddQual(Query
*parsetree
, Node
*qual
)
1063 if (parsetree
->commandType
== CMD_UTILITY
)
1066 * There's noplace to put the qual on a utility statement.
1068 * If it's a NOTIFY, silently ignore the qual; this means that the
1069 * NOTIFY will execute, whether or not there are any qualifying rows.
1070 * While clearly wrong, this is much more useful than refusing to
1071 * execute the rule at all, and extra NOTIFY events are harmless for
1072 * typical uses of NOTIFY.
1074 * If it isn't a NOTIFY, error out, since unconditional execution of
1075 * other utility stmts is unlikely to be wanted. (This case is not
1076 * currently allowed anyway, but keep the test for safety.)
1078 if (parsetree
->utilityStmt
&& IsA(parsetree
->utilityStmt
, NotifyStmt
))
1082 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1083 errmsg("conditional utility statements are not implemented")));
1086 if (parsetree
->setOperations
!= NULL
)
1089 * There's noplace to put the qual on a setop statement, either. (This
1090 * could be fixed, but right now the planner simply ignores any qual
1091 * condition on a setop query.)
1094 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1095 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
1098 /* INTERSECT wants the original, but we need to copy - Jan */
1099 copy
= copyObject(qual
);
1101 parsetree
->jointree
->quals
= make_and_qual(parsetree
->jointree
->quals
,
1105 * We had better not have stuck an aggregate into the WHERE clause.
1107 Assert(!contain_aggs_of_level(copy
, 0));
1110 * Make sure query is marked correctly if added qual has sublinks. Need
1111 * not search qual when query is already marked.
1113 if (!parsetree
->hasSubLinks
)
1114 parsetree
->hasSubLinks
= checkExprHasSubLink(copy
);
1119 * Invert the given clause and add it to the WHERE qualifications of the
1120 * given querytree. Inversion means "x IS NOT TRUE", not just "NOT x",
1121 * else we will do the wrong thing when x evaluates to NULL.
1124 AddInvertedQual(Query
*parsetree
, Node
*qual
)
1126 BooleanTest
*invqual
;
1131 /* Need not copy input qual, because AddQual will... */
1132 invqual
= makeNode(BooleanTest
);
1133 invqual
->arg
= (Expr
*) qual
;
1134 invqual
->booltesttype
= IS_NOT_TRUE
;
1135 invqual
->location
= -1;
1137 AddQual(parsetree
, (Node
*) invqual
);
1142 * add_nulling_relids() finds Vars and PlaceHolderVars that belong to any
1143 * of the target_relids, and adds added_relids to their varnullingrels
1144 * and phnullingrels fields.
1147 add_nulling_relids(Node
*node
,
1148 const Bitmapset
*target_relids
,
1149 const Bitmapset
*added_relids
)
1151 add_nulling_relids_context context
;
1153 context
.target_relids
= target_relids
;
1154 context
.added_relids
= added_relids
;
1155 context
.sublevels_up
= 0;
1156 return query_or_expression_tree_mutator(node
,
1157 add_nulling_relids_mutator
,
1163 add_nulling_relids_mutator(Node
*node
,
1164 add_nulling_relids_context
*context
)
1170 Var
*var
= (Var
*) node
;
1172 if (var
->varlevelsup
== context
->sublevels_up
&&
1173 bms_is_member(var
->varno
, context
->target_relids
))
1175 Relids newnullingrels
= bms_union(var
->varnullingrels
,
1176 context
->added_relids
);
1178 /* Copy the Var ... */
1179 var
= copyObject(var
);
1180 /* ... and replace the copy's varnullingrels field */
1181 var
->varnullingrels
= newnullingrels
;
1182 return (Node
*) var
;
1184 /* Otherwise fall through to copy the Var normally */
1186 else if (IsA(node
, PlaceHolderVar
))
1188 PlaceHolderVar
*phv
= (PlaceHolderVar
*) node
;
1190 if (phv
->phlevelsup
== context
->sublevels_up
&&
1191 bms_overlap(phv
->phrels
, context
->target_relids
))
1193 Relids newnullingrels
= bms_union(phv
->phnullingrels
,
1194 context
->added_relids
);
1197 * We don't modify the contents of the PHV's expression, only add
1198 * to phnullingrels. This corresponds to assuming that the PHV
1199 * will be evaluated at the same level as before, then perhaps be
1200 * nulled as it bubbles up. Hence, just flat-copy the node ...
1202 phv
= makeNode(PlaceHolderVar
);
1203 memcpy(phv
, node
, sizeof(PlaceHolderVar
));
1204 /* ... and replace the copy's phnullingrels field */
1205 phv
->phnullingrels
= newnullingrels
;
1206 return (Node
*) phv
;
1208 /* Otherwise fall through to copy the PlaceHolderVar normally */
1210 else if (IsA(node
, Query
))
1212 /* Recurse into RTE or sublink subquery */
1215 context
->sublevels_up
++;
1216 newnode
= query_tree_mutator((Query
*) node
,
1217 add_nulling_relids_mutator
,
1220 context
->sublevels_up
--;
1221 return (Node
*) newnode
;
1223 return expression_tree_mutator(node
, add_nulling_relids_mutator
,
1228 * remove_nulling_relids() removes mentions of the specified RT index(es)
1229 * in Var.varnullingrels and PlaceHolderVar.phnullingrels fields within
1230 * the given expression, except in nodes belonging to rels listed in
1234 remove_nulling_relids(Node
*node
,
1235 const Bitmapset
*removable_relids
,
1236 const Bitmapset
*except_relids
)
1238 remove_nulling_relids_context context
;
1240 context
.removable_relids
= removable_relids
;
1241 context
.except_relids
= except_relids
;
1242 context
.sublevels_up
= 0;
1243 return query_or_expression_tree_mutator(node
,
1244 remove_nulling_relids_mutator
,
1250 remove_nulling_relids_mutator(Node
*node
,
1251 remove_nulling_relids_context
*context
)
1257 Var
*var
= (Var
*) node
;
1259 if (var
->varlevelsup
== context
->sublevels_up
&&
1260 !bms_is_member(var
->varno
, context
->except_relids
) &&
1261 bms_overlap(var
->varnullingrels
, context
->removable_relids
))
1263 /* Copy the Var ... */
1264 var
= copyObject(var
);
1265 /* ... and replace the copy's varnullingrels field */
1266 var
->varnullingrels
= bms_difference(var
->varnullingrels
,
1267 context
->removable_relids
);
1268 return (Node
*) var
;
1270 /* Otherwise fall through to copy the Var normally */
1272 else if (IsA(node
, PlaceHolderVar
))
1274 PlaceHolderVar
*phv
= (PlaceHolderVar
*) node
;
1276 if (phv
->phlevelsup
== context
->sublevels_up
&&
1277 !bms_overlap(phv
->phrels
, context
->except_relids
))
1280 * Note: it might seem desirable to remove the PHV altogether if
1281 * phnullingrels goes to empty. Currently we dare not do that
1282 * because we use PHVs in some cases to enforce separate identity
1283 * of subexpressions; see wrap_non_vars usages in prepjointree.c.
1285 /* Copy the PlaceHolderVar and mutate what's below ... */
1286 phv
= (PlaceHolderVar
*)
1287 expression_tree_mutator(node
,
1288 remove_nulling_relids_mutator
,
1290 /* ... and replace the copy's phnullingrels field */
1291 phv
->phnullingrels
= bms_difference(phv
->phnullingrels
,
1292 context
->removable_relids
);
1293 /* We must also update phrels, if it contains a removable RTI */
1294 phv
->phrels
= bms_difference(phv
->phrels
,
1295 context
->removable_relids
);
1296 Assert(!bms_is_empty(phv
->phrels
));
1297 return (Node
*) phv
;
1299 /* Otherwise fall through to copy the PlaceHolderVar normally */
1301 else if (IsA(node
, Query
))
1303 /* Recurse into RTE or sublink subquery */
1306 context
->sublevels_up
++;
1307 newnode
= query_tree_mutator((Query
*) node
,
1308 remove_nulling_relids_mutator
,
1311 context
->sublevels_up
--;
1312 return (Node
*) newnode
;
1314 return expression_tree_mutator(node
, remove_nulling_relids_mutator
,
1320 * replace_rte_variables() finds all Vars in an expression tree
1321 * that reference a particular RTE, and replaces them with substitute
1322 * expressions obtained from a caller-supplied callback function.
1324 * When invoking replace_rte_variables on a portion of a Query, pass the
1325 * address of the containing Query's hasSubLinks field as outer_hasSubLinks.
1326 * Otherwise, pass NULL, but inserting a SubLink into a non-Query expression
1327 * will then cause an error.
1329 * Note: the business with inserted_sublink is needed to update hasSubLinks
1330 * in subqueries when the replacement adds a subquery inside a subquery.
1331 * Messy, isn't it? We do not need to do similar pushups for hasAggs,
1332 * because it isn't possible for this transformation to insert a level-zero
1333 * aggregate reference into a subquery --- it could only insert outer aggs.
1334 * Likewise for hasWindowFuncs.
1336 * Note: usually, we'd not expose the mutator function or context struct
1337 * for a function like this. We do so because callbacks often find it
1338 * convenient to recurse directly to the mutator on sub-expressions of
1339 * what they will return.
1342 replace_rte_variables(Node
*node
, int target_varno
, int sublevels_up
,
1343 replace_rte_variables_callback callback
,
1345 bool *outer_hasSubLinks
)
1348 replace_rte_variables_context context
;
1350 context
.callback
= callback
;
1351 context
.callback_arg
= callback_arg
;
1352 context
.target_varno
= target_varno
;
1353 context
.sublevels_up
= sublevels_up
;
1356 * We try to initialize inserted_sublink to true if there is no need to
1357 * detect new sublinks because the query already has some.
1359 if (node
&& IsA(node
, Query
))
1360 context
.inserted_sublink
= ((Query
*) node
)->hasSubLinks
;
1361 else if (outer_hasSubLinks
)
1362 context
.inserted_sublink
= *outer_hasSubLinks
;
1364 context
.inserted_sublink
= false;
1367 * Must be prepared to start with a Query or a bare expression tree; if
1368 * it's a Query, we don't want to increment sublevels_up.
1370 result
= query_or_expression_tree_mutator(node
,
1371 replace_rte_variables_mutator
,
1375 if (context
.inserted_sublink
)
1377 if (result
&& IsA(result
, Query
))
1378 ((Query
*) result
)->hasSubLinks
= true;
1379 else if (outer_hasSubLinks
)
1380 *outer_hasSubLinks
= true;
1382 elog(ERROR
, "replace_rte_variables inserted a SubLink, but has noplace to record it");
1389 replace_rte_variables_mutator(Node
*node
,
1390 replace_rte_variables_context
*context
)
1396 Var
*var
= (Var
*) node
;
1398 if (var
->varno
== context
->target_varno
&&
1399 var
->varlevelsup
== context
->sublevels_up
)
1401 /* Found a matching variable, make the substitution */
1404 newnode
= context
->callback(var
, context
);
1405 /* Detect if we are adding a sublink to query */
1406 if (!context
->inserted_sublink
)
1407 context
->inserted_sublink
= checkExprHasSubLink(newnode
);
1410 /* otherwise fall through to copy the var normally */
1412 else if (IsA(node
, CurrentOfExpr
))
1414 CurrentOfExpr
*cexpr
= (CurrentOfExpr
*) node
;
1416 if (cexpr
->cvarno
== context
->target_varno
&&
1417 context
->sublevels_up
== 0)
1420 * We get here if a WHERE CURRENT OF expression turns out to apply
1421 * to a view. Someday we might be able to translate the
1422 * expression to apply to an underlying table of the view, but
1423 * right now it's not implemented.
1426 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1427 errmsg("WHERE CURRENT OF on a view is not implemented")));
1429 /* otherwise fall through to copy the expr normally */
1431 else if (IsA(node
, Query
))
1433 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1435 bool save_inserted_sublink
;
1437 context
->sublevels_up
++;
1438 save_inserted_sublink
= context
->inserted_sublink
;
1439 context
->inserted_sublink
= ((Query
*) node
)->hasSubLinks
;
1440 newnode
= query_tree_mutator((Query
*) node
,
1441 replace_rte_variables_mutator
,
1444 newnode
->hasSubLinks
|= context
->inserted_sublink
;
1445 context
->inserted_sublink
= save_inserted_sublink
;
1446 context
->sublevels_up
--;
1447 return (Node
*) newnode
;
1449 return expression_tree_mutator(node
, replace_rte_variables_mutator
,
1455 * map_variable_attnos() finds all user-column Vars in an expression tree
1456 * that reference a particular RTE, and adjusts their varattnos according
1457 * to the given mapping array (varattno n is replaced by attno_map[n-1]).
1458 * Vars for system columns are not modified.
1460 * A zero in the mapping array represents a dropped column, which should not
1461 * appear in the expression.
1463 * If the expression tree contains a whole-row Var for the target RTE,
1464 * *found_whole_row is set to true. In addition, if to_rowtype is
1465 * not InvalidOid, we replace the Var with a Var of that vartype, inserting
1466 * a ConvertRowtypeExpr to map back to the rowtype expected by the expression.
1467 * (Therefore, to_rowtype had better be a child rowtype of the rowtype of the
1468 * RTE we're changing references to.) Callers that don't provide to_rowtype
1469 * should report an error if *found_whole_row is true; we don't do that here
1470 * because we don't know exactly what wording for the error message would
1471 * be most appropriate. The caller will be aware of the context.
1473 * This could be built using replace_rte_variables and a callback function,
1474 * but since we don't ever need to insert sublinks, replace_rte_variables is
1475 * overly complicated.
1480 int target_varno
; /* RTE index to search for */
1481 int sublevels_up
; /* (current) nesting depth */
1482 const AttrMap
*attno_map
; /* map array for user attnos */
1483 Oid to_rowtype
; /* change whole-row Vars to this type */
1484 bool *found_whole_row
; /* output flag */
1485 } map_variable_attnos_context
;
1488 map_variable_attnos_mutator(Node
*node
,
1489 map_variable_attnos_context
*context
)
1495 Var
*var
= (Var
*) node
;
1497 if (var
->varno
== context
->target_varno
&&
1498 var
->varlevelsup
== context
->sublevels_up
)
1500 /* Found a matching variable, make the substitution */
1501 Var
*newvar
= (Var
*) palloc(sizeof(Var
));
1502 int attno
= var
->varattno
;
1504 *newvar
= *var
; /* initially copy all fields of the Var */
1508 /* user-defined column, replace attno */
1509 if (attno
> context
->attno_map
->maplen
||
1510 context
->attno_map
->attnums
[attno
- 1] == 0)
1511 elog(ERROR
, "unexpected varattno %d in expression to be mapped",
1513 newvar
->varattno
= context
->attno_map
->attnums
[attno
- 1];
1514 /* If the syntactic referent is same RTE, fix it too */
1515 if (newvar
->varnosyn
== context
->target_varno
)
1516 newvar
->varattnosyn
= newvar
->varattno
;
1518 else if (attno
== 0)
1520 /* whole-row variable, warn caller */
1521 *(context
->found_whole_row
) = true;
1523 /* If the caller expects us to convert the Var, do so. */
1524 if (OidIsValid(context
->to_rowtype
) &&
1525 context
->to_rowtype
!= var
->vartype
)
1527 ConvertRowtypeExpr
*r
;
1529 /* This certainly won't work for a RECORD variable. */
1530 Assert(var
->vartype
!= RECORDOID
);
1532 /* Var itself is changed to the requested type. */
1533 newvar
->vartype
= context
->to_rowtype
;
1536 * Add a conversion node on top to convert back to the
1537 * original type expected by the expression.
1539 r
= makeNode(ConvertRowtypeExpr
);
1540 r
->arg
= (Expr
*) newvar
;
1541 r
->resulttype
= var
->vartype
;
1542 r
->convertformat
= COERCE_IMPLICIT_CAST
;
1548 return (Node
*) newvar
;
1550 /* otherwise fall through to copy the var normally */
1552 else if (IsA(node
, ConvertRowtypeExpr
))
1554 ConvertRowtypeExpr
*r
= (ConvertRowtypeExpr
*) node
;
1555 Var
*var
= (Var
*) r
->arg
;
1558 * If this is coercing a whole-row Var that we need to convert, then
1559 * just convert the Var without adding an extra ConvertRowtypeExpr.
1560 * Effectively we're simplifying var::parenttype::grandparenttype into
1561 * just var::grandparenttype. This avoids building stacks of CREs if
1562 * this function is applied repeatedly.
1564 if (IsA(var
, Var
) &&
1565 var
->varno
== context
->target_varno
&&
1566 var
->varlevelsup
== context
->sublevels_up
&&
1567 var
->varattno
== 0 &&
1568 OidIsValid(context
->to_rowtype
) &&
1569 context
->to_rowtype
!= var
->vartype
)
1571 ConvertRowtypeExpr
*newnode
;
1572 Var
*newvar
= (Var
*) palloc(sizeof(Var
));
1574 /* whole-row variable, warn caller */
1575 *(context
->found_whole_row
) = true;
1577 *newvar
= *var
; /* initially copy all fields of the Var */
1579 /* This certainly won't work for a RECORD variable. */
1580 Assert(var
->vartype
!= RECORDOID
);
1582 /* Var itself is changed to the requested type. */
1583 newvar
->vartype
= context
->to_rowtype
;
1585 newnode
= (ConvertRowtypeExpr
*) palloc(sizeof(ConvertRowtypeExpr
));
1586 *newnode
= *r
; /* initially copy all fields of the CRE */
1587 newnode
->arg
= (Expr
*) newvar
;
1589 return (Node
*) newnode
;
1591 /* otherwise fall through to process the expression normally */
1593 else if (IsA(node
, Query
))
1595 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1598 context
->sublevels_up
++;
1599 newnode
= query_tree_mutator((Query
*) node
,
1600 map_variable_attnos_mutator
,
1603 context
->sublevels_up
--;
1604 return (Node
*) newnode
;
1606 return expression_tree_mutator(node
, map_variable_attnos_mutator
,
1611 map_variable_attnos(Node
*node
,
1612 int target_varno
, int sublevels_up
,
1613 const AttrMap
*attno_map
,
1614 Oid to_rowtype
, bool *found_whole_row
)
1616 map_variable_attnos_context context
;
1618 context
.target_varno
= target_varno
;
1619 context
.sublevels_up
= sublevels_up
;
1620 context
.attno_map
= attno_map
;
1621 context
.to_rowtype
= to_rowtype
;
1622 context
.found_whole_row
= found_whole_row
;
1624 *found_whole_row
= false;
1627 * Must be prepared to start with a Query or a bare expression tree; if
1628 * it's a Query, we don't want to increment sublevels_up.
1630 return query_or_expression_tree_mutator(node
,
1631 map_variable_attnos_mutator
,
1638 * ReplaceVarsFromTargetList - replace Vars with items from a targetlist
1640 * Vars matching target_varno and sublevels_up are replaced by the
1641 * entry with matching resno from targetlist, if there is one.
1643 * If there is no matching resno for such a Var, the action depends on the
1645 * REPLACEVARS_REPORT_ERROR: throw an error
1646 * REPLACEVARS_CHANGE_VARNO: change Var's varno to nomatch_varno
1647 * REPLACEVARS_SUBSTITUTE_NULL: replace Var with a NULL Const of same type
1649 * The caller must also provide target_rte, the RTE describing the target
1650 * relation. This is needed to handle whole-row Vars referencing the target.
1651 * We expand such Vars into RowExpr constructs.
1653 * outer_hasSubLinks works the same as for replace_rte_variables().
1658 RangeTblEntry
*target_rte
;
1660 ReplaceVarsNoMatchOption nomatch_option
;
1662 } ReplaceVarsFromTargetList_context
;
1665 ReplaceVarsFromTargetList_callback(Var
*var
,
1666 replace_rte_variables_context
*context
)
1668 ReplaceVarsFromTargetList_context
*rcon
= (ReplaceVarsFromTargetList_context
*) context
->callback_arg
;
1671 if (var
->varattno
== InvalidAttrNumber
)
1673 /* Must expand whole-tuple reference into RowExpr */
1679 * If generating an expansion for a var of a named rowtype (ie, this
1680 * is a plain relation RTE), then we must include dummy items for
1681 * dropped columns. If the var is RECORD (ie, this is a JOIN), then
1682 * omit dropped columns. In the latter case, attach column names to
1683 * the RowExpr for use of the executor and ruleutils.c.
1685 expandRTE(rcon
->target_rte
,
1686 var
->varno
, var
->varlevelsup
, var
->location
,
1687 (var
->vartype
!= RECORDOID
),
1688 &colnames
, &fields
);
1689 /* Adjust the generated per-field Vars... */
1690 fields
= (List
*) replace_rte_variables_mutator((Node
*) fields
,
1692 rowexpr
= makeNode(RowExpr
);
1693 rowexpr
->args
= fields
;
1694 rowexpr
->row_typeid
= var
->vartype
;
1695 rowexpr
->row_format
= COERCE_IMPLICIT_CAST
;
1696 rowexpr
->colnames
= (var
->vartype
== RECORDOID
) ? colnames
: NIL
;
1697 rowexpr
->location
= var
->location
;
1699 return (Node
*) rowexpr
;
1702 /* Normal case referencing one targetlist element */
1703 tle
= get_tle_by_resno(rcon
->targetlist
, var
->varattno
);
1705 if (tle
== NULL
|| tle
->resjunk
)
1707 /* Failed to find column in targetlist */
1708 switch (rcon
->nomatch_option
)
1710 case REPLACEVARS_REPORT_ERROR
:
1711 /* fall through, throw error below */
1714 case REPLACEVARS_CHANGE_VARNO
:
1715 var
= (Var
*) copyObject(var
);
1716 var
->varno
= rcon
->nomatch_varno
;
1717 /* we leave the syntactic referent alone */
1718 return (Node
*) var
;
1720 case REPLACEVARS_SUBSTITUTE_NULL
:
1723 * If Var is of domain type, we should add a CoerceToDomain
1724 * node, in case there is a NOT NULL domain constraint.
1726 return coerce_to_domain((Node
*) makeNullConst(var
->vartype
,
1732 COERCE_IMPLICIT_CAST
,
1736 elog(ERROR
, "could not find replacement targetlist entry for attno %d",
1738 return NULL
; /* keep compiler quiet */
1742 /* Make a copy of the tlist item to return */
1743 Expr
*newnode
= copyObject(tle
->expr
);
1745 /* Must adjust varlevelsup if tlist item is from higher query */
1746 if (var
->varlevelsup
> 0)
1747 IncrementVarSublevelsUp((Node
*) newnode
, var
->varlevelsup
, 0);
1750 * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
1751 * and throw error if so. This case could only happen when expanding
1752 * an ON UPDATE rule's NEW variable and the referenced tlist item in
1753 * the original UPDATE command is part of a multiple assignment. There
1754 * seems no practical way to handle such cases without multiple
1755 * evaluation of the multiple assignment's sub-select, which would
1756 * create semantic oddities that users of rules would probably prefer
1757 * not to cope with. So treat it as an unimplemented feature.
1759 if (contains_multiexpr_param((Node
*) newnode
, NULL
))
1761 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1762 errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
1764 return (Node
*) newnode
;
1769 ReplaceVarsFromTargetList(Node
*node
,
1770 int target_varno
, int sublevels_up
,
1771 RangeTblEntry
*target_rte
,
1773 ReplaceVarsNoMatchOption nomatch_option
,
1775 bool *outer_hasSubLinks
)
1777 ReplaceVarsFromTargetList_context context
;
1779 context
.target_rte
= target_rte
;
1780 context
.targetlist
= targetlist
;
1781 context
.nomatch_option
= nomatch_option
;
1782 context
.nomatch_varno
= nomatch_varno
;
1784 return replace_rte_variables(node
, target_varno
, sublevels_up
,
1785 ReplaceVarsFromTargetList_callback
,