OSDN Git Service

Update copyrights to 2003.
[pg-rex/syncrep.git] / src / backend / rewrite / rewriteManip.c
index 16b31ea..c9f96f5 100644 (file)
  *
  * rewriteManip.c
  *
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.40 1999/08/25 23:21:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.76 2003/08/04 02:40:03 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
+#include "optimizer/tlist.h"
 #include "parser/parsetree.h"
 #include "parser/parse_clause.h"
 #include "rewrite/rewriteManip.h"
-#include "utils/builtins.h"
 #include "utils/lsyscache.h"
 
-static void ResolveNew(RewriteInfo *info, List *targetlist,
-                  Node **node, int sublevels_up);
 
-
-/*
- * OffsetVarnodes -
- */
-void
-OffsetVarNodes(Node *node, int offset, int sublevels_up)
+typedef struct
 {
-       if (node == NULL)
-               return;
+       int                     sublevels_up;
+}      checkExprHasAggs_context;
 
-       switch (nodeTag(node))
-       {
-               case T_TargetEntry:
-                       {
-                               TargetEntry *tle = (TargetEntry *) node;
-
-                               OffsetVarNodes(
-                                                          (Node *) (tle->expr),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref     *aggref = (Aggref *) node;
+static bool checkExprHasAggs_walker(Node *node,
+                                               checkExprHasAggs_context * context);
+static bool checkExprHasSubLink_walker(Node *node, void *context);
+static Relids offset_relid_set(Relids relids, int offset);
+static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
 
-                               OffsetVarNodes(
-                                                          (Node *) (aggref->target),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
 
-               case T_GroupClause:
-                       break;
+/*
+ * checkExprHasAggs -
+ *     Queries marked hasAggs might not have them any longer after
+ *     rewriting. Check it.
+ *
+ * The objective of this routine is to detect whether there are aggregates
+ * belonging to the initial query level.  Aggregates belonging to subqueries
+ * or outer queries do NOT cause a true result.  We must recurse into
+ * subqueries to detect outer-reference aggregates that logically belong to
+ * the initial query level.
+ */
+bool
+checkExprHasAggs(Node *node)
+{
+       checkExprHasAggs_context context;
 
-               case T_Expr:
-                       {
-                               Expr       *exp = (Expr *) node;
+       context.sublevels_up = 0;
 
-                               OffsetVarNodes(
-                                                          (Node *) (exp->args),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, we don't want to increment sublevels_up.
+        */
+       return query_or_expression_tree_walker(node,
+                                                                                  checkExprHasAggs_walker,
+                                                                                  (void *) &context,
+                                                                                  0);
+}
 
-               case T_Iter:
-                       {
-                               Iter       *iter = (Iter *) node;
+static bool
+checkExprHasAggs_walker(Node *node, checkExprHasAggs_context * context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Aggref))
+       {
+               if (((Aggref *) node)->agglevelsup == context->sublevels_up)
+                       return true;            /* abort the tree traversal and return
+                                                                * true */
+               /* else fall through to examine argument */
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node,
+                                                                  checkExprHasAggs_walker,
+                                                                  (void *) context, 0);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, checkExprHasAggs_walker,
+                                                                 (void *) context);
+}
 
-                               OffsetVarNodes(
-                                                          (Node *) (iter->iterexpr),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
+/*
+ * checkExprHasSubLink -
+ *     Queries marked hasSubLinks might not have them any longer after
+ *     rewriting. Check it.
+ */
+bool
+checkExprHasSubLink(Node *node)
+{
+       /*
+        * If a Query is passed, examine it --- but we will not recurse into
+        * sub-Queries.
+        */
+       return query_or_expression_tree_walker(node,
+                                                                                  checkExprHasSubLink_walker,
+                                                                                  NULL,
+                                                                                  QTW_IGNORE_RT_SUBQUERIES);
+}
 
-               case T_ArrayRef:
-                       {
-                               ArrayRef   *ref = (ArrayRef *) node;
-
-                               OffsetVarNodes(
-                                                          (Node *) (ref->refupperindexpr),
-                                                          offset,
-                                                          sublevels_up);
-                               OffsetVarNodes(
-                                                          (Node *) (ref->reflowerindexpr),
-                                                          offset,
-                                                          sublevels_up);
-                               OffsetVarNodes(
-                                                          (Node *) (ref->refexpr),
-                                                          offset,
-                                                          sublevels_up);
-                               OffsetVarNodes(
-                                                          (Node *) (ref->refassgnexpr),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
+static bool
+checkExprHasSubLink_walker(Node *node, void *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, SubLink))
+               return true;                    /* abort the tree traversal and return
+                                                                * true */
+       return expression_tree_walker(node, checkExprHasSubLink_walker, context);
+}
 
-               case T_Var:
-                       {
-                               Var                *var = (Var *) node;
 
-                               if (var->varlevelsup == sublevels_up)
-                               {
-                                       var->varno += offset;
-                                       var->varnoold += offset;
-                               }
-                       }
-                       break;
+/*
+ * OffsetVarNodes - adjust Vars when appending one query's RT to another
+ *
+ * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
+ * and increment their varno fields (rangetable indexes) by 'offset'.
+ * The varnoold fields are adjusted similarly. Also, adjust other nodes
+ * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * nodes in-place.     The given expression tree should have been copied
+ * earlier to ensure that no unwanted side-effects occur!
+ */
 
-               case T_Param:
-                       break;
+typedef struct
+{
+       int                     offset;
+       int                     sublevels_up;
+} OffsetVarNodes_context;
 
-               case T_Const:
-                       break;
+static bool
+OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
 
-               case T_List:
-                       {
-                               List       *l;
+               if (var->varlevelsup == context->sublevels_up)
+               {
+                       var->varno += context->offset;
+                       var->varnoold += context->offset;
+               }
+               return false;
+       }
+       if (IsA(node, RangeTblRef))
+       {
+               RangeTblRef *rtr = (RangeTblRef *) node;
 
-                               foreach(l, (List *) node)
-                                       OffsetVarNodes(
-                                                                  (Node *) lfirst(l),
-                                                                  offset,
-                                                                  sublevels_up);
-                       }
-                       break;
+               if (context->sublevels_up == 0)
+                       rtr->rtindex += context->offset;
+               /* the subquery itself is visited separately */
+               return false;
+       }
+       if (IsA(node, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) node;
 
-               case T_SubLink:
-                       {
-                               SubLink    *sub = (SubLink *) node;
+               if (context->sublevels_up == 0)
+                       j->rtindex += context->offset;
+               /* fall through to examine children */
+       }
+       if (IsA(node, InClauseInfo))
+       {
+               InClauseInfo *ininfo = (InClauseInfo *) node;
 
-                               OffsetVarNodes(
-                                                          (Node *) (sub->lefthand),
-                                                          offset,
-                                                          sublevels_up);
+               if (context->sublevels_up == 0)
+               {
+                       ininfo->lefthand = offset_relid_set(ininfo->lefthand,
+                                                                                               context->offset);
+                       ininfo->righthand = offset_relid_set(ininfo->righthand,
+                                                                                                context->offset);
+               }
+               /* fall through to examine children */
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
+                                                                  (void *) context, 0);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, OffsetVarNodes_walker,
+                                                                 (void *) context);
+}
 
-                               OffsetVarNodes(
-                                                          (Node *) (sub->subselect),
-                                                          offset,
-                                                          sublevels_up + 1);
-                       }
-                       break;
+void
+OffsetVarNodes(Node *node, int offset, int sublevels_up)
+{
+       OffsetVarNodes_context context;
 
-               case T_Query:
-                       {
-                               Query      *qry = (Query *) node;
-
-                               OffsetVarNodes(
-                                                          (Node *) (qry->targetList),
-                                                          offset,
-                                                          sublevels_up);
-
-                               OffsetVarNodes(
-                                                          (Node *) (qry->qual),
-                                                          offset,
-                                                          sublevels_up);
-
-                               OffsetVarNodes(
-                                                          (Node *) (qry->havingQual),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
+       context.offset = offset;
+       context.sublevels_up = sublevels_up;
 
-               case T_CaseExpr:
-                       {
-                               CaseExpr   *exp = (CaseExpr *) node;
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
+       {
+               Query      *qry = (Query *) node;
+               List       *l;
+
+               /*
+                * If we are starting at a Query, and sublevels_up is zero, then
+                * we must also fix rangetable indexes in the Query itself ---
+                * namely resultRelation and rowMarks entries.  sublevels_up
+                * cannot be zero when recursing into a subquery, so there's no
+                * need to have the same logic inside OffsetVarNodes_walker.
+                */
+               if (sublevels_up == 0)
+               {
+                       if (qry->resultRelation)
+                               qry->resultRelation += offset;
+                       foreach(l, qry->rowMarks)
+                               lfirsti(l) += offset;
+               }
+               query_tree_walker(qry, OffsetVarNodes_walker,
+                                                 (void *) &context, 0);
+       }
+       else
+               OffsetVarNodes_walker(node, &context);
+}
 
-                               OffsetVarNodes(
-                                                          (Node *) (exp->args),
-                                                          offset,
-                                                          sublevels_up);
+static Relids
+offset_relid_set(Relids relids, int offset)
+{
+       Relids          result = NULL;
+       Relids          tmprelids;
+       int                     rtindex;
+
+       tmprelids = bms_copy(relids);
+       while ((rtindex = bms_first_member(tmprelids)) >= 0)
+               result = bms_add_member(result, rtindex + offset);
+       bms_free(tmprelids);
+       return result;
+}
 
-                               OffsetVarNodes(
-                                                          (Node *) (exp->defresult),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
+/*
+ * ChangeVarNodes - adjust Var nodes for a specific change of RT index
+ *
+ * Find all Var nodes in the given tree belonging to a specific relation
+ * (identified by sublevels_up and rt_index), and change their varno fields
+ * to 'new_index'.     The varnoold fields are changed too.  Also, adjust other
+ * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * nodes in-place.     The given expression tree should have been copied
+ * earlier to ensure that no unwanted side-effects occur!
+ */
 
-               case T_CaseWhen:
-                       {
-                               CaseWhen   *exp = (CaseWhen *) node;
+typedef struct
+{
+       int                     rt_index;
+       int                     new_index;
+       int                     sublevels_up;
+} ChangeVarNodes_context;
 
-                               OffsetVarNodes(
-                                                          (Node *) (exp->expr),
-                                                          offset,
-                                                          sublevels_up);
+static bool
+ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
 
-                               OffsetVarNodes(
-                                                          (Node *) (exp->result),
-                                                          offset,
-                                                          sublevels_up);
-                       }
-                       break;
+               if (var->varlevelsup == context->sublevels_up &&
+                       var->varno == context->rt_index)
+               {
+                       var->varno = context->new_index;
+                       var->varnoold = context->new_index;
+               }
+               return false;
+       }
+       if (IsA(node, RangeTblRef))
+       {
+               RangeTblRef *rtr = (RangeTblRef *) node;
 
-               default:
-                       elog(NOTICE, "unknown node tag %d in OffsetVarNodes()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
+               if (context->sublevels_up == 0 &&
+                       rtr->rtindex == context->rt_index)
+                       rtr->rtindex = context->new_index;
+               /* the subquery itself is visited separately */
+               return false;
+       }
+       if (IsA(node, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) node;
 
+               if (context->sublevels_up == 0 &&
+                       j->rtindex == context->rt_index)
+                       j->rtindex = context->new_index;
+               /* fall through to examine children */
+       }
+       if (IsA(node, InClauseInfo))
+       {
+               InClauseInfo *ininfo = (InClauseInfo *) node;
 
+               if (context->sublevels_up == 0)
+               {
+                       ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
+                                                                                               context->rt_index,
+                                                                                               context->new_index);
+                       ininfo->righthand = adjust_relid_set(ininfo->righthand,
+                                                                                                context->rt_index,
+                                                                                                context->new_index);
+               }
+               /* fall through to examine children */
        }
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
+                                                                  (void *) context, 0);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, ChangeVarNodes_walker,
+                                                                 (void *) context);
 }
 
-
-/*
- * ChangeVarNodes -
- */
 void
 ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
 {
-       if (node == NULL)
-               return;
+       ChangeVarNodes_context context;
+
+       context.rt_index = rt_index;
+       context.new_index = new_index;
+       context.sublevels_up = sublevels_up;
 
-       switch (nodeTag(node))
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
        {
-               case T_TargetEntry:
+               Query      *qry = (Query *) node;
+               List       *l;
+
+               /*
+                * If we are starting at a Query, and sublevels_up is zero, then
+                * we must also fix rangetable indexes in the Query itself ---
+                * namely resultRelation and rowMarks entries.  sublevels_up
+                * cannot be zero when recursing into a subquery, so there's no
+                * need to have the same logic inside ChangeVarNodes_walker.
+                */
+               if (sublevels_up == 0)
+               {
+                       if (qry->resultRelation == rt_index)
+                               qry->resultRelation = new_index;
+                       foreach(l, qry->rowMarks)
                        {
-                               TargetEntry *tle = (TargetEntry *) node;
-
-                               ChangeVarNodes(
-                                                          (Node *) (tle->expr),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
+                               if (lfirsti(l) == rt_index)
+                                       lfirsti(l) = new_index;
                        }
-                       break;
+               }
+               query_tree_walker(qry, ChangeVarNodes_walker,
+                                                 (void *) &context, 0);
+       }
+       else
+               ChangeVarNodes_walker(node, &context);
+}
 
-               case T_Aggref:
-                       {
-                               Aggref     *aggref = (Aggref *) node;
+/*
+ * Substitute newrelid for oldrelid in a Relid set
+ */
+static Relids
+adjust_relid_set(Relids relids, int oldrelid, int newrelid)
+{
+       if (bms_is_member(oldrelid, relids))
+       {
+               /* Ensure we have a modifiable copy */
+               relids = bms_copy(relids);
+               /* Remove old, add new */
+               relids = bms_del_member(relids, oldrelid);
+               relids = bms_add_member(relids, newrelid);
+       }
+       return relids;
+}
 
-                               ChangeVarNodes(
-                                                          (Node *) (aggref->target),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                       }
-                       break;
+/*
+ * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
+ *
+ * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
+ * and add delta_sublevels_up to their varlevelsup value.  This is needed when
+ * an expression that's correct for some nesting level is inserted into a
+ * subquery.  Ordinarily the initial call has min_sublevels_up == 0 so that
+ * all Vars are affected.  The point of min_sublevels_up is that we can
+ * increment it when we recurse into a sublink, so that local variables in
+ * that sublink are not affected, only outer references to vars that belong
+ * to the expression's original query level or parents thereof.
+ *
+ * Aggref nodes are adjusted similarly.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * Var nodes in-place. The given expression tree should have been copied
+ * earlier to ensure that no unwanted side-effects occur!
+ */
 
-               case T_GroupClause:
-                       break;
+typedef struct
+{
+       int                     delta_sublevels_up;
+       int                     min_sublevels_up;
+} IncrementVarSublevelsUp_context;
 
-               case T_Expr:
-                       {
-                               Expr       *exp = (Expr *) node;
+static bool
+IncrementVarSublevelsUp_walker(Node *node,
+                                                          IncrementVarSublevelsUp_context *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
 
-                               ChangeVarNodes(
-                                                          (Node *) (exp->args),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                       }
-                       break;
+               if (var->varlevelsup >= context->min_sublevels_up)
+                       var->varlevelsup += context->delta_sublevels_up;
+               return false;                   /* done here */
+       }
+       if (IsA(node, Aggref))
+       {
+               Aggref     *agg = (Aggref *) node;
 
-               case T_Iter:
-                       {
-                               Iter       *iter = (Iter *) node;
+               if (agg->agglevelsup >= context->min_sublevels_up)
+                       agg->agglevelsup += context->delta_sublevels_up;
+               /* fall through to recurse into argument */
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->min_sublevels_up++;
+               result = query_tree_walker((Query *) node,
+                                                                  IncrementVarSublevelsUp_walker,
+                                                                  (void *) context, 0);
+               context->min_sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
+                                                                 (void *) context);
+}
 
-                               ChangeVarNodes(
-                                                          (Node *) (iter->iterexpr),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                       }
-                       break;
+void
+IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
+                                               int min_sublevels_up)
+{
+       IncrementVarSublevelsUp_context context;
 
-               case T_ArrayRef:
-                       {
-                               ArrayRef   *ref = (ArrayRef *) node;
-
-                               ChangeVarNodes(
-                                                          (Node *) (ref->refupperindexpr),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                               ChangeVarNodes(
-                                                          (Node *) (ref->reflowerindexpr),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                               ChangeVarNodes(
-                                                          (Node *) (ref->refexpr),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                               ChangeVarNodes(
-                                                          (Node *) (ref->refassgnexpr),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                       }
-                       break;
+       context.delta_sublevels_up = delta_sublevels_up;
+       context.min_sublevels_up = min_sublevels_up;
 
-               case T_Var:
-                       {
-                               Var                *var = (Var *) node;
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, we don't want to increment sublevels_up.
+        */
+       query_or_expression_tree_walker(node,
+                                                                       IncrementVarSublevelsUp_walker,
+                                                                       (void *) &context,
+                                                                       0);
+}
 
-                               if (var->varlevelsup == sublevels_up &&
-                                       var->varno == rt_index)
-                               {
-                                       var->varno = new_index;
-                                       var->varnoold = new_index;
-                               }
-                       }
-                       break;
 
-               case T_Param:
-                       break;
+/*
+ * rangeTableEntry_used - detect whether an RTE is referenced somewhere
+ *     in var nodes or join or setOp trees of a query or expression.
+ */
 
-               case T_Const:
-                       break;
+typedef struct
+{
+       int                     rt_index;
+       int                     sublevels_up;
+} rangeTableEntry_used_context;
 
-               case T_List:
-                       {
-                               List       *l;
-
-                               foreach(l, (List *) node)
-                                       ChangeVarNodes(
-                                                                  (Node *) lfirst(l),
-                                                                  rt_index,
-                                                                  new_index,
-                                                                  sublevels_up);
-                       }
-                       break;
+static bool
+rangeTableEntry_used_walker(Node *node,
+                                                       rangeTableEntry_used_context *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
 
-               case T_SubLink:
-                       {
-                               SubLink    *sub = (SubLink *) node;
-
-                               ChangeVarNodes(
-                                                          (Node *) (sub->lefthand),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-
-                               ChangeVarNodes(
-                                                          (Node *) (sub->subselect),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up + 1);
-                       }
-                       break;
+               if (var->varlevelsup == context->sublevels_up &&
+                       var->varno == context->rt_index)
+                       return true;
+               return false;
+       }
+       if (IsA(node, RangeTblRef))
+       {
+               RangeTblRef *rtr = (RangeTblRef *) node;
 
-               case T_Query:
-                       {
-                               Query      *qry = (Query *) node;
-
-                               ChangeVarNodes(
-                                                          (Node *) (qry->targetList),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-
-                               ChangeVarNodes(
-                                                          (Node *) (qry->qual),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-
-                               ChangeVarNodes(
-                                                          (Node *) (qry->havingQual),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                       }
-                       break;
+               if (rtr->rtindex == context->rt_index &&
+                       context->sublevels_up == 0)
+                       return true;
+               /* the subquery itself is visited separately */
+               return false;
+       }
+       if (IsA(node, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) node;
 
-               case T_CaseExpr:
-                       {
-                               CaseExpr   *exp = (CaseExpr *) node;
-
-                               ChangeVarNodes(
-                                                          (Node *) (exp->args),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-
-                               ChangeVarNodes(
-                                                          (Node *) (exp->defresult),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                       }
-                       break;
+               if (j->rtindex == context->rt_index &&
+                       context->sublevels_up == 0)
+                       return true;
+               /* fall through to examine children */
+       }
+       if (IsA(node, InClauseInfo))
+       {
+               InClauseInfo *ininfo = (InClauseInfo *) node;
 
-               case T_CaseWhen:
-                       {
-                               CaseWhen   *exp = (CaseWhen *) node;
-
-                               ChangeVarNodes(
-                                                          (Node *) (exp->expr),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-
-                               ChangeVarNodes(
-                                                          (Node *) (exp->result),
-                                                          rt_index,
-                                                          new_index,
-                                                          sublevels_up);
-                       }
-                       break;
+               if (context->sublevels_up == 0 &&
+                       (bms_is_member(context->rt_index, ininfo->lefthand) ||
+                        bms_is_member(context->rt_index, ininfo->righthand)))
+                       return true;
+               /* fall through to examine children */
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
+                                                                  (void *) context, 0);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, rangeTableEntry_used_walker,
+                                                                 (void *) context);
+}
 
-               default:
-                       elog(NOTICE, "unknown node tag %d in ChangeVarNodes()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
+bool
+rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
+{
+       rangeTableEntry_used_context context;
 
+       context.rt_index = rt_index;
+       context.sublevels_up = sublevels_up;
 
-       }
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, we don't want to increment sublevels_up.
+        */
+       return query_or_expression_tree_walker(node,
+                                                                                  rangeTableEntry_used_walker,
+                                                                                  (void *) &context,
+                                                                                  0);
 }
 
 
+/*
+ * attribute_used -
+ *     Check if a specific attribute number of a RTE is used
+ *     somewhere in the query or expression.
+ */
 
-void
-AddQual(Query *parsetree, Node *qual)
+typedef struct
 {
-       Node       *copy,
-                          *old;
-
-       if (qual == NULL)
-               return;
-
-       /* INTERSECT want's the original, but we need to copy - Jan */
-       /* copy = qual; */
-       copy = copyObject(qual);
+       int                     rt_index;
+       int                     attno;
+       int                     sublevels_up;
+} attribute_used_context;
+
+static bool
+attribute_used_walker(Node *node,
+                                         attribute_used_context *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
 
-       old = parsetree->qual;
-       if (old == NULL)
-               parsetree->qual = copy;
-       else
-               parsetree->qual = (Node *) make_andclause(makeList(parsetree->qual, copy, -1));
+               if (var->varlevelsup == context->sublevels_up &&
+                       var->varno == context->rt_index &&
+                       var->varattno == context->attno)
+                       return true;
+               return false;
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, attribute_used_walker,
+                                                                  (void *) context, 0);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, attribute_used_walker,
+                                                                 (void *) context);
 }
 
-/* Adds the given havingQual to the one already contained in the parsetree just as
- * AddQual does for the normal 'where' qual */
-void
-AddHavingQual(Query *parsetree, Node *havingQual)
+bool
+attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
 {
-       Node       *copy,
-                          *old;
+       attribute_used_context context;
 
-       if (havingQual == NULL)
-               return;
-
-       /* INTERSECT want's the original, but we need to copy - Jan */
-       /* copy = havingQual; */
-       copy = copyObject(havingQual);
+       context.rt_index = rt_index;
+       context.attno = attno;
+       context.sublevels_up = sublevels_up;
 
-       old = parsetree->havingQual;
-       if (old == NULL)
-               parsetree->havingQual = copy;
-       else
-               parsetree->havingQual = (Node *) make_andclause(makeList(parsetree->havingQual, copy, -1));
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, we don't want to increment sublevels_up.
+        */
+       return query_or_expression_tree_walker(node,
+                                                                                  attribute_used_walker,
+                                                                                  (void *) &context,
+                                                                                  0);
 }
 
-#ifdef NOT_USED
-void
-AddNotHavingQual(Query *parsetree, Node *havingQual)
+
+/*
+ * If the given Query is an INSERT ... SELECT construct, extract and
+ * return the sub-Query node that represents the SELECT part.  Otherwise
+ * return the given Query.
+ *
+ * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
+ * of the link to the SELECT subquery inside parsetree, or NULL if not an
+ * INSERT ... SELECT.
+ *
+ * This is a hack needed because transformations on INSERT ... SELECTs that
+ * appear in rule actions should be applied to the source SELECT, not to the
+ * INSERT part.  Perhaps this can be cleaned up with redesigned querytrees.
+ */
+Query *
+getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
 {
-       Node       *copy;
+       Query      *selectquery;
+       RangeTblEntry *selectrte;
+       RangeTblRef *rtr;
 
-       if (havingQual == NULL)
-               return;
+       if (subquery_ptr)
+               *subquery_ptr = NULL;
 
-       /* INTERSECT want's the original, but we need to copy - Jan */
-       /* copy = (Node *) make_notclause((Expr *)havingQual); */
-       copy = (Node *) make_notclause((Expr *) copyObject(havingQual));
+       if (parsetree == NULL)
+               return parsetree;
+       if (parsetree->commandType != CMD_INSERT)
+               return parsetree;
 
-       AddHavingQual(parsetree, copy);
+       /*
+        * Currently, this is ONLY applied to rule-action queries, and so we
+        * expect to find the *OLD* and *NEW* placeholder entries in the given
+        * query.  If they're not there, it must be an INSERT/SELECT in which
+        * they've been pushed down to the SELECT.
+        */
+       if (length(parsetree->rtable) >= 2 &&
+        strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
+                       "*OLD*") == 0 &&
+        strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
+                       "*NEW*") == 0)
+               return parsetree;
+       Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
+       if (length(parsetree->jointree->fromlist) != 1)
+               elog(ERROR, "expected to find SELECT subquery");
+       rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);
+       Assert(IsA(rtr, RangeTblRef));
+       selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
+       selectquery = selectrte->subquery;
+       if (!(selectquery && IsA(selectquery, Query) &&
+                 selectquery->commandType == CMD_SELECT))
+               elog(ERROR, "expected to find SELECT subquery");
+       if (length(selectquery->rtable) >= 2 &&
+       strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
+                  "*OLD*") == 0 &&
+       strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
+                  "*NEW*") == 0)
+       {
+               if (subquery_ptr)
+                       *subquery_ptr = &(selectrte->subquery);
+               return selectquery;
+       }
+       elog(ERROR, "could not find rule placeholders");
+       return NULL;                            /* not reached */
 }
-#endif
 
+
+/*
+ * Add the given qualifier condition to the query's WHERE clause
+ */
 void
-AddNotQual(Query *parsetree, Node *qual)
+AddQual(Query *parsetree, Node *qual)
 {
        Node       *copy;
 
        if (qual == NULL)
                return;
 
+       if (parsetree->commandType == CMD_UTILITY)
+       {
+               /*
+                * There's noplace to put the qual on a utility statement.
+                *
+                * If it's a NOTIFY, silently ignore the qual; this means that the
+                * NOTIFY will execute, whether or not there are any qualifying
+                * rows. While clearly wrong, this is much more useful than
+                * refusing to execute the rule at all, and extra NOTIFY events
+                * are harmless for typical uses of NOTIFY.
+                *
+                * If it isn't a NOTIFY, error out, since unconditional execution of
+                * other utility stmts is unlikely to be wanted.  (This case is
+                * not currently allowed anyway, but keep the test for safety.)
+                */
+               if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
+                       return;
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("conditional utility statements are not implemented")));
+       }
+
+       if (parsetree->setOperations != NULL)
+       {
+               /*
+                * There's noplace to put the qual on a setop statement, either.
+                * (This could be fixed, but right now the planner simply ignores
+                * any qual condition on a setop query.)
+                */
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
+       }
+
        /* INTERSECT want's the original, but we need to copy - Jan */
-       /* copy = (Node *) make_notclause((Expr *)qual); */
-       copy = (Node *) make_notclause((Expr *) copyObject(qual));
+       copy = copyObject(qual);
 
-       AddQual(parsetree, copy);
-}
+       parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
+                                                                                          copy);
 
+       /*
+        * Make sure query is marked correctly if added qual has sublinks or
+        * aggregates (not sure it can ever have aggs, but sublinks
+        * definitely).
+        */
+       parsetree->hasAggs |= checkExprHasAggs(copy);
+       parsetree->hasSubLinks |= checkExprHasSubLink(copy);
+}
 
 /*
- * Add all expressions used by the given GroupClause list to the
- * parsetree's targetlist and groupclause list.
- *
- * tlist is the old targetlist associated with the input groupclauses.
- *
- * XXX shouldn't we be checking to see if there are already matching
- * entries in parsetree->targetlist?
+ * Add the given havingQual to the one already contained in the parsetree
+ * just as AddQual does for the normal 'where' qual
  */
 void
-AddGroupClause(Query *parsetree, List *group_by, List *tlist)
+AddHavingQual(Query *parsetree, Node *havingQual)
 {
-       List       *l;
+       Node       *copy;
 
-       foreach(l, group_by)
-       {
-               GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
-               Index           refnumber = groupclause->tleSortGroupRef;
-               TargetEntry *tle = NULL;
-               List       *tl;
+       if (havingQual == NULL)
+               return;
 
-               /* Find and copy the groupclause's TLE in the old tlist */
-               foreach(tl, tlist)
-               {
-                       if (((TargetEntry *) lfirst(tl))->resdom->ressortgroupref ==
-                               refnumber)
-                       {
-                               tle = (TargetEntry *) copyObject(lfirst(tl));
-                               break;
-                       }
-               }
-               if (tle == NULL)
-                       elog(ERROR, "AddGroupClause(): GROUP BY entry not found in rules targetlist");
+       if (parsetree->commandType == CMD_UTILITY)
+       {
+               /*
+                * There's noplace to put the qual on a utility statement.
+                *
+                * See comments in AddQual for motivation.
+                */
+               if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
+                       return;
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("conditional utility statements are not implemented")));
+       }
 
-               /* The ressortgroupref number in the old tlist might be already
-                * taken in the new tlist, so force assignment of a new number.
+       if (parsetree->setOperations != NULL)
+       {
+               /*
+                * There's noplace to put the qual on a setop statement, either.
+                * (This could be fixed, but right now the planner simply ignores
+                * any qual condition on a setop query.)
                 */
-               tle->resdom->ressortgroupref = 0;
-               groupclause->tleSortGroupRef =
-                       assignSortGroupRef(tle, parsetree->targetList);
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
+       }
+
+       /* INTERSECT want's the original, but we need to copy - Jan */
+       copy = copyObject(havingQual);
 
-               /* Also need to set the resno and mark it resjunk. */
-               tle->resdom->resno = length(parsetree->targetList) + 1;
-               tle->resdom->resjunk = true;
+       parsetree->havingQual = make_and_qual(parsetree->havingQual,
+                                                                                 copy);
 
-               parsetree->targetList = lappend(parsetree->targetList, tle);
-               parsetree->groupClause = lappend(parsetree->groupClause, groupclause);
-       }
+       /*
+        * Make sure query is marked correctly if added qual has sublinks or
+        * aggregates (not sure it can ever have aggs, but sublinks
+        * definitely).
+        */
+       parsetree->hasAggs |= checkExprHasAggs(copy);
+       parsetree->hasSubLinks |= checkExprHasSubLink(copy);
 }
 
-static Node *
-make_null(Oid type)
-{
-       Const      *c = makeNode(Const);
-
-       c->consttype = type;
-       c->constlen = get_typlen(type);
-       c->constvalue = PointerGetDatum(NULL);
-       c->constisnull = true;
-       c->constbyval = get_typbyval(type);
-       return (Node *) c;
-}
 
-#ifdef NOT_USED
+/*
+ * Invert the given clause and add it to the WHERE qualifications of the
+ * given querytree.  Inversion means "x IS NOT TRUE", not just "NOT x",
+ * else we will do the wrong thing when x evaluates to NULL.
+ */
 void
-FixResdomTypes(List *tlist)
+AddInvertedQual(Query *parsetree, Node *qual)
 {
-       List       *i;
+       BooleanTest *invqual;
 
-       foreach(i, tlist)
-       {
-               TargetEntry *tle = lfirst(i);
+       if (qual == NULL)
+               return;
 
-               if (nodeTag(tle->expr) == T_Var)
-               {
-                       Var                *var = (Var *) tle->expr;
+       /* Need not copy input qual, because AddQual will... */
+       invqual = makeNode(BooleanTest);
+       invqual->arg = (Expr *) qual;
+       invqual->booltesttype = IS_NOT_TRUE;
 
-                       tle->resdom->restype = var->vartype;
-                       tle->resdom->restypmod = var->vartypmod;
-               }
-       }
+       AddQual(parsetree, (Node *) invqual);
 }
 
-#endif
 
+/* Find a targetlist entry by resno */
 static Node *
 FindMatchingNew(List *tlist, int attno)
 {
@@ -593,11 +849,14 @@ FindMatchingNew(List *tlist, int attno)
                TargetEntry *tle = lfirst(i);
 
                if (tle->resdom->resno == attno)
-                       return tle->expr;
+                       return (Node *) tle->expr;
        }
        return NULL;
 }
 
+#ifdef NOT_USED
+
+/* Find a targetlist entry by resname */
 static Node *
 FindMatchingTLEntry(List *tlist, char *e_attname)
 {
@@ -609,470 +868,241 @@ FindMatchingTLEntry(List *tlist, char *e_attname)
                char       *resname;
 
                resname = tle->resdom->resname;
-               if (!strcmp(e_attname, resname))
+               if (strcmp(e_attname, resname) == 0)
                        return tle->expr;
        }
        return NULL;
 }
+#endif
 
-static void
-ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
-                  int sublevels_up)
-{
-       Node       *node = *nodePtr;
-
-       if (node == NULL)
-               return;
 
-       switch (nodeTag(node))
-       {
-               case T_TargetEntry:
-                       ResolveNew(info, targetlist, &((TargetEntry *) node)->expr,
-                                          sublevels_up);
-                       break;
-               case T_Aggref:
-                       ResolveNew(info, targetlist, &((Aggref *) node)->target,
-                                          sublevels_up);
-                       break;
-               case T_Expr:
-                       ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)),
-                                          sublevels_up);
-                       break;
-               case T_Iter:
-                       ResolveNew(info, targetlist, (Node **) (&(((Iter *) node)->iterexpr)),
-                                          sublevels_up);
-                       break;
-               case T_ArrayRef:
-                       ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refupperindexpr)),
-                                          sublevels_up);
-                       ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->reflowerindexpr)),
-                                          sublevels_up);
-                       ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refexpr)),
-                                          sublevels_up);
-                       ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refassgnexpr)),
-                                          sublevels_up);
-                       break;
-               case T_Var:
-                       {
-                               int                     this_varno = (int) ((Var *) node)->varno;
-                               int                     this_varlevelsup = (int) ((Var *) node)->varlevelsup;
-                               Node       *n;
-
-                               if (this_varno == info->new_varno &&
-                                       this_varlevelsup == sublevels_up)
-                               {
-                                       n = FindMatchingNew(targetlist,
-                                                                               ((Var *) node)->varattno);
-                                       if (n == NULL)
-                                       {
-                                               if (info->event == CMD_UPDATE)
-                                               {
-                                                       *nodePtr = n = copyObject(node);
-                                                       ((Var *) n)->varno = info->current_varno;
-                                                       ((Var *) n)->varnoold = info->current_varno;
-                                               }
-                                               else
-                                                       *nodePtr = make_null(((Var *) node)->vartype);
-                                       }
-                                       else
-                                       {
-                                               *nodePtr = copyObject(n);
-                                               ((Var *) *nodePtr)->varlevelsup = this_varlevelsup;
-                                       }
-                               }
-                               break;
-                       }
-               case T_List:
-                       {
-                               List       *l;
-
-                               foreach(l, (List *) node)
-                                       ResolveNew(info, targetlist, (Node **) &(lfirst(l)),
-                                                          sublevels_up);
-                               break;
-                       }
-               case T_SubLink:
-                       {
-                               SubLink    *sublink = (SubLink *) node;
-                               Query      *query = (Query *) sublink->subselect;
-
-                               /* XXX what about lefthand?  What about rest of subquery? */
-                               ResolveNew(info, targetlist, (Node **) &(query->qual), sublevels_up + 1);
-                       }
-                       break;
-               case T_GroupClause:
-                       break;
-               default:
-                       /* ignore the others */
-                       break;
-       }
-}
+/*
+ * ResolveNew - replace Vars with corresponding items from a targetlist
+ *
+ * Vars matching target_varno and sublevels_up are replaced by the
+ * entry with matching resno from targetlist, if there is one.
+ * If not, we either change the unmatched Var's varno to update_varno
+ * (when event == CMD_UPDATE) or replace it with a constant NULL.
+ */
 
-void
-FixNew(RewriteInfo *info, Query *parsetree)
+typedef struct
 {
-       ResolveNew(info, parsetree->targetList,
-                          (Node **) &(info->rule_action->targetList), 0);
-       ResolveNew(info, parsetree->targetList,
-                          (Node **) &info->rule_action->qual, 0);
-       ResolveNew(info, parsetree->targetList,
-                          (Node **) &(info->rule_action->groupClause), 0);
-}
+       int                     target_varno;
+       int                     sublevels_up;
+       List       *targetlist;
+       int                     event;
+       int                     update_varno;
+} ResolveNew_context;
 
-static void
-nodeHandleRIRAttributeRule(Node **nodePtr,
-                                                  List *rtable,
-                                                  List *targetlist,
-                                                  int rt_index,
-                                                  int attr_num,
-                                                  int *modified,
-                                                  int *badsql,
-                                                  int sublevels_up)
+static Node *
+ResolveNew_mutator(Node *node, ResolveNew_context *context)
 {
-       Node       *node = *nodePtr;
-
        if (node == NULL)
-               return;
-       switch (nodeTag(node))
+               return NULL;
+       if (IsA(node, Var))
        {
-               case T_TargetEntry:
-                       {
-                               TargetEntry *tle = (TargetEntry *) node;
+               Var                *var = (Var *) node;
+               int                     this_varno = (int) var->varno;
+               int                     this_varlevelsup = (int) var->varlevelsup;
 
-                               nodeHandleRIRAttributeRule(&tle->expr, rtable, targetlist,
-                                                                       rt_index, attr_num, modified, badsql,
-                                                                                  sublevels_up);
-                       }
-                       break;
-               case T_Aggref:
-                       {
-                               Aggref     *aggref = (Aggref *) node;
+               if (this_varno == context->target_varno &&
+                       this_varlevelsup == context->sublevels_up)
+               {
+                       Node       *n;
 
-                               nodeHandleRIRAttributeRule(&aggref->target, rtable, targetlist,
-                                                                       rt_index, attr_num, modified, badsql,
-                                                                                  sublevels_up);
-                       }
-                       break;
-               case T_Expr:
-                       {
-                               Expr       *expr = (Expr *) node;
+                       /* band-aid: don't do the wrong thing with a whole-tuple Var */
+                       if (var->varattno == InvalidAttrNumber)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("cannot handle whole-tuple reference")));
 
-                               nodeHandleRIRAttributeRule((Node **) (&(expr->args)), rtable,
-                                                                                  targetlist, rt_index, attr_num,
-                                                                                  modified, badsql,
-                                                                                  sublevels_up);
-                       }
-                       break;
-               case T_Iter:
-                       {
-                               Iter       *iter = (Iter *) node;
+                       n = FindMatchingNew(context->targetlist, var->varattno);
 
-                               nodeHandleRIRAttributeRule((Node **) (&(iter->iterexpr)), rtable,
-                                                                                  targetlist, rt_index, attr_num,
-                                                                                  modified, badsql,
-                                                                                  sublevels_up);
-                       }
-                       break;
-               case T_ArrayRef:
-                       {
-                               ArrayRef   *ref = (ArrayRef *) node;
-
-                               nodeHandleRIRAttributeRule((Node **) (&(ref->refupperindexpr)), rtable,
-                                                                                  targetlist, rt_index, attr_num,
-                                                                                  modified, badsql,
-                                                                                  sublevels_up);
-                               nodeHandleRIRAttributeRule((Node **) (&(ref->reflowerindexpr)), rtable,
-                                                                                  targetlist, rt_index, attr_num,
-                                                                                  modified, badsql,
-                                                                                  sublevels_up);
-                               nodeHandleRIRAttributeRule((Node **) (&(ref->refexpr)), rtable,
-                                                                                  targetlist, rt_index, attr_num,
-                                                                                  modified, badsql,
-                                                                                  sublevels_up);
-                               nodeHandleRIRAttributeRule((Node **) (&(ref->refassgnexpr)), rtable,
-                                                                                  targetlist, rt_index, attr_num,
-                                                                                  modified, badsql,
-                                                                                  sublevels_up);
-                       }
-                       break;
-               case T_Var:
+                       if (n == NULL)
                        {
-                               int                     this_varno = ((Var *) node)->varno;
-                               int                     this_varattno = ((Var *) node)->varattno;
-                               int                     this_varlevelsup = ((Var *) node)->varlevelsup;
-
-                               if (this_varno == rt_index &&
-                                       this_varattno == attr_num &&
-                                       this_varlevelsup == sublevels_up)
+                               if (context->event == CMD_UPDATE)
                                {
-                                       if (((Var *) node)->vartype == 32)
-                                       {                       /* HACK */
-                                               *nodePtr = make_null(((Var *) node)->vartype);
-                                               *modified = TRUE;
-                                               *badsql = TRUE;
-                                               break;
-                                       }
-                                       else
-                                       {
-                                               NameData        name_to_look_for;
-
-                                               name_to_look_for.data[0] = '\0';
-                                               namestrcpy(&name_to_look_for,
-                                                               (char *) get_attname(getrelid(this_varno,
-                                                                                                                         rtable),
-                                                                                                        attr_num));
-                                               if (name_to_look_for.data[0])
-                                               {
-                                                       Node       *n;
-
-                                                       n = FindMatchingTLEntry(targetlist, (char *) &name_to_look_for);
-                                                       if (n == NULL)
-                                                               *nodePtr = make_null(((Var *) node)->vartype);
-                                                       else
-                                                               *nodePtr = n;
-                                                       *modified = TRUE;
-                                               }
-                                       }
+                                       /* For update, just change unmatched var's varno */
+                                       var = (Var *) copyObject(node);
+                                       var->varno = context->update_varno;
+                                       var->varnoold = context->update_varno;
+                                       return (Node *) var;
                                }
-                       }
-                       break;
-               case T_List:
-                       {
-                               List       *i;
-
-                               foreach(i, (List *) node)
+                               else
                                {
-                                       nodeHandleRIRAttributeRule((Node **) (&(lfirst(i))), rtable,
-                                                                                 targetlist, rt_index, attr_num,
-                                                                                modified, badsql, sublevels_up);
+                                       /* Otherwise replace unmatched var with a null */
+                                       return (Node *) makeNullConst(var->vartype);
                                }
                        }
-                       break;
-               case T_SubLink:
+                       else
                        {
-                               SubLink    *sublink = (SubLink *) node;
-                               Query      *query = (Query *) sublink->subselect;
-
-                               /* XXX what about lefthand?  What about rest of subquery? */
-                               nodeHandleRIRAttributeRule((Node **) &(query->qual), rtable, targetlist,
-                                                                       rt_index, attr_num, modified, badsql,
-                                                                                  sublevels_up + 1);
+                               /* Make a copy of the tlist item to return */
+                               n = copyObject(n);
+                               /* Adjust varlevelsup if tlist item is from higher query */
+                               if (this_varlevelsup > 0)
+                                       IncrementVarSublevelsUp(n, this_varlevelsup, 0);
+                               return n;
                        }
-                       break;
-               default:
-                       /* ignore the others */
-                       break;
+               }
+               /* otherwise fall through to copy the var normally */
        }
-}
-
-/*
- * Handles 'on retrieve to relation.attribute
- *                     do instead retrieve (attribute = expression) w/qual'
- */
-void
-HandleRIRAttributeRule(Query *parsetree,
-                                          List *rtable,
-                                          List *targetlist,
-                                          int rt_index,
-                                          int attr_num,
-                                          int *modified,
-                                          int *badsql)
-{
 
-       nodeHandleRIRAttributeRule((Node **) (&(parsetree->targetList)), rtable,
-                                                          targetlist, rt_index, attr_num,
-                                                          modified, badsql, 0);
-       nodeHandleRIRAttributeRule(&parsetree->qual, rtable, targetlist,
-                                                          rt_index, attr_num, modified, badsql, 0);
+       if (IsA(node, Query))
+       {
+               /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+               Query      *newnode;
+
+               context->sublevels_up++;
+               newnode = query_tree_mutator((Query *) node,
+                                                                        ResolveNew_mutator,
+                                                                        (void *) context,
+                                                                        0);
+               context->sublevels_up--;
+               return (Node *) newnode;
+       }
+       return expression_tree_mutator(node, ResolveNew_mutator,
+                                                                  (void *) context);
 }
 
-#ifdef NOT_USED
-static void
-nodeHandleViewRule(Node **nodePtr,
-                                  List *rtable,
-                                  List *targetlist,
-                                  int rt_index,
-                                  int *modified,
-                                  int sublevels_up)
+Node *
+ResolveNew(Node *node, int target_varno, int sublevels_up,
+                  List *targetlist, int event, int update_varno)
 {
-       Node       *node = *nodePtr;
+       ResolveNew_context context;
 
-       if (node == NULL)
-               return;
+       context.target_varno = target_varno;
+       context.sublevels_up = sublevels_up;
+       context.targetlist = targetlist;
+       context.event = event;
+       context.update_varno = update_varno;
 
-       switch (nodeTag(node))
-       {
-               case T_TargetEntry:
-                       {
-                               TargetEntry *tle = (TargetEntry *) node;
-
-                               nodeHandleViewRule(&(tle->expr), rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                       }
-                       break;
-               case T_Aggref:
-                       {
-                               Aggref     *aggref = (Aggref *) node;
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, we don't want to increment sublevels_up.
+        */
+       return query_or_expression_tree_mutator(node,
+                                                                                       ResolveNew_mutator,
+                                                                                       (void *) &context,
+                                                                                       0);
+}
 
-                               nodeHandleViewRule(&(aggref->target), rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                       }
-                       break;
 
-                       /*
-                        * This has to be done to make queries using groupclauses work
-                        * on views
-                        */
-               case T_GroupClause:
-                       {
-                               GroupClause *group = (GroupClause *) node;
+#ifdef NOT_USED
 
-                               nodeHandleViewRule((Node **) (&(group->entry)), rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                       }
-                       break;
-               case T_Expr:
-                       {
-                               Expr       *expr = (Expr *) node;
+/*
+ * HandleRIRAttributeRule
+ *     Replace Vars matching a given RT index with copies of TL expressions.
+ *
+ * Handles 'on retrieve to relation.attribute
+ *                     do instead retrieve (attribute = expression) w/qual'
+ */
 
-                               nodeHandleViewRule((Node **) (&(expr->args)),
-                                                                  rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                       }
-                       break;
-               case T_Iter:
-                       {
-                               Iter       *iter = (Iter *) node;
+typedef struct
+{
+       List       *rtable;
+       List       *targetlist;
+       int                     rt_index;
+       int                     attr_num;
+       int                *modified;
+       int                *badsql;
+       int                     sublevels_up;
+}      HandleRIRAttributeRule_context;
 
-                               nodeHandleViewRule((Node **) (&(iter->iterexpr)),
-                                                                  rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                       }
-                       break;
-               case T_ArrayRef:
-                       {
-                               ArrayRef   *ref = (ArrayRef *) node;
-
-                               nodeHandleViewRule((Node **) (&(ref->refupperindexpr)),
-                                                                  rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                               nodeHandleViewRule((Node **) (&(ref->reflowerindexpr)),
-                                                                  rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                               nodeHandleViewRule((Node **) (&(ref->refexpr)),
-                                                                  rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
-                               nodeHandleViewRule((Node **) (&(ref->refassgnexpr)),
-                                                                  rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up);
+static Node *
+HandleRIRAttributeRule_mutator(Node *node,
+                                                          HandleRIRAttributeRule_context * context)
+{
+       if (node == NULL)
+               return NULL;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
+               int                     this_varno = var->varno;
+               int                     this_varattno = var->varattno;
+               int                     this_varlevelsup = var->varlevelsup;
+
+               if (this_varno == context->rt_index &&
+                       this_varattno == context->attr_num &&
+                       this_varlevelsup == context->sublevels_up)
+               {
+                       if (var->vartype == 32)
+                       {                                       /* HACK: disallow SET variables */
+                               *context->modified = TRUE;
+                               *context->badsql = TRUE;
+                               return (Node *) makeNullConst(var->vartype);
                        }
-                       break;
-               case T_Var:
+                       else
                        {
-                               Var                *var = (Var *) node;
-                               int                     this_varno = var->varno;
-                               int                     this_varlevelsup = var->varlevelsup;
-                               Node       *n;
+                               char       *name_to_look_for;
 
-                               if (this_varno == rt_index &&
-                                       this_varlevelsup == sublevels_up)
+                               name_to_look_for = get_attname(getrelid(this_varno,
+                                                                                                               context->rtable),
+                                                                                          this_varattno);
+                               if (name_to_look_for)
                                {
-                                       n = FindMatchingTLEntry(targetlist,
-                                                                                get_attname(getrelid(this_varno,
-                                                                                                                         rtable),
-                                                                                                        var->varattno));
-                                       if (n == NULL)
-                                               *nodePtr = make_null(((Var *) node)->vartype);
-                                       else
-                                       {
-
-                                               /*
-                                                * This is a hack: The varlevelsup of the orignal
-                                                * variable and the new one should be the same.
-                                                * Normally we adapt the node by changing a
-                                                * pointer to point to a var contained in
-                                                * 'targetlist'. In the targetlist all
-                                                * varlevelsups are 0 so if we want to change it
-                                                * to the original value we have to copy the node
-                                                * before! (Maybe this will cause troubles with
-                                                * some sophisticated queries on views?)
-                                                */
-                                               if (this_varlevelsup > 0)
-                                                       *nodePtr = copyObject(n);
-                                               else
-                                                       *nodePtr = n;
-
-                                               if (nodeTag(nodePtr) == T_Var)
-                                                       ((Var *) *nodePtr)->varlevelsup = this_varlevelsup;
-                                               else
-                                                       nodeHandleViewRule(&n, rtable, targetlist,
-                                                                          rt_index, modified, sublevels_up);
-                                       }
-                                       *modified = TRUE;
-                               }
-                               break;
-                       }
-               case T_List:
-                       {
-                               List       *l;
+                                       Node       *n;
 
-                               foreach(l, (List *) node)
-                               {
-                                       nodeHandleViewRule((Node **) (&(lfirst(l))),
-                                                                          rtable, targetlist,
-                                                                          rt_index, modified, sublevels_up);
+                                       *context->modified = TRUE;
+                                       n = FindMatchingTLEntry(context->targetlist,
+                                                                                       name_to_look_for);
+                                       if (n == NULL)
+                                               return (Node *) makeNullConst(var->vartype);
+                                       /* Make a copy of the tlist item to return */
+                                       n = copyObject(n);
+
+                                       /*
+                                        * Adjust varlevelsup if tlist item is from higher
+                                        * query
+                                        */
+                                       if (this_varlevelsup > 0)
+                                               IncrementVarSublevelsUp(n, this_varlevelsup, 0);
+                                       return n;
                                }
                        }
-                       break;
-               case T_SubLink:
-                       {
-                               SubLink    *sublink = (SubLink *) node;
-                               Query      *query = (Query *) sublink->subselect;
-
-                               nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up + 1);
-
-                               /***S*H*D***/
-                               nodeHandleViewRule((Node **) &(query->havingQual), rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up + 1);
-                               nodeHandleViewRule((Node **) &(query->targetList), rtable, targetlist,
-                                                                  rt_index, modified, sublevels_up + 1);
-
+               }
+               /* otherwise fall through to copy the var normally */
+       }
 
-                               /*
-                                * We also have to adapt the variables used in
-                                * sublink->lefthand
-                                */
-                               nodeHandleViewRule((Node **) &(sublink->lefthand), rtable,
-                                                  targetlist, rt_index, modified, sublevels_up);
-                       }
-                       break;
-               default:
-                       /* ignore the others */
-                       break;
+       if (IsA(node, Query))
+       {
+               /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+               Query      *newnode;
+
+               context->sublevels_up++;
+               newnode = query_tree_mutator((Query *) node,
+                                                                        HandleRIRAttributeRule_mutator,
+                                                                        (void *) context,
+                                                                        0);
+               context->sublevels_up--;
+               return (Node *) newnode;
        }
+       return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
+                                                                  (void *) context);
 }
 
 void
-HandleViewRule(Query *parsetree,
-                          List *rtable,
-                          List *targetlist,
-                          int rt_index,
-                          int *modified)
+HandleRIRAttributeRule(Query *parsetree,
+                                          List *rtable,
+                                          List *targetlist,
+                                          int rt_index,
+                                          int attr_num,
+                                          int *modified,
+                                          int *badsql)
 {
-       nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index,
-                                          modified, 0);
-       nodeHandleViewRule((Node **) (&(parsetree->targetList)), rtable, targetlist,
-                                          rt_index, modified, 0);
-
-       /*
-        * The variables in the havingQual and groupClause also have to be
-        * adapted
-        */
-       nodeHandleViewRule(&parsetree->havingQual, rtable, targetlist, rt_index,
-                                          modified, 0);
-       nodeHandleViewRule((Node **) (&(parsetree->groupClause)), rtable, targetlist, rt_index,
-                                          modified, 0);
+       HandleRIRAttributeRule_context context;
+
+       context.rtable = rtable;
+       context.targetlist = targetlist;
+       context.rt_index = rt_index;
+       context.attr_num = attr_num;
+       context.modified = modified;
+       context.badsql = badsql;
+       context.sublevels_up = 0;
+
+       query_tree_mutator(parsetree,
+                                          HandleRIRAttributeRule_mutator,
+                                          (void *) &context,
+                                          QTW_DONT_COPY_QUERY);
 }
 
-#endif
+#endif   /* NOT_USED */