1 /*-------------------------------------------------------------------------
5 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994, Regents of the University of California
10 * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.89 2004/12/31 22:00:46 pgsql Exp $
12 *-------------------------------------------------------------------------
16 #include "catalog/pg_type.h"
17 #include "nodes/makefuncs.h"
18 #include "optimizer/clauses.h"
19 #include "optimizer/tlist.h"
20 #include "parser/parsetree.h"
21 #include "parser/parse_relation.h"
22 #include "rewrite/rewriteManip.h"
23 #include "utils/lsyscache.h"
29 } checkExprHasAggs_context;
31 static bool checkExprHasAggs_walker(Node *node,
32 checkExprHasAggs_context *context);
33 static bool checkExprHasSubLink_walker(Node *node, void *context);
34 static Relids offset_relid_set(Relids relids, int offset);
35 static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
40 * Queries marked hasAggs might not have them any longer after
41 * rewriting. Check it.
43 * The objective of this routine is to detect whether there are aggregates
44 * belonging to the initial query level. Aggregates belonging to subqueries
45 * or outer queries do NOT cause a true result. We must recurse into
46 * subqueries to detect outer-reference aggregates that logically belong to
47 * the initial query level.
50 checkExprHasAggs(Node *node)
52 checkExprHasAggs_context context;
54 context.sublevels_up = 0;
57 * Must be prepared to start with a Query or a bare expression tree;
58 * if it's a Query, we don't want to increment sublevels_up.
60 return query_or_expression_tree_walker(node,
61 checkExprHasAggs_walker,
67 checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context)
71 if (IsA(node, Aggref))
73 if (((Aggref *) node)->agglevelsup == context->sublevels_up)
74 return true; /* abort the tree traversal and return
76 /* else fall through to examine argument */
80 /* Recurse into subselects */
83 context->sublevels_up++;
84 result = query_tree_walker((Query *) node,
85 checkExprHasAggs_walker,
87 context->sublevels_up--;
90 return expression_tree_walker(node, checkExprHasAggs_walker,
95 * checkExprHasSubLink -
96 * Queries marked hasSubLinks might not have them any longer after
97 * rewriting. Check it.
100 checkExprHasSubLink(Node *node)
103 * If a Query is passed, examine it --- but we need not recurse into
106 return query_or_expression_tree_walker(node,
107 checkExprHasSubLink_walker,
109 QTW_IGNORE_RT_SUBQUERIES);
113 checkExprHasSubLink_walker(Node *node, void *context)
117 if (IsA(node, SubLink))
118 return true; /* abort the tree traversal and return
120 return expression_tree_walker(node, checkExprHasSubLink_walker, context);
125 * OffsetVarNodes - adjust Vars when appending one query's RT to another
127 * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
128 * and increment their varno fields (rangetable indexes) by 'offset'.
129 * The varnoold fields are adjusted similarly. Also, adjust other nodes
130 * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
132 * NOTE: although this has the form of a walker, we cheat and modify the
133 * nodes in-place. The given expression tree should have been copied
134 * earlier to ensure that no unwanted side-effects occur!
141 } OffsetVarNodes_context;
144 OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
150 Var *var = (Var *) node;
152 if (var->varlevelsup == context->sublevels_up)
154 var->varno += context->offset;
155 var->varnoold += context->offset;
159 if (IsA(node, RangeTblRef))
161 RangeTblRef *rtr = (RangeTblRef *) node;
163 if (context->sublevels_up == 0)
164 rtr->rtindex += context->offset;
165 /* the subquery itself is visited separately */
168 if (IsA(node, JoinExpr))
170 JoinExpr *j = (JoinExpr *) node;
172 if (context->sublevels_up == 0)
173 j->rtindex += context->offset;
174 /* fall through to examine children */
176 if (IsA(node, InClauseInfo))
178 InClauseInfo *ininfo = (InClauseInfo *) node;
180 if (context->sublevels_up == 0)
182 ininfo->lefthand = offset_relid_set(ininfo->lefthand,
184 ininfo->righthand = offset_relid_set(ininfo->righthand,
187 /* fall through to examine children */
189 if (IsA(node, Query))
191 /* Recurse into subselects */
194 context->sublevels_up++;
195 result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
196 (void *) context, 0);
197 context->sublevels_up--;
200 return expression_tree_walker(node, OffsetVarNodes_walker,
205 OffsetVarNodes(Node *node, int offset, int sublevels_up)
207 OffsetVarNodes_context context;
209 context.offset = offset;
210 context.sublevels_up = sublevels_up;
213 * Must be prepared to start with a Query or a bare expression tree;
214 * if it's a Query, go straight to query_tree_walker to make sure that
215 * sublevels_up doesn't get incremented prematurely.
217 if (node && IsA(node, Query))
219 Query *qry = (Query *) node;
222 * If we are starting at a Query, and sublevels_up is zero, then
223 * we must also fix rangetable indexes in the Query itself ---
224 * namely resultRelation and rowMarks entries. sublevels_up
225 * cannot be zero when recursing into a subquery, so there's no
226 * need to have the same logic inside OffsetVarNodes_walker.
228 if (sublevels_up == 0)
232 if (qry->resultRelation)
233 qry->resultRelation += offset;
234 foreach(l, qry->rowMarks)
235 lfirst_int(l) += offset;
237 query_tree_walker(qry, OffsetVarNodes_walker,
238 (void *) &context, 0);
241 OffsetVarNodes_walker(node, &context);
245 offset_relid_set(Relids relids, int offset)
247 Relids result = NULL;
251 tmprelids = bms_copy(relids);
252 while ((rtindex = bms_first_member(tmprelids)) >= 0)
253 result = bms_add_member(result, rtindex + offset);
259 * ChangeVarNodes - adjust Var nodes for a specific change of RT index
261 * Find all Var nodes in the given tree belonging to a specific relation
262 * (identified by sublevels_up and rt_index), and change their varno fields
263 * to 'new_index'. The varnoold fields are changed too. Also, adjust other
264 * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
266 * NOTE: although this has the form of a walker, we cheat and modify the
267 * nodes in-place. The given expression tree should have been copied
268 * earlier to ensure that no unwanted side-effects occur!
276 } ChangeVarNodes_context;
279 ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
285 Var *var = (Var *) node;
287 if (var->varlevelsup == context->sublevels_up &&
288 var->varno == context->rt_index)
290 var->varno = context->new_index;
291 var->varnoold = context->new_index;
295 if (IsA(node, RangeTblRef))
297 RangeTblRef *rtr = (RangeTblRef *) node;
299 if (context->sublevels_up == 0 &&
300 rtr->rtindex == context->rt_index)
301 rtr->rtindex = context->new_index;
302 /* the subquery itself is visited separately */
305 if (IsA(node, JoinExpr))
307 JoinExpr *j = (JoinExpr *) node;
309 if (context->sublevels_up == 0 &&
310 j->rtindex == context->rt_index)
311 j->rtindex = context->new_index;
312 /* fall through to examine children */
314 if (IsA(node, InClauseInfo))
316 InClauseInfo *ininfo = (InClauseInfo *) node;
318 if (context->sublevels_up == 0)
320 ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
323 ininfo->righthand = adjust_relid_set(ininfo->righthand,
327 /* fall through to examine children */
329 if (IsA(node, Query))
331 /* Recurse into subselects */
334 context->sublevels_up++;
335 result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
336 (void *) context, 0);
337 context->sublevels_up--;
340 return expression_tree_walker(node, ChangeVarNodes_walker,
345 ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
347 ChangeVarNodes_context context;
349 context.rt_index = rt_index;
350 context.new_index = new_index;
351 context.sublevels_up = sublevels_up;
354 * Must be prepared to start with a Query or a bare expression tree;
355 * if it's a Query, go straight to query_tree_walker to make sure that
356 * sublevels_up doesn't get incremented prematurely.
358 if (node && IsA(node, Query))
360 Query *qry = (Query *) node;
363 * If we are starting at a Query, and sublevels_up is zero, then
364 * we must also fix rangetable indexes in the Query itself ---
365 * namely resultRelation and rowMarks entries. sublevels_up
366 * cannot be zero when recursing into a subquery, so there's no
367 * need to have the same logic inside ChangeVarNodes_walker.
369 if (sublevels_up == 0)
373 if (qry->resultRelation == rt_index)
374 qry->resultRelation = new_index;
375 foreach(l, qry->rowMarks)
377 if (lfirst_int(l) == rt_index)
378 lfirst_int(l) = new_index;
381 query_tree_walker(qry, ChangeVarNodes_walker,
382 (void *) &context, 0);
385 ChangeVarNodes_walker(node, &context);
389 * Substitute newrelid for oldrelid in a Relid set
392 adjust_relid_set(Relids relids, int oldrelid, int newrelid)
394 if (bms_is_member(oldrelid, relids))
396 /* Ensure we have a modifiable copy */
397 relids = bms_copy(relids);
398 /* Remove old, add new */
399 relids = bms_del_member(relids, oldrelid);
400 relids = bms_add_member(relids, newrelid);
406 * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
408 * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
409 * and add delta_sublevels_up to their varlevelsup value. This is needed when
410 * an expression that's correct for some nesting level is inserted into a
411 * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
412 * all Vars are affected. The point of min_sublevels_up is that we can
413 * increment it when we recurse into a sublink, so that local variables in
414 * that sublink are not affected, only outer references to vars that belong
415 * to the expression's original query level or parents thereof.
417 * Aggref nodes are adjusted similarly.
419 * NOTE: although this has the form of a walker, we cheat and modify the
420 * Var nodes in-place. The given expression tree should have been copied
421 * earlier to ensure that no unwanted side-effects occur!
426 int delta_sublevels_up;
427 int min_sublevels_up;
428 } IncrementVarSublevelsUp_context;
431 IncrementVarSublevelsUp_walker(Node *node,
432 IncrementVarSublevelsUp_context *context)
438 Var *var = (Var *) node;
440 if (var->varlevelsup >= context->min_sublevels_up)
441 var->varlevelsup += context->delta_sublevels_up;
442 return false; /* done here */
444 if (IsA(node, Aggref))
446 Aggref *agg = (Aggref *) node;
448 if (agg->agglevelsup >= context->min_sublevels_up)
449 agg->agglevelsup += context->delta_sublevels_up;
450 /* fall through to recurse into argument */
452 if (IsA(node, Query))
454 /* Recurse into subselects */
457 context->min_sublevels_up++;
458 result = query_tree_walker((Query *) node,
459 IncrementVarSublevelsUp_walker,
460 (void *) context, 0);
461 context->min_sublevels_up--;
464 return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
469 IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
470 int min_sublevels_up)
472 IncrementVarSublevelsUp_context context;
474 context.delta_sublevels_up = delta_sublevels_up;
475 context.min_sublevels_up = min_sublevels_up;
478 * Must be prepared to start with a Query or a bare expression tree;
479 * if it's a Query, we don't want to increment sublevels_up.
481 query_or_expression_tree_walker(node,
482 IncrementVarSublevelsUp_walker,
489 * rangeTableEntry_used - detect whether an RTE is referenced somewhere
490 * in var nodes or join or setOp trees of a query or expression.
497 } rangeTableEntry_used_context;
500 rangeTableEntry_used_walker(Node *node,
501 rangeTableEntry_used_context *context)
507 Var *var = (Var *) node;
509 if (var->varlevelsup == context->sublevels_up &&
510 var->varno == context->rt_index)
514 if (IsA(node, RangeTblRef))
516 RangeTblRef *rtr = (RangeTblRef *) node;
518 if (rtr->rtindex == context->rt_index &&
519 context->sublevels_up == 0)
521 /* the subquery itself is visited separately */
524 if (IsA(node, JoinExpr))
526 JoinExpr *j = (JoinExpr *) node;
528 if (j->rtindex == context->rt_index &&
529 context->sublevels_up == 0)
531 /* fall through to examine children */
533 if (IsA(node, InClauseInfo))
535 InClauseInfo *ininfo = (InClauseInfo *) node;
537 if (context->sublevels_up == 0 &&
538 (bms_is_member(context->rt_index, ininfo->lefthand) ||
539 bms_is_member(context->rt_index, ininfo->righthand)))
541 /* fall through to examine children */
543 if (IsA(node, Query))
545 /* Recurse into subselects */
548 context->sublevels_up++;
549 result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
550 (void *) context, 0);
551 context->sublevels_up--;
554 return expression_tree_walker(node, rangeTableEntry_used_walker,
559 rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
561 rangeTableEntry_used_context context;
563 context.rt_index = rt_index;
564 context.sublevels_up = sublevels_up;
567 * Must be prepared to start with a Query or a bare expression tree;
568 * if it's a Query, we don't want to increment sublevels_up.
570 return query_or_expression_tree_walker(node,
571 rangeTableEntry_used_walker,
579 * Check if a specific attribute number of a RTE is used
580 * somewhere in the query or expression.
588 } attribute_used_context;
591 attribute_used_walker(Node *node,
592 attribute_used_context *context)
598 Var *var = (Var *) node;
600 if (var->varlevelsup == context->sublevels_up &&
601 var->varno == context->rt_index &&
602 var->varattno == context->attno)
606 if (IsA(node, Query))
608 /* Recurse into subselects */
611 context->sublevels_up++;
612 result = query_tree_walker((Query *) node, attribute_used_walker,
613 (void *) context, 0);
614 context->sublevels_up--;
617 return expression_tree_walker(node, attribute_used_walker,
622 attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
624 attribute_used_context context;
626 context.rt_index = rt_index;
627 context.attno = attno;
628 context.sublevels_up = sublevels_up;
631 * Must be prepared to start with a Query or a bare expression tree;
632 * if it's a Query, we don't want to increment sublevels_up.
634 return query_or_expression_tree_walker(node,
635 attribute_used_walker,
642 * If the given Query is an INSERT ... SELECT construct, extract and
643 * return the sub-Query node that represents the SELECT part. Otherwise
644 * return the given Query.
646 * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
647 * of the link to the SELECT subquery inside parsetree, or NULL if not an
650 * This is a hack needed because transformations on INSERT ... SELECTs that
651 * appear in rule actions should be applied to the source SELECT, not to the
652 * INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
655 getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
658 RangeTblEntry *selectrte;
662 *subquery_ptr = NULL;
664 if (parsetree == NULL)
666 if (parsetree->commandType != CMD_INSERT)
670 * Currently, this is ONLY applied to rule-action queries, and so we
671 * expect to find the *OLD* and *NEW* placeholder entries in the given
672 * query. If they're not there, it must be an INSERT/SELECT in which
673 * they've been pushed down to the SELECT.
675 if (list_length(parsetree->rtable) >= 2 &&
676 strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
678 strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
681 Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
682 if (list_length(parsetree->jointree->fromlist) != 1)
683 elog(ERROR, "expected to find SELECT subquery");
684 rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
685 Assert(IsA(rtr, RangeTblRef));
686 selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
687 selectquery = selectrte->subquery;
688 if (!(selectquery && IsA(selectquery, Query) &&
689 selectquery->commandType == CMD_SELECT))
690 elog(ERROR, "expected to find SELECT subquery");
691 if (list_length(selectquery->rtable) >= 2 &&
692 strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
694 strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
698 *subquery_ptr = &(selectrte->subquery);
701 elog(ERROR, "could not find rule placeholders");
702 return NULL; /* not reached */
707 * Add the given qualifier condition to the query's WHERE clause
710 AddQual(Query *parsetree, Node *qual)
717 if (parsetree->commandType == CMD_UTILITY)
720 * There's noplace to put the qual on a utility statement.
722 * If it's a NOTIFY, silently ignore the qual; this means that the
723 * NOTIFY will execute, whether or not there are any qualifying
724 * rows. While clearly wrong, this is much more useful than
725 * refusing to execute the rule at all, and extra NOTIFY events
726 * are harmless for typical uses of NOTIFY.
728 * If it isn't a NOTIFY, error out, since unconditional execution of
729 * other utility stmts is unlikely to be wanted. (This case is
730 * not currently allowed anyway, but keep the test for safety.)
732 if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
736 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
737 errmsg("conditional utility statements are not implemented")));
740 if (parsetree->setOperations != NULL)
743 * There's noplace to put the qual on a setop statement, either.
744 * (This could be fixed, but right now the planner simply ignores
745 * any qual condition on a setop query.)
748 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
749 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
752 /* INTERSECT want's the original, but we need to copy - Jan */
753 copy = copyObject(qual);
755 parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
759 * Make sure query is marked correctly if added qual has sublinks or
760 * aggregates (not sure it can ever have aggs, but sublinks
761 * definitely). Need not search qual when query is already marked.
763 if (!parsetree->hasAggs)
764 parsetree->hasAggs = checkExprHasAggs(copy);
765 if (!parsetree->hasSubLinks)
766 parsetree->hasSubLinks = checkExprHasSubLink(copy);
770 * Add the given havingQual to the one already contained in the parsetree
771 * just as AddQual does for the normal 'where' qual
774 AddHavingQual(Query *parsetree, Node *havingQual)
778 if (havingQual == NULL)
781 if (parsetree->commandType == CMD_UTILITY)
784 * There's noplace to put the qual on a utility statement.
786 * See comments in AddQual for motivation.
788 if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
792 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
793 errmsg("conditional utility statements are not implemented")));
796 if (parsetree->setOperations != NULL)
799 * There's noplace to put the qual on a setop statement, either.
800 * (This could be fixed, but right now the planner simply ignores
801 * any qual condition on a setop query.)
804 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
805 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
808 /* INTERSECT want's the original, but we need to copy - Jan */
809 copy = copyObject(havingQual);
811 parsetree->havingQual = make_and_qual(parsetree->havingQual,
815 * Make sure query is marked correctly if added qual has sublinks or
816 * aggregates (not sure it can ever have aggs, but sublinks
817 * definitely). Need not search qual when query is already marked.
819 if (!parsetree->hasAggs)
820 parsetree->hasAggs = checkExprHasAggs(copy);
821 if (!parsetree->hasSubLinks)
822 parsetree->hasSubLinks = checkExprHasSubLink(copy);
827 * Invert the given clause and add it to the WHERE qualifications of the
828 * given querytree. Inversion means "x IS NOT TRUE", not just "NOT x",
829 * else we will do the wrong thing when x evaluates to NULL.
832 AddInvertedQual(Query *parsetree, Node *qual)
834 BooleanTest *invqual;
839 /* Need not copy input qual, because AddQual will... */
840 invqual = makeNode(BooleanTest);
841 invqual->arg = (Expr *) qual;
842 invqual->booltesttype = IS_NOT_TRUE;
844 AddQual(parsetree, (Node *) invqual);
849 * ResolveNew - replace Vars with corresponding items from a targetlist
851 * Vars matching target_varno and sublevels_up are replaced by the
852 * entry with matching resno from targetlist, if there is one.
853 * If not, we either change the unmatched Var's varno to update_varno
854 * (when event == CMD_UPDATE) or replace it with a constant NULL.
856 * The caller must also provide target_rtable, the rangetable containing
857 * the target relation (which must be described by the target_varno'th
858 * RTE in that list). This is needed to handle whole-row Vars referencing
859 * the target. We expand such Vars into RowExpr constructs.
861 * Note: the business with inserted_sublink is needed to update hasSubLinks
862 * in subqueries when the replacement adds a subquery inside a subquery.
863 * Messy, isn't it? We do not need to do similar pushups for hasAggs,
864 * because it isn't possible for this transformation to insert a level-zero
865 * aggregate reference into a subquery --- it could only insert outer aggs.
876 bool inserted_sublink;
877 } ResolveNew_context;
880 resolve_one_var(Var *var, ResolveNew_context *context)
884 tle = get_tle_by_resno(context->targetlist, var->varattno);
888 /* Failed to find column in insert/update tlist */
889 if (context->event == CMD_UPDATE)
891 /* For update, just change unmatched var's varno */
892 var = (Var *) copyObject(var);
893 var->varno = context->update_varno;
894 var->varnoold = context->update_varno;
899 /* Otherwise replace unmatched var with a null */
900 return (Node *) makeNullConst(var->vartype);
905 /* Make a copy of the tlist item to return */
906 Node *n = copyObject(tle->expr);
908 /* Adjust varlevelsup if tlist item is from higher query */
909 if (var->varlevelsup > 0)
910 IncrementVarSublevelsUp(n, var->varlevelsup, 0);
911 /* Report it if we are adding a sublink to query */
912 if (!context->inserted_sublink)
913 context->inserted_sublink = checkExprHasSubLink(n);
919 ResolveNew_mutator(Node *node, ResolveNew_context *context)
925 Var *var = (Var *) node;
926 int this_varno = (int) var->varno;
927 int this_varlevelsup = (int) var->varlevelsup;
929 if (this_varno == context->target_varno &&
930 this_varlevelsup == context->sublevels_up)
932 if (var->varattno == InvalidAttrNumber)
934 /* Must expand whole-tuple reference into RowExpr */
939 * If generating an expansion for a var of a named rowtype
940 * (ie, this is a plain relation RTE), then we must
941 * include dummy items for dropped columns. If the var is
942 * RECORD (ie, this is a JOIN), then omit dropped columns.
944 expandRTE(context->target_rtable, this_varno, this_varlevelsup,
945 (var->vartype != RECORDOID),
947 /* Adjust the generated per-field Vars... */
948 fields = (List *) ResolveNew_mutator((Node *) fields,
950 rowexpr = makeNode(RowExpr);
951 rowexpr->args = fields;
952 rowexpr->row_typeid = var->vartype;
953 rowexpr->row_format = COERCE_IMPLICIT_CAST;
955 return (Node *) rowexpr;
958 /* Normal case for scalar variable */
959 return resolve_one_var(var, context);
961 /* otherwise fall through to copy the var normally */
964 if (IsA(node, Query))
966 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
968 bool save_inserted_sublink;
970 context->sublevels_up++;
971 save_inserted_sublink = context->inserted_sublink;
972 context->inserted_sublink = false;
973 newnode = query_tree_mutator((Query *) node,
977 newnode->hasSubLinks |= context->inserted_sublink;
978 context->inserted_sublink = save_inserted_sublink;
979 context->sublevels_up--;
980 return (Node *) newnode;
982 return expression_tree_mutator(node, ResolveNew_mutator,
987 ResolveNew(Node *node, int target_varno, int sublevels_up,
989 List *targetlist, int event, int update_varno)
991 ResolveNew_context context;
993 context.target_varno = target_varno;
994 context.sublevels_up = sublevels_up;
995 context.target_rtable = target_rtable;
996 context.targetlist = targetlist;
997 context.event = event;
998 context.update_varno = update_varno;
999 context.inserted_sublink = false;
1002 * Must be prepared to start with a Query or a bare expression tree;
1003 * if it's a Query, we don't want to increment sublevels_up.
1005 return query_or_expression_tree_mutator(node,