]> granicus.if.org Git - postgresql/blobdiff - src/backend/rewrite/rewriteHandler.c
Code review for UPDATE tab SET col = DEFAULT patch ... whack it around
[postgresql] / src / backend / rewrite / rewriteHandler.c
index a9ff8a0a3b18b7ab3855f088296849ceeb57b5bc..a1d9122f828e9146974fa1571ed77e3dbc78078a 100644 (file)
 /*-------------------------------------------------------------------------
  *
  * rewriteHandler.c
+ *             Primary module of query rewriter.
  *
- * Copyright (c) 1994, Regents of the University of California
- *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.37 1999/02/22 05:26:46 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.122 2003/07/03 16:34:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <string.h>
 #include "postgres.h"
-#include "miscadmin.h"
-#include "utils/palloc.h"
-#include "utils/elog.h"
-#include "utils/rel.h"
-#include "nodes/pg_list.h"
-#include "nodes/primnodes.h"
-#include "nodes/relation.h"
-
-#include "parser/parsetree.h"  /* for parsetree manipulation */
-#include "parser/parse_relation.h"
-#include "nodes/parsenodes.h"
-
-/***S*I***/
-#include "parser/parse_node.h"
-#include "parser/parse_target.h"
 
-#include "parser/analyze.h"
+#include "access/heapam.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_type.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
 #include "optimizer/prep.h"
-
-#include "rewrite/rewriteSupport.h"
+#include "optimizer/var.h"
+#include "parser/analyze.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_oper.h"
+#include "parser/parse_type.h"
+#include "parser/parsetree.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
-#include "rewrite/locks.h"
-
-#include "commands/creatinh.h"
-#include "access/heapam.h"
-
+#include "utils/builtins.h"
 #include "utils/lsyscache.h"
-#include "utils/syscache.h"
-#include "utils/acl.h"
-#include "catalog/pg_shadow.h"
-#include "catalog/pg_type.h"
 
 
-static RewriteInfo *gatherRewriteMeta(Query *parsetree,
+/* We use a list of these to detect recursion in RewriteQuery */
+typedef struct rewrite_event {
+       Oid                     relation;               /* OID of relation having rules */
+       CmdType         event;                  /* type of rule being fired */
+} rewrite_event;
+
+static Query *rewriteRuleAction(Query *parsetree,
                                  Query *rule_action,
                                  Node *rule_qual,
                                  int rt_index,
-                                 CmdType event,
-                                 bool *instead_flag);
-static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
-static bool attribute_used(Node *node, int rt_index, int attno, int sublevels_up);
-static void modifyAggrefUplevel(Node *node);
-static void modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up);
-static void modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr);
-static SubLink *modifyAggrefMakeSublink(Expr *origexp, Query *parsetree);
-static void modifyAggrefQual(Node **nodePtr, Query *parsetree);
-
-
-static Query *fireRIRrules(Query *parsetree);
+                                 CmdType event);
+static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
+static void rewriteTargetList(Query *parsetree, Relation target_relation);
+static TargetEntry *process_matched_tle(TargetEntry *src_tle,
+                                       TargetEntry *prior_tle);
+static void markQueryForUpdate(Query *qry, bool skipOldNew);
+static List *matchLocks(CmdType event, RuleLock *rulelocks,
+                  int varno, Query *parsetree);
+static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
 
 
 /*
- * gatherRewriteMeta -
- *       Gather meta information about parsetree, and rule. Fix rule body
- *       and qualifier so that they can be mixed with the parsetree and
- *       maintain semantic validity
+ * rewriteRuleAction -
+ *       Rewrite the rule action with appropriate qualifiers (taken from
+ *       the triggering query).
  */
-static RewriteInfo *
-gatherRewriteMeta(Query *parsetree,
+static Query *
+rewriteRuleAction(Query *parsetree,
                                  Query *rule_action,
                                  Node *rule_qual,
                                  int rt_index,
-                                 CmdType event,
-                                 bool *instead_flag)
+                                 CmdType event)
 {
-       RewriteInfo *info;
+       int                     current_varno,
+                               new_varno;
+       List       *main_rtable;
        int                     rt_length;
-       int                     result_reln;
-
-       info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
-       info->rt_index = rt_index;
-       info->event = event;
-       info->instead_flag = *instead_flag;
-       info->rule_action = (Query *) copyObject(rule_action);
-       info->rule_qual = (Node *) copyObject(rule_qual);
-       if (info->rule_action == NULL)
-               info->nothing = TRUE;
-       else
+       Query      *sub_action;
+       Query     **sub_action_ptr;
+       List       *rt;
+
+       /*
+        * Make modifiable copies of rule action and qual (what we're passed
+        * are the stored versions in the relcache; don't touch 'em!).
+        */
+       rule_action = (Query *) copyObject(rule_action);
+       rule_qual = (Node *) copyObject(rule_qual);
+
+       current_varno = rt_index;
+       rt_length = length(parsetree->rtable);
+       new_varno = PRS2_NEW_VARNO + rt_length;
+
+       /*
+        * Adjust rule action and qual to offset its varnos, so that we can
+        * merge its rtable with the main parsetree's rtable.
+        *
+        * If the rule action is an INSERT...SELECT, the OLD/NEW rtable entries
+        * will be in the SELECT part, and we have to modify that rather than
+        * the top-level INSERT (kluge!).
+        */
+       sub_action = getInsertSelectQuery(rule_action, &sub_action_ptr);
+
+       OffsetVarNodes((Node *) sub_action, rt_length, 0);
+       OffsetVarNodes(rule_qual, rt_length, 0);
+       /* but references to *OLD* should point at original rt_index */
+       ChangeVarNodes((Node *) sub_action,
+                                  PRS2_OLD_VARNO + rt_length, rt_index, 0);
+       ChangeVarNodes(rule_qual,
+                                  PRS2_OLD_VARNO + rt_length, rt_index, 0);
+
+       /*
+        * Generate expanded rtable consisting of main parsetree's rtable plus
+        * rule action's rtable; this becomes the complete rtable for the rule
+        * action.      Some of the entries may be unused after we finish
+        * rewriting, but if we tried to remove them we'd have a much
+        * harder job to adjust RT indexes in the query's Vars.  It's OK to
+        * have unused RT entries, since planner will ignore them.
+        *
+        * NOTE: because planner will destructively alter rtable, we must ensure
+        * that rule action's rtable is separate and shares no substructure
+        * with the main rtable.  Hence do a deep copy here.
+        *
+        * Also, we must disable write-access checking in all the RT entries
+        * copied from the main query.  This is safe since in fact the rule action
+        * won't write on them, and it's necessary because the rule action may
+        * have a different commandType than the main query, causing
+        * ExecCheckRTEPerms() to make an inappropriate check.  The read-access
+        * checks can be left enabled, although they're probably redundant.
+        */
+       main_rtable = (List *) copyObject(parsetree->rtable);
+
+       foreach(rt, main_rtable)
        {
-               info->nothing = FALSE;
-               info->action = info->rule_action->commandType;
-               info->current_varno = rt_index;
-               info->rt = parsetree->rtable;
-               rt_length = length(info->rt);
-               info->rt = nconc(info->rt, copyObject(info->rule_action->rtable));
-
-               info->new_varno = PRS2_NEW_VARNO + rt_length;
-               OffsetVarNodes(info->rule_action->qual, rt_length, 0);
-               OffsetVarNodes((Node *) info->rule_action->targetList, rt_length, 0);
-               OffsetVarNodes(info->rule_qual, rt_length, 0);
-               ChangeVarNodes((Node *) info->rule_action->qual,
-                                          PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
-               ChangeVarNodes((Node *) info->rule_action->targetList,
-                                          PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
-               ChangeVarNodes(info->rule_qual,
-                                          PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
+               RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
-               /*
-                * bug here about replace CURRENT  -- sort of replace current is
-                * deprecated now so this code shouldn't really need to be so
-                * clutzy but.....
-                */
-               if (info->action != CMD_SELECT)
-               {                                               /* i.e update XXXXX */
-                       int                     new_result_reln = 0;
+               rte->checkForWrite = false;
+       }
 
-                       result_reln = info->rule_action->resultRelation;
-                       switch (result_reln)
-                       {
-                               case PRS2_CURRENT_VARNO:
-                                       new_result_reln = rt_index;
-                                       break;
-                               case PRS2_NEW_VARNO:    /* XXX */
-                               default:
-                                       new_result_reln = result_reln + rt_length;
-                                       break;
-                       }
-                       info->rule_action->resultRelation = new_result_reln;
-               }
+       sub_action->rtable = nconc(main_rtable, sub_action->rtable);
+
+       /*
+        * Each rule action's jointree should be the main parsetree's jointree
+        * plus that rule's jointree, but usually *without* the original
+        * rtindex that we're replacing (if present, which it won't be for
+        * INSERT). Note that if the rule action refers to OLD, its jointree
+        * will add a reference to rt_index.  If the rule action doesn't refer
+        * to OLD, but either the rule_qual or the user query quals do, then
+        * we need to keep the original rtindex in the jointree to provide
+        * data for the quals.  We don't want the original rtindex to be
+        * joined twice, however, so avoid keeping it if the rule action
+        * mentions it.
+        *
+        * As above, the action's jointree must not share substructure with the
+        * main parsetree's.
+        */
+       if (sub_action->jointree != NULL)
+       {
+               bool            keeporig;
+               List       *newjointree;
+
+               keeporig = (!rangeTableEntry_used((Node *) sub_action->jointree,
+                                                                                 rt_index, 0)) &&
+                       (rangeTableEntry_used(rule_qual, rt_index, 0) ||
+                 rangeTableEntry_used(parsetree->jointree->quals, rt_index, 0));
+               newjointree = adjustJoinTreeList(parsetree, !keeporig, rt_index);
+               sub_action->jointree->fromlist =
+                       nconc(newjointree, sub_action->jointree->fromlist);
        }
-       return info;
-}
 
+       /*
+        * We copy the qualifications of the parsetree to the action and vice
+        * versa. So force hasSubLinks if one of them has it. If this is not
+        * right, the flag will get cleared later, but we mustn't risk having
+        * it not set when it needs to be.      (XXX this should probably be
+        * handled by AddQual and friends, not here...)
+        */
+       if (parsetree->hasSubLinks)
+               sub_action->hasSubLinks = TRUE;
+       else if (sub_action->hasSubLinks)
+               parsetree->hasSubLinks = TRUE;
+
+       /*
+        * Event Qualification forces copying of parsetree and splitting into
+        * two queries one w/rule_qual, one w/NOT rule_qual. Also add user
+        * query qual onto rule action
+        */
+       AddQual(sub_action, rule_qual);
+
+       AddQual(sub_action, parsetree->jointree->quals);
+
+       /*
+        * Rewrite new.attribute w/ right hand side of target-list entry for
+        * appropriate field name in insert/update.
+        *
+        * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just
+        * apply it to sub_action; we have to remember to update the sublink
+        * inside rule_action, too.
+        */
+       if (event == CMD_INSERT || event == CMD_UPDATE)
+       {
+               sub_action = (Query *) ResolveNew((Node *) sub_action,
+                                                                                 new_varno,
+                                                                                 0,
+                                                                                 parsetree->targetList,
+                                                                                 event,
+                                                                                 current_varno);
+               if (sub_action_ptr)
+                       *sub_action_ptr = sub_action;
+               else
+                       rule_action = sub_action;
+       }
+
+       return rule_action;
+}
 
 /*
- * rangeTableEntry_used -
- *     we need to process a RTE for RIR rules only if it is
- *     referenced somewhere in var nodes of the query.
+ * Copy the query's jointree list, and optionally attempt to remove any
+ * occurrence of the given rt_index as a top-level join item (we do not look
+ * for it within join items; this is OK because we are only expecting to find
+ * it as an UPDATE or DELETE target relation, which will be at the top level
+ * of the join).  Returns modified jointree list --- this is a separate copy
+ * sharing no nodes with the original.
  */
-static bool
-rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
+static List *
+adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
 {
-       if (node == NULL)
-               return FALSE;
-
-       switch(nodeTag(node)) {
-               case T_TargetEntry:
-                       {
-                               TargetEntry     *tle = (TargetEntry *)node;
+       List       *newjointree = copyObject(parsetree->jointree->fromlist);
+       List       *jjt;
 
-                               return rangeTableEntry_used(
-                                               (Node *)(tle->expr),
-                                               rt_index,
-                                               sublevels_up);
-                       }
-                       break;
+       if (removert)
+       {
+               foreach(jjt, newjointree)
+               {
+                       RangeTblRef *rtr = lfirst(jjt);
 
-               case T_Aggref:
+                       if (IsA(rtr, RangeTblRef) &&
+                               rtr->rtindex == rt_index)
                        {
-                               Aggref  *aggref = (Aggref *)node;
-
-                               return rangeTableEntry_used(
-                                               (Node *)(aggref->target),
-                                               rt_index,
-                                               sublevels_up);
+                               newjointree = lremove(rtr, newjointree);
+                               /* foreach is safe because we exit loop after lremove... */
+                               break;
                        }
-                       break;
+               }
+       }
+       return newjointree;
+}
 
-               case T_GroupClause:
-                       {
-                               GroupClause     *grp = (GroupClause *)node;
 
-                               return rangeTableEntry_used(
-                                               (Node *)(grp->entry),
-                                               rt_index,
-                                               sublevels_up);
-                       }
-                       break;
+/*
+ * rewriteTargetList - rewrite INSERT/UPDATE targetlist into standard form
+ *
+ * This has the following responsibilities:
+ *
+ * 1. For an INSERT, add tlist entries to compute default values for any
+ * attributes that have defaults and are not assigned to in the given tlist.
+ * (We do not insert anything for default-less attributes, however.  The
+ * planner will later insert NULLs for them, but there's no reason to slow
+ * down rewriter processing with extra tlist nodes.)  Also, for both INSERT
+ * and UPDATE, replace explicit DEFAULT specifications with column default
+ * expressions.
+ *
+ * 2. Merge multiple entries for the same target attribute, or declare error
+ * if we can't.  Presently, multiple entries are only allowed for UPDATE of
+ * an array field, for example "UPDATE table SET foo[2] = 42, foo[4] = 43".
+ * We can merge such operations into a single assignment op.  Essentially,
+ * the expression we want to produce in this case is like
+ *             foo = array_set(array_set(foo, 2, 42), 4, 43)
+ *
+ * 3. Sort the tlist into standard order: non-junk fields in order by resno,
+ * then junk fields (these in no particular order).
+ *
+ * We must do items 1 and 2 before firing rewrite rules, else rewritten
+ * references to NEW.foo will produce wrong or incomplete results.     Item 3
+ * is not needed for rewriting, but will be needed by the planner, and we
+ * can do it essentially for free while handling items 1 and 2.
+ */
+static void
+rewriteTargetList(Query *parsetree, Relation target_relation)
+{
+       CmdType         commandType = parsetree->commandType;
+       List       *tlist = parsetree->targetList;
+       List       *new_tlist = NIL;
+       int                     attrno,
+                               numattrs;
+       List       *temp;
 
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
+       /*
+        * Scan the tuple description in the relation's relcache entry to make
+        * sure we have all the user attributes in the right order.
+        */
+       numattrs = RelationGetNumberOfAttributes(target_relation);
 
-                               return rangeTableEntry_used(
-                                               (Node *)(exp->args),
-                                               rt_index,
-                                               sublevels_up);
-                       }
-                       break;
+       for (attrno = 1; attrno <= numattrs; attrno++)
+       {
+               Form_pg_attribute att_tup = target_relation->rd_att->attrs[attrno - 1];
+               TargetEntry *new_tle = NULL;
 
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
+               /* We can ignore deleted attributes */
+               if (att_tup->attisdropped)
+                       continue;
 
-                               return rangeTableEntry_used(
-                                               (Node *)(iter->iterexpr),
-                                               rt_index,
-                                               sublevels_up);
-                       }
-                       break;
+               /*
+                * Look for targetlist entries matching this attr.      We match by
+                * resno, but the resname should match too.
+                *
+                * Junk attributes are not candidates to be matched.
+                */
+               foreach(temp, tlist)
+               {
+                       TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
+                       Resdom     *resdom = old_tle->resdom;
 
-               case T_ArrayRef:
+                       if (!resdom->resjunk && resdom->resno == attrno)
                        {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               if (rangeTableEntry_used(
-                                               (Node *)(ref->refupperindexpr),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-                               
-                               if (rangeTableEntry_used(
-                                               (Node *)(ref->reflowerindexpr),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-                               
-                               if (rangeTableEntry_used(
-                                               (Node *)(ref->refexpr),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-                               
-                               if (rangeTableEntry_used(
-                                               (Node *)(ref->refassgnexpr),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-                               
-                               return FALSE;
+                               Assert(strcmp(resdom->resname,
+                                                         NameStr(att_tup->attname)) == 0);
+                               new_tle = process_matched_tle(old_tle, new_tle);
+                               /* keep scanning to detect multiple assignments to attr */
                        }
-                       break;
+               }
 
-               case T_Var:
-                       {
-                               Var     *var = (Var *)node;
+               /*
+                * Handle the two cases where we need to insert a default expression:
+                * it's an INSERT and there's no tlist entry for the column, or the
+                * tlist entry is a DEFAULT placeholder node.
+                */
+               if ((new_tle == NULL && commandType == CMD_INSERT) ||
+                       (new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
+               {
+                       Node       *new_expr;
+
+                       new_expr = build_column_default(target_relation, attrno);
 
-                               if (var->varlevelsup == sublevels_up)
-                                       return var->varno == rt_index;
+                       /*
+                        * If there is no default (ie, default is effectively NULL),
+                        * we can omit the tlist entry in the INSERT case, since the
+                        * planner can insert a NULL for itself, and there's no point
+                        * in spending any more rewriter cycles on the entry.  But in the
+                        * UPDATE case we've got to explicitly set the column to NULL.
+                        */
+                       if (!new_expr)
+                       {
+                               if (commandType == CMD_INSERT)
+                                       new_tle = NULL;
                                else
-                                       return FALSE;
+                               {
+                                       new_expr = (Node *) makeConst(att_tup->atttypid,
+                                                                                                 att_tup->attlen,
+                                                                                                 (Datum) 0,
+                                                                                                 true, /* isnull */
+                                                                                                 att_tup->attbyval);
+                                       /* this is to catch a NOT NULL domain constraint */
+                                       new_expr = coerce_to_domain(new_expr,
+                                                                                               InvalidOid,
+                                                                                               att_tup->atttypid,
+                                                                                               COERCE_IMPLICIT_CAST);
+                               }
                        }
-                       break;
 
-               case T_Param:
-                       return FALSE;
+                       if (new_expr)
+                               new_tle = makeTargetEntry(makeResdom(attrno,
+                                                                                                        att_tup->atttypid,
+                                                                                                        att_tup->atttypmod,
+                                                                         pstrdup(NameStr(att_tup->attname)),
+                                                                                                        false),
+                                                                                 (Expr *) new_expr);
+               }
 
-               case T_Const:
-                       return FALSE;
+               if (new_tle)
+                       new_tlist = lappend(new_tlist, new_tle);
+       }
 
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node) {
-                                       if (rangeTableEntry_used(
-                                                       (Node *)lfirst(l),
-                                                       rt_index,
-                                                       sublevels_up))
-                                               return TRUE;
-                               }
-                               return FALSE;
-                       }
-                       break;
+       /*
+        * Copy all resjunk tlist entries to the end of the new tlist, and
+        * assign them resnos above the last real resno.
+        *
+        * Typical junk entries include ORDER BY or GROUP BY expressions (are
+        * these actually possible in an INSERT or UPDATE?), system attribute
+        * references, etc.
+        */
+       foreach(temp, tlist)
+       {
+               TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
+               Resdom     *resdom = old_tle->resdom;
 
-               case T_SubLink:
+               if (resdom->resjunk)
+               {
+                       /* Get the resno right, but don't copy unnecessarily */
+                       if (resdom->resno != attrno)
                        {
-                               SubLink *sub = (SubLink *)node;
+                               resdom = (Resdom *) copyObject((Node *) resdom);
+                               resdom->resno = attrno;
+                               old_tle = makeTargetEntry(resdom, old_tle->expr);
+                       }
+                       new_tlist = lappend(new_tlist, old_tle);
+                       attrno++;
+               }
+               else
+               {
+                       /* Let's just make sure we processed all the non-junk items */
+                       if (resdom->resno < 1 || resdom->resno > numattrs)
+                               elog(ERROR, "rewriteTargetList: bogus resno %d in targetlist",
+                                        resdom->resno);
+               }
+       }
 
-                               if (rangeTableEntry_used(
-                                               (Node *)(sub->lefthand),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
+       parsetree->targetList = new_tlist;
+}
 
-                               if (rangeTableEntry_used(
-                                               (Node *)(sub->subselect),
-                                               rt_index,
-                                               sublevels_up + 1))
-                                       return TRUE;
 
-                               return FALSE;
-                       }
-                       break;
+/*
+ * Convert a matched TLE from the original tlist into a correct new TLE.
+ *
+ * This routine detects and handles multiple assignments to the same target
+ * attribute.
+ */
+static TargetEntry *
+process_matched_tle(TargetEntry *src_tle,
+                                       TargetEntry *prior_tle)
+{
+       Resdom     *resdom = src_tle->resdom;
+       Node       *priorbottom;
+       ArrayRef   *newexpr;
 
-               case T_CaseExpr:
-                       {
-                               CaseExpr        *exp = (CaseExpr *)node;
+       if (prior_tle == NULL)
+       {
+               /*
+                * Normal case where this is the first assignment to the
+                * attribute.
+                */
+               return src_tle;
+       }
 
-                               if (rangeTableEntry_used(
-                                               (Node *)(exp->args),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
+       /*
+        * Multiple assignments to same attribute.      Allow only if all are
+        * array-assign operators with same bottom array object.
+        */
+       if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
+               ((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||
+               prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||
+               ((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||
+               ((ArrayRef *) src_tle->expr)->refrestype !=
+               ((ArrayRef *) prior_tle->expr)->refrestype)
+               elog(ERROR, "Multiple assignments to same attribute \"%s\"",
+                        resdom->resname);
 
-                               if (rangeTableEntry_used(
-                                               (Node *)(exp->defresult),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
+       /*
+        * Prior TLE could be a nest of ArrayRefs if we do this more than
+        * once.
+        */
+       priorbottom = (Node *) ((ArrayRef *) prior_tle->expr)->refexpr;
+       while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
+                  ((ArrayRef *) priorbottom)->refassgnexpr != NULL)
+               priorbottom = (Node *) ((ArrayRef *) priorbottom)->refexpr;
+       if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
+               elog(ERROR, "Multiple assignments to same attribute \"%s\"",
+                        resdom->resname);
 
-                               return FALSE;
-                       }
-                       break;
+       /*
+        * Looks OK to nest 'em.
+        */
+       newexpr = makeNode(ArrayRef);
+       memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
+       newexpr->refexpr = prior_tle->expr;
 
-               case T_CaseWhen:
-                       {
-                               CaseWhen        *when = (CaseWhen *)node;
+       return makeTargetEntry(resdom, (Expr *) newexpr);
+}
 
-                               if (rangeTableEntry_used(
-                                               (Node *)(when->expr),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
 
-                               if (rangeTableEntry_used(
-                                               (Node *)(when->result),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
+/*
+ * Make an expression tree for the default value for a column.
+ *
+ * If there is no default, return a NULL instead.
+ */
+Node *
+build_column_default(Relation rel, int attrno)
+{
+       TupleDesc       rd_att = rel->rd_att;
+       Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
+       Oid                     atttype = att_tup->atttypid;
+       int32           atttypmod = att_tup->atttypmod;
+       Node       *expr = NULL;
+       Oid                     exprtype;
 
-                               return FALSE;
-                       }
-                       break;
+       /*
+        * Scan to see if relation has a default for this column.
+        */
+       if (rd_att->constr && rd_att->constr->num_defval > 0)
+       {
+               AttrDefault *defval = rd_att->constr->defval;
+               int                     ndef = rd_att->constr->num_defval;
 
-               case T_Query:
+               while (--ndef >= 0)
+               {
+                       if (attrno == defval[ndef].adnum)
                        {
-                               Query   *qry = (Query *)node;
-
-                               if (rangeTableEntry_used(
-                                               (Node *)(qry->targetList),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (rangeTableEntry_used(
-                                               (Node *)(qry->qual),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (rangeTableEntry_used(
-                                               (Node *)(qry->havingQual),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (rangeTableEntry_used(
-                                               (Node *)(qry->groupClause),
-                                               rt_index,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               return FALSE;
+                               /*
+                                * Found it, convert string representation to node tree.
+                                */
+                               expr = stringToNode(defval[ndef].adbin);
+                               break;
                        }
-                       break;
+               }
+       }
 
-               default:
-                       elog(NOTICE, "unknown node tag %d in rangeTableEntry_used()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
+       if (expr == NULL)
+       {
+               /*
+                * No per-column default, so look for a default for the type
+                * itself.
+                */
+               if (att_tup->attisset)
+               {
+                       /*
+                        * Set attributes are represented as OIDs no matter what the
+                        * set element type is, and the element type's default is
+                        * irrelevant too.
+                        */
+               }
+               else
+                       expr = get_typdefault(atttype);
+       }
 
+       if (expr == NULL)
+               return NULL;                    /* No default anywhere */
 
-       }
+       /*
+        * Make sure the value is coerced to the target column type (might not
+        * be right type yet if it's not a constant!)  This should match the
+        * parser's processing of non-defaulted expressions --- see
+        * updateTargetListEntry().
+        */
+       exprtype = exprType(expr);
 
-       return FALSE;
+       expr = coerce_to_target_type(NULL, /* no UNKNOWN params here */
+                                                                expr, exprtype,
+                                                                atttype, atttypmod,
+                                                                COERCION_ASSIGNMENT,
+                                                                COERCE_IMPLICIT_CAST);
+       /*
+        * This really shouldn't fail; should have checked the default's
+        * type when it was created ...
+        */
+       if (expr == NULL)
+               elog(ERROR, "Column \"%s\" is of type %s"
+                        " but default expression is of type %s"
+                        "\n\tYou will need to rewrite or cast the expression",
+                        NameStr(att_tup->attname),
+                        format_type_be(atttype),
+                        format_type_be(exprtype));
+
+       return expr;
 }
 
 
 /*
- * attribute_used -
- *     Check if a specific attribute number of a RTE is used
- *     somewhere in the query
+ * matchLocks -
+ *       match the list of locks and returns the matching rules
  */
-static bool
-attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
+static List *
+matchLocks(CmdType event,
+                  RuleLock *rulelocks,
+                  int varno,
+                  Query *parsetree)
 {
-       if (node == NULL)
-               return FALSE;
-
-       switch(nodeTag(node)) {
-               case T_TargetEntry:
-                       {
-                               TargetEntry     *tle = (TargetEntry *)node;
-
-                               return attribute_used(
-                                               (Node *)(tle->expr),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref  *aggref = (Aggref *)node;
-
-                               return attribute_used(
-                                               (Node *)(aggref->target),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up);
-                       }
-                       break;
+       List       *matching_locks = NIL;
+       int                     nlocks;
+       int                     i;
 
-               case T_GroupClause:
-                       {
-                               GroupClause     *grp = (GroupClause *)node;
+       if (rulelocks == NULL)
+               return NIL;
 
-                               return attribute_used(
-                                               (Node *)(grp->entry),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up);
-                       }
-                       break;
+       if (parsetree->commandType != CMD_SELECT)
+       {
+               if (parsetree->resultRelation != varno)
+                       return NIL;
+       }
 
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
+       nlocks = rulelocks->numLocks;
 
-                               return attribute_used(
-                                               (Node *)(exp->args),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up);
-                       }
-                       break;
+       for (i = 0; i < nlocks; i++)
+       {
+               RewriteRule *oneLock = rulelocks->rules[i];
 
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
+               if (oneLock->event == event)
+               {
+                       if (parsetree->commandType != CMD_SELECT ||
+                               (oneLock->attrno == -1 ?
+                                rangeTableEntry_used((Node *) parsetree, varno, 0) :
+                                attribute_used((Node *) parsetree,
+                                                               varno, oneLock->attrno, 0)))
+                               matching_locks = lappend(matching_locks, oneLock);
+               }
+       }
 
-                               return attribute_used(
-                                               (Node *)(iter->iterexpr),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up);
-                       }
-                       break;
+       return matching_locks;
+}
 
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               if (attribute_used(
-                                               (Node *)(ref->refupperindexpr),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (attribute_used(
-                                               (Node *)(ref->reflowerindexpr),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (attribute_used(
-                                               (Node *)(ref->refexpr),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (attribute_used(
-                                               (Node *)(ref->refassgnexpr),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               return FALSE;
-                       }
-                       break;
 
-               case T_Var:
-                       {
-                               Var     *var = (Var *)node;
+static Query *
+ApplyRetrieveRule(Query *parsetree,
+                                 RewriteRule *rule,
+                                 int rt_index,
+                                 bool relation_level,
+                                 Relation relation,
+                                 bool relIsUsed,
+                                 List *activeRIRs)
+{
+       Query      *rule_action;
+       RangeTblEntry *rte,
+                          *subrte;
 
-                               if (var->varlevelsup == sublevels_up)
-                                       return var->varno == rt_index;
-                               else
-                                       return FALSE;
-                       }
-                       break;
+       if (length(rule->actions) != 1)
+               elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
+       if (rule->qual != NULL)
+               elog(ERROR, "ApplyRetrieveRule: can't handle qualified ON SELECT rule");
+       if (!relation_level)
+               elog(ERROR, "ApplyRetrieveRule: can't handle per-attribute ON SELECT rule");
 
-               case T_Param:
-                       return FALSE;
+       /*
+        * Make a modifiable copy of the view query, and recursively expand
+        * any view references inside it.
+        */
+       rule_action = copyObject(lfirst(rule->actions));
 
-               case T_Const:
-                       return FALSE;
+       rule_action = fireRIRrules(rule_action, activeRIRs);
 
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node) {
-                                       if (attribute_used(
-                                                       (Node *)lfirst(l),
-                                                       rt_index,
-                                                       attno,
-                                                       sublevels_up))
-                                               return TRUE;
-                               }
-                               return FALSE;
-                       }
-                       break;
+       /*
+        * VIEWs are really easy --- just plug the view query in as a
+        * subselect, replacing the relation's original RTE.
+        */
+       rte = rt_fetch(rt_index, parsetree->rtable);
 
-               case T_SubLink:
-                       {
-                               SubLink *sub = (SubLink *)node;
-
-                               if (attribute_used(
-                                               (Node *)(sub->lefthand),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (attribute_used(
-                                               (Node *)(sub->subselect),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up + 1))
-                                       return TRUE;
-
-                               return FALSE;
-                       }
-                       break;
+       rte->rtekind = RTE_SUBQUERY;
+       rte->relid = InvalidOid;
+       rte->subquery = rule_action;
+       rte->inh = false;                       /* must not be set for a subquery */
 
-               case T_Query:
-                       {
-                               Query   *qry = (Query *)node;
-
-                               if (attribute_used(
-                                               (Node *)(qry->targetList),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (attribute_used(
-                                               (Node *)(qry->qual),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (attribute_used(
-                                               (Node *)(qry->havingQual),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               if (attribute_used(
-                                               (Node *)(qry->groupClause),
-                                               rt_index,
-                                               attno,
-                                               sublevels_up))
-                                       return TRUE;
-
-                               return FALSE;
-                       }
-                       break;
+       /*
+        * We move the view's permission check data down to its rangetable.
+        * The checks will actually be done against the *OLD* entry therein.
+        */
+       subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
+       Assert(subrte->relid == relation->rd_id);
+       subrte->checkForRead = rte->checkForRead;
+       subrte->checkForWrite = rte->checkForWrite;
+       subrte->checkAsUser = rte->checkAsUser;
 
-               default:
-                       elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
+       rte->checkForRead = false;      /* no permission check on subquery itself */
+       rte->checkForWrite = false;
+       rte->checkAsUser = InvalidOid;
 
+       /*
+        * FOR UPDATE of view?
+        */
+       if (intMember(rt_index, parsetree->rowMarks))
+       {
+               /*
+                * Remove the view from the list of rels that will actually be
+                * marked FOR UPDATE by the executor.  It will still be access-
+                * checked for write access, though.
+                */
+               parsetree->rowMarks = lremovei(rt_index, parsetree->rowMarks);
 
+               /*
+                * Set up the view's referenced tables as if FOR UPDATE.
+                */
+               markQueryForUpdate(rule_action, true);
        }
 
-       return FALSE;
+       return parsetree;
 }
 
-
 /*
- * modifyAggrefUplevel -
- *     In the newly created sublink for an aggregate column used in
- *     the qualification, we must adjust the varlevelsup in all the
- *     var nodes.
+ * Recursively mark all relations used by a view as FOR UPDATE.
+ *
+ * This may generate an invalid query, eg if some sub-query uses an
+ * aggregate.  We leave it to the planner to detect that.
+ *
+ * NB: this must agree with the parser's transformForUpdate() routine.
  */
 static void
-modifyAggrefUplevel(Node *node)
+markQueryForUpdate(Query *qry, bool skipOldNew)
 {
-       if (node == NULL)
-               return;
+       Index           rti = 0;
+       List       *l;
 
-       switch(nodeTag(node)) {
-               case T_TargetEntry:
-                       {
-                               TargetEntry     *tle = (TargetEntry *)node;
+       foreach(l, qry->rtable)
+       {
+               RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
 
-                               modifyAggrefUplevel(
-                                               (Node *)(tle->expr));
-                       }
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref  *aggref = (Aggref *)node;
-
-                               modifyAggrefUplevel(
-                                               (Node *)(aggref->target));
-                       }
-                       break;
-
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
-
-                               modifyAggrefUplevel(
-                                               (Node *)(exp->args));
-                       }
-                       break;
-
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
-
-                               modifyAggrefUplevel(
-                                               (Node *)(iter->iterexpr));
-                       }
-                       break;
-
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               modifyAggrefUplevel(
-                                               (Node *)(ref->refupperindexpr));
-                               modifyAggrefUplevel(
-                                               (Node *)(ref->reflowerindexpr));
-                               modifyAggrefUplevel(
-                                               (Node *)(ref->refexpr));
-                               modifyAggrefUplevel(
-                                               (Node *)(ref->refassgnexpr));
-                       }
-                       break;
-
-               case T_Var:
-                       {
-                               Var     *var = (Var *)node;
-
-                               var->varlevelsup++;
-                       }
-                       break;
-
-               case T_Param:
-                       break;
-
-               case T_Const:
-                       break;
-
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node)
-                                       modifyAggrefUplevel(
-                                                       (Node *)lfirst(l));
-                       }
-                       break;
-
-               case T_SubLink:
-                       {
-                               SubLink *sub = (SubLink *)node;
-
-                               modifyAggrefUplevel(
-                                               (Node *)(sub->lefthand));
-
-                               modifyAggrefUplevel(
-                                               (Node *)(sub->oper));
-
-                               modifyAggrefUplevel(
-                                               (Node *)(sub->subselect));
-                       }
-                       break;
-
-               case T_Query:
-                       {
-                               Query   *qry = (Query *)node;
-
-                               modifyAggrefUplevel(
-                                               (Node *)(qry->targetList));
-
-                               modifyAggrefUplevel(
-                                               (Node *)(qry->qual));
-
-                               modifyAggrefUplevel(
-                                               (Node *)(qry->havingQual));
-
-                               modifyAggrefUplevel(
-                                               (Node *)(qry->groupClause));
-                       }
-                       break;
-
-               default:
-                       elog(NOTICE, "unknown node tag %d in modifyAggrefUplevel()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
-
-
-       }
-}
-
-
-/*
- * modifyAggrefChangeVarnodes -
- *     Change the var nodes in a sublink created for an aggregate column
- *     used in the qualification that is subject of the aggregate
- *     function to point to the correct local RTE.
- */
-static void
-modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up)
-{
-       Node    *node = *nodePtr;
-
-       if (node == NULL)
-               return;
-
-       switch(nodeTag(node)) {
-               case T_TargetEntry:
-                       {
-                               TargetEntry     *tle = (TargetEntry *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(tle->expr)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref  *aggref = (Aggref *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(aggref->target)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_GroupClause:
-                       {
-                               GroupClause     *grp = (GroupClause *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(grp->entry)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(exp->args)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(iter->iterexpr)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(ref->refupperindexpr)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(ref->reflowerindexpr)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(ref->refexpr)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(ref->refassgnexpr)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Var:
-                       {
-                               Var     *var = (Var *)node;
-
-                               if (var->varlevelsup == sublevels_up &&
-                                               var->varno == rt_index) {
-                                       var = copyObject(var);
-                                       var->varno = new_index;
-                                       var->varnoold = new_index;
-                                       var->varlevelsup = 0;
-
-                                       *nodePtr = (Node *)var;
-                               }
-                       }
-                       break;
-
-               case T_Param:
-                       break;
-
-               case T_Const:
-                       break;
-
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node)
-                                       modifyAggrefChangeVarnodes(
-                                                       (Node **)(&lfirst(l)),
-                                                       rt_index,
-                                                       new_index,
-                                                       sublevels_up);
-                       }
-                       break;
-
-               case T_SubLink:
-                       {
-                               SubLink *sub = (SubLink *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(sub->lefthand)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(sub->oper)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(sub->subselect)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up + 1);
-                       }
-                       break;
-
-               case T_Query:
-                       {
-                               Query   *qry = (Query *)node;
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(qry->targetList)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(qry->qual)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(qry->havingQual)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-
-                               modifyAggrefChangeVarnodes(
-                                               (Node **)(&(qry->groupClause)),
-                                               rt_index,
-                                               new_index,
-                                               sublevels_up);
-                       }
-                       break;
-
-               default:
-                       elog(NOTICE, "unknown node tag %d in modifyAggrefChangeVarnodes()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
+               rti++;
 
+               /* Ignore OLD and NEW entries if we are at top level of view */
+               if (skipOldNew &&
+                       (rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
+                       continue;
 
+               if (rte->rtekind == RTE_RELATION)
+               {
+                       if (!intMember(rti, qry->rowMarks))
+                               qry->rowMarks = lappendi(qry->rowMarks, rti);
+                       rte->checkForWrite = true;
+               }
+               else if (rte->rtekind == RTE_SUBQUERY)
+               {
+                       /* FOR UPDATE of subquery is propagated to subquery's rels */
+                       markQueryForUpdate(rte->subquery, false);
+               }
        }
 }
 
 
 /*
- * modifyAggrefDropQual -
- *     remove the pure aggref clase from a qualification
+ * fireRIRonSubLink -
+ *     Apply fireRIRrules() to each SubLink (subselect in expression) found
+ *     in the given tree.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * SubLink nodes in-place.     It is caller's responsibility to ensure that
+ * no unwanted side-effects occur!
+ *
+ * This is unlike most of the other routines that recurse into subselects,
+ * because we must take control at the SubLink node in order to replace
+ * the SubLink's subselect link with the possibly-rewritten subquery.
  */
-static void
-modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr)
+static bool
+fireRIRonSubLink(Node *node, List *activeRIRs)
 {
-       Node    *node = *nodePtr;
-
        if (node == NULL)
-               return;
-
-       switch(nodeTag(node)) {
-               case T_Var:
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref  *aggref = (Aggref *)node;
-                               Aggref  *oaggref = (Aggref *)orignode;
-
-                               modifyAggrefDropQual(
-                                               (Node **)(&(aggref->target)),
-                                               (Node *)(oaggref->target),
-                                               expr);
-                       }
-                       break;
-
-               case T_Param:
-                       break;
-
-               case T_Const:
-                       break;
-
-               case T_GroupClause:
-                       break;
-
-               case T_Expr:
-                       {
-                               Expr    *this_expr = (Expr *)node;
-                               Expr    *orig_expr = (Expr *)orignode;
-
-                               if (orig_expr == expr) {
-                                       Const   *ctrue;
-
-                                       if (expr->typeOid != BOOLOID)
-                                               elog(ERROR,
-                                                       "aggregate expression in qualification isn't of type bool");
-                                       ctrue = makeNode(Const);
-                                       ctrue->consttype = BOOLOID;
-                                       ctrue->constlen = 1;
-                                       ctrue->constisnull = FALSE;
-                                       ctrue->constvalue = (Datum)TRUE;
-                                       ctrue->constbyval = TRUE;
-
-                                       *nodePtr = (Node *)ctrue;
-                               }
-                               else
-                                       modifyAggrefDropQual(
-                                               (Node **)(&(this_expr->args)),
-                                               (Node *)(orig_expr->args),
-                                               expr);
-                       }
-                       break;
-
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
-                               Iter    *oiter = (Iter *)orignode;
-
-                               modifyAggrefDropQual(
-                                               (Node **)(&(iter->iterexpr)),
-                                               (Node *)(oiter->iterexpr),
-                                               expr);
-                       }
-                       break;
-
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-                               ArrayRef        *oref = (ArrayRef *)orignode;
-
-                               modifyAggrefDropQual(
-                                               (Node **)(&(ref->refupperindexpr)),
-                                               (Node *)(oref->refupperindexpr),
-                                               expr);
-                               modifyAggrefDropQual(
-                                               (Node **)(&(ref->reflowerindexpr)),
-                                               (Node *)(oref->reflowerindexpr),
-                                               expr);
-                               modifyAggrefDropQual(
-                                               (Node **)(&(ref->refexpr)),
-                                               (Node *)(oref->refexpr),
-                                               expr);
-                               modifyAggrefDropQual(
-                                               (Node **)(&(ref->refassgnexpr)),
-                                               (Node *)(oref->refassgnexpr),
-                                               expr);
-                       }
-                       break;
-
-               case T_List:
-                       {
-                               List    *l;
-                               List    *ol = (List *)orignode;
-                               int     li = 0;
-
-                               foreach (l, (List *)node) {
-                                       modifyAggrefDropQual(
-                                                       (Node **)(&(lfirst(l))),
-                                                       (Node *)nth(li, ol),
-                                                       expr);
-                                       li++;
-                               }
-                       }
-                       break;
-
-               case T_SubLink:
-                       {
-                               SubLink *sub = (SubLink *)node;
-                               SubLink *osub = (SubLink *)orignode;
-
-                               modifyAggrefDropQual(
-                                               (Node **)(&(sub->subselect)),
-                                               (Node *)(osub->subselect),
-                                               expr);
-                       }
-                       break;
-
-               case T_Query:
-                       {
-                               Query   *qry = (Query *)node;
-                               Query   *oqry = (Query *)orignode;
-
-                               modifyAggrefDropQual(
-                                               (Node **)(&(qry->qual)),
-                                               (Node *)(oqry->qual),
-                                               expr);
-
-                               modifyAggrefDropQual(
-                                               (Node **)(&(qry->havingQual)),
-                                               (Node *)(oqry->havingQual),
-                                               expr);
-                       }
-                       break;
-
-               default:
-                       elog(NOTICE, "unknown node tag %d in modifyAggrefDropQual()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
-
-
-       }
-}
-
-
-/*
- * modifyAggrefMakeSublink -
- *     Create a sublink node for a qualification expression that
- *     uses an aggregate column of a view
- */
-static SubLink *
-modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
-{
-       SubLink         *sublink;
-       Query           *subquery;
-       Node            *subqual;
-       RangeTblEntry   *rte;
-       Aggref          *aggref;
-       Var             *target;
-       TargetEntry     *tle;
-       Resdom          *resdom;
-       Expr            *exp = copyObject(origexp);
-
-       if (nodeTag(nth(0, exp->args)) == T_Aggref)
+               return false;
+       if (IsA(node, SubLink))
        {
-               if (nodeTag(nth(1, exp->args)) == T_Aggref)
-                       elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported");
-               else
-                       elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual");
+               SubLink    *sub = (SubLink *) node;
+
+               /* Do what we came for */
+               sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect,
+                                                                                          activeRIRs);
+               /* Fall through to process lefthand args of SubLink */
        }
 
-       aggref = (Aggref *)nth(1, exp->args);
-       target  = (Var *)(aggref->target);
-       rte     = (RangeTblEntry *)nth(target->varno - 1, parsetree->rtable);
-       tle     = makeNode(TargetEntry);
-       resdom  = makeNode(Resdom);
-
-       aggref->usenulls = TRUE;
-
-       resdom->resno   = 1;
-       resdom->restype = ((Oper *)(exp->oper))->opresulttype;
-       resdom->restypmod = -1;
-       resdom->resname = pstrdup("<noname>");
-       resdom->reskey  = 0;
-       resdom->reskeyop = 0;
-       resdom->resjunk = 0;
-
-       tle->resdom     = resdom;
-       tle->expr       = (Node *)aggref;
-
-       subqual = copyObject(parsetree->qual);
-       modifyAggrefDropQual((Node **)&subqual, (Node *)parsetree->qual, origexp);
-
-       sublink = makeNode(SubLink);
-       sublink->subLinkType    = EXPR_SUBLINK;
-       sublink->useor          = FALSE;
-       sublink->lefthand       = lappend(NIL, copyObject(lfirst(exp->args)));
-       sublink->oper           = lappend(NIL, copyObject(exp));
-       sublink->subselect      = NULL;
-
-       subquery                = makeNode(Query);
-       sublink->subselect      = (Node *)subquery;
-
-       subquery->commandType           = CMD_SELECT;
-       subquery->utilityStmt           = NULL;
-       subquery->resultRelation        = 0;
-       subquery->into                  = NULL;
-       subquery->isPortal              = FALSE;
-       subquery->isBinary              = FALSE;
-       subquery->isTemp                = FALSE;
-       subquery->unionall              = FALSE;
-       subquery->uniqueFlag            = NULL;
-       subquery->sortClause            = NULL;
-       subquery->rtable                = lappend(NIL, rte);
-       subquery->targetList            = lappend(NIL, tle);
-       subquery->qual                  = subqual;
-       subquery->groupClause           = NIL;
-       subquery->havingQual            = NULL;
-       subquery->hasAggs               = TRUE;
-       subquery->hasSubLinks           = FALSE;
-       subquery->unionClause           = NULL;
-
-
-       modifyAggrefUplevel((Node *)sublink);
-
-       modifyAggrefChangeVarnodes((Node **)&(sublink->lefthand), target->varno,
-                       1, target->varlevelsup);
-       modifyAggrefChangeVarnodes((Node **)&(sublink->oper), target->varno,
-                       1, target->varlevelsup);
-       modifyAggrefChangeVarnodes((Node **)&(sublink->subselect), target->varno,
-                       1, target->varlevelsup);
-
-       return sublink;
+       /*
+        * Do NOT recurse into Query nodes, because fireRIRrules already
+        * processed subselects of subselects for us.
+        */
+       return expression_tree_walker(node, fireRIRonSubLink,
+                                                                 (void *) activeRIRs);
 }
 
 
 /*
- * modifyAggrefQual -
- *     Search for qualification expressions that contain aggregate
- *     functions and substiture them by sublinks. These expressions
- *     originally come from qualifications that use aggregate columns
- *     of a view.
+ * fireRIRrules -
+ *     Apply all RIR rules on each rangetable entry in a query
  */
-static void
-modifyAggrefQual(Node **nodePtr, Query *parsetree)
+static Query *
+fireRIRrules(Query *parsetree, List *activeRIRs)
 {
-       Node    *node = *nodePtr;
-
-       if (node == NULL)
-               return;
-
-       switch(nodeTag(node)) {
-               case T_Var:
-                       break;
-
-               case T_Param:
-                       break;
+       int                     rt_index;
 
-               case T_Const:
-                       break;
-
-               case T_GroupClause:
-                       {
-                               GroupClause     *grp = (GroupClause *)node;
-
-                               modifyAggrefQual(
-                                               (Node **)(&(grp->entry)),
-                                               parsetree);
-                       }
-                       break;
-
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
-                               SubLink *sub;
-
-
-                               if (length(exp->args) != 2) {
-                                       modifyAggrefQual(
-                                               (Node **)(&(exp->args)),
-                                               parsetree);
-                                       break;
-                               }
-
-                               if (nodeTag(nth(0, exp->args)) != T_Aggref &&
-                                       nodeTag(nth(1, exp->args)) != T_Aggref) {
-
-                                       modifyAggrefQual(
-                                               (Node **)(&(exp->args)),
-                                               parsetree);
-                                       break;
-                               }
-
-                               sub = modifyAggrefMakeSublink(exp,
-                                               parsetree);
-
-                               *nodePtr = (Node *)sub;
-                               parsetree->hasSubLinks = TRUE;
-                       }
-                       break;
-
-               case T_CaseExpr:
-                       {
-                               /* We're calling recursively,
-                                * and this routine knows how to handle lists
-                                * so let it do the work to handle the WHEN clauses... */
-                               modifyAggrefQual(
-                                               (Node **)(&(((CaseExpr *)node)->args)),
-                                               parsetree);
-
-                               modifyAggrefQual(
-                                               (Node **)(&(((CaseExpr *)node)->defresult)),
-                                               parsetree);
-                       }
-                       break;
-
-               case T_CaseWhen:
-                       {
-                               modifyAggrefQual(
-                                               (Node **)(&(((CaseWhen *)node)->expr)),
-                                               parsetree);
-
-                               modifyAggrefQual(
-                                               (Node **)(&(((CaseWhen *)node)->result)),
-                                               parsetree);
-                       }
-                       break;
-
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
-
-                               modifyAggrefQual(
-                                               (Node **)(&(iter->iterexpr)),
-                                               parsetree);
-                       }
-                       break;
-
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               modifyAggrefQual(
-                                               (Node **)(&(ref->refupperindexpr)),
-                                               parsetree);
-                               modifyAggrefQual(
-                                               (Node **)(&(ref->reflowerindexpr)),
-                                               parsetree);
-                               modifyAggrefQual(
-                                               (Node **)(&(ref->refexpr)),
-                                               parsetree);
-                               modifyAggrefQual(
-                                               (Node **)(&(ref->refassgnexpr)),
-                                               parsetree);
-                       }
-                       break;
-
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node)
-                                       modifyAggrefQual(
-                                                       (Node **)(&(lfirst(l))),
-                                                       parsetree);
-                       }
-                       break;
-
-               case T_SubLink:
-                       {
-                               SubLink *sub = (SubLink *)node;
-
-                               modifyAggrefQual(
-                                               (Node **)(&(sub->subselect)),
-                                               (Query *)(sub->subselect));
-                       }
-                       break;
+       /*
+        * don't try to convert this into a foreach loop, because rtable list
+        * can get changed each time through...
+        */
+       rt_index = 0;
+       while (rt_index < length(parsetree->rtable))
+       {
+               RangeTblEntry *rte;
+               Relation        rel;
+               List       *locks;
+               RuleLock   *rules;
+               RewriteRule *rule;
+               LOCKMODE        lockmode;
+               bool            relIsUsed;
+               int                     i;
 
-               case T_Query:
-                       {
-                               Query   *qry = (Query *)node;
+               ++rt_index;
 
-                               modifyAggrefQual(
-                                               (Node **)(&(qry->qual)),
-                                               parsetree);
+               rte = rt_fetch(rt_index, parsetree->rtable);
 
-                               modifyAggrefQual(
-                                               (Node **)(&(qry->havingQual)),
-                                               parsetree);
-                       }
-                       break;
+               /*
+                * A subquery RTE can't have associated rules, so there's nothing
+                * to do to this level of the query, but we must recurse into the
+                * subquery to expand any rule references in it.
+                */
+               if (rte->rtekind == RTE_SUBQUERY)
+               {
+                       rte->subquery = fireRIRrules(rte->subquery, activeRIRs);
+                       continue;
+               }
 
-               default:
-                       elog(NOTICE, "unknown node tag %d in modifyAggrefQual()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
-
-
-       }
-}
-
-
-static Node *
-FindMatchingTLEntry(List *tlist, char *e_attname)
-{
-       List       *i;
-
-       foreach(i, tlist)
-       {
-               TargetEntry *tle = lfirst(i);
-               char       *resname;
-
-               resname = tle->resdom->resname;
-               if (!strcmp(e_attname, resname))
-                       return (tle->expr);
-       }
-       return NULL;
-}
-
-
-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;
-}
-
-
-static void 
-apply_RIR_adjust_sublevel(Node *node, int sublevels_up)
-{
-       if (node == NULL)
-               return;
-
-       switch(nodeTag(node)) {
-               case T_TargetEntry:
-                       {
-                               TargetEntry     *tle = (TargetEntry *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(tle->expr),
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref  *aggref = (Aggref *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(aggref->target),
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_GroupClause:
-                       {
-                               GroupClause     *grp = (GroupClause *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(grp->entry),
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(exp->args),
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(iter->iterexpr),
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(ref->refupperindexpr),
-                                               sublevels_up);
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(ref->reflowerindexpr),
-                                               sublevels_up);
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(ref->refexpr),
-                                               sublevels_up);
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(ref->refassgnexpr),
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Var:
-                       {
-                               Var     *var = (Var *)node;
-
-                               var->varlevelsup = sublevels_up;
-                       }
-                       break;
-
-               case T_Param:
-                       break;
-
-               case T_Const:
-                       break;
-
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node) {
-                                       apply_RIR_adjust_sublevel(
-                                                       (Node *)lfirst(l),
-                                                       sublevels_up);
-                               }
-                       }
-                       break;
-
-               case T_CaseExpr:
-                       {
-                               CaseExpr        *exp = (CaseExpr *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(exp->args),
-                                               sublevels_up);
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(exp->defresult),
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_CaseWhen:
-                       {
-                               CaseWhen        *exp = (CaseWhen *)node;
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(exp->expr),
-                                               sublevels_up);
-
-                               apply_RIR_adjust_sublevel(
-                                               (Node *)(exp->result),
-                                               sublevels_up);
-                       }
-                       break;
-
-               default:
-                       elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
-
-
-       }
-}
-
-
-static void
-apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, int *modified, int sublevels_up)
-{
-       Node    *node = *nodePtr;
-
-       if (node == NULL)
-               return;
-
-       switch(nodeTag(node)) {
-               case T_TargetEntry:
-                       {
-                               TargetEntry     *tle = (TargetEntry *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(tle->expr)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref  *aggref = (Aggref *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(aggref->target)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_GroupClause:
-                       {
-                               GroupClause     *grp = (GroupClause *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(grp->entry)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(exp->args)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(iter->iterexpr)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(ref->refupperindexpr)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                               apply_RIR_view(
-                                               (Node **)(&(ref->reflowerindexpr)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                               apply_RIR_view(
-                                               (Node **)(&(ref->refexpr)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                               apply_RIR_view(
-                                               (Node **)(&(ref->refassgnexpr)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_Var:
-                       {
-                               Var     *var = (Var *)node;
-
-                               if (var->varlevelsup == sublevels_up &&
-                                               var->varno == rt_index) {
-                                       Node            *exp;
-
-                                       if (var->varattno < 0)
-                                               elog(ERROR, "system column %s not available - %s is a view", get_attname(rte->relid, var->varattno), rte->relname);
-                                       exp = FindMatchingTLEntry(
-                                                       tlist,
-                                                       get_attname(rte->relid,
-                                                               var->varattno));
-
-                                       if (exp == NULL) {
-                                               *nodePtr = make_null(var->vartype);
-                                               return;
-                                       }
-
-                                       exp = copyObject(exp);
-                                       if (var->varlevelsup > 0)
-                                               apply_RIR_adjust_sublevel(exp, var->varlevelsup);
-                                       *nodePtr = exp;
-                                       *modified = TRUE;
-                               }
-                       }
-                       break;
-
-               case T_Param:
-                       break;
-
-               case T_Const:
-                       break;
-
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node)
-                                       apply_RIR_view(
-                                                       (Node **)(&(lfirst(l))),
-                                                       rt_index,
-                                                       rte,
-                                                       tlist,
-                                                       modified,
-                                                       sublevels_up);
-                       }
-                       break;
-
-               case T_SubLink:
-                       {
-                               SubLink *sub = (SubLink *)node;
-                               List *tmp_lefthand, *tmp_oper;
-
-                               apply_RIR_view(
-                                               (Node **)(&(sub->lefthand)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-
-                               apply_RIR_view(
-                                               (Node **)(&(sub->subselect)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up + 1);
-
-                               /***S*I***/
-                               tmp_lefthand = sub->lefthand;                           
-                               foreach(tmp_oper, sub->oper)
-                                 {                                 
-                                   lfirst(((Expr *) lfirst(tmp_oper))->args) = 
-                                     lfirst(tmp_lefthand);
-                                   tmp_lefthand = lnext(tmp_lefthand);
-                                 }                                                             
-                       }
-                       break;
-
-               case T_Query:
-                       {
-                               Query   *qry = (Query *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(qry->targetList)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-
-                               apply_RIR_view(
-                                               (Node **)(&(qry->qual)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-
-                               apply_RIR_view(
-                                               (Node **)(&(qry->havingQual)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-
-                               apply_RIR_view(
-                                               (Node **)(&(qry->groupClause)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_CaseExpr:
-                       {
-                               CaseExpr        *exp = (CaseExpr *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(exp->args)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-
-                               apply_RIR_view(
-                                               (Node **)(&(exp->defresult)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               case T_CaseWhen:
-                       {
-                               CaseWhen        *exp = (CaseWhen *)node;
-
-                               apply_RIR_view(
-                                               (Node **)(&(exp->expr)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-
-                               apply_RIR_view(
-                                               (Node **)(&(exp->result)),
-                                               rt_index,
-                                               rte,
-                                               tlist,
-                                               modified,
-                                               sublevels_up);
-                       }
-                       break;
-
-               default:
-                       elog(NOTICE, "unknown node tag %d in apply_RIR_view()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
-       }
-}
-
-extern void CheckSelectForUpdate(Query *rule_action);  /* in analyze.c */
-
-static void
-ApplyRetrieveRule(Query *parsetree,
-                                 RewriteRule *rule,
-                                 int rt_index,
-                                 int relation_level,
-                                 Relation relation,
-                                 int *modified)
-{
-       Query      *rule_action = NULL;
-       Node       *rule_qual;
-       List       *rtable,
-                          *rt,
-                          *l;
-       int                     nothing,
-                               rt_length;
-       int                     badsql = FALSE;
-
-       rule_qual = rule->qual;
-       if (rule->actions)
-       {
-               if (length(rule->actions) > 1)  /* ??? because we don't handle
-                                                                                * rules with more than one
-                                                                                * action? -ay */
-
-                       return;
-               rule_action = copyObject(lfirst(rule->actions));
-               nothing = FALSE;
-       }
-       else
-               nothing = TRUE;
-
-       rtable = copyObject(parsetree->rtable);
-       foreach(rt, rtable)
-       {
-               RangeTblEntry *rte = lfirst(rt);
+               /*
+                * Joins and other non-relation RTEs can be ignored completely.
+                */
+               if (rte->rtekind != RTE_RELATION)
+                       continue;
 
                /*
-                * this is to prevent add_missing_vars_to_base_rels() from adding
-                * a bogus entry to the new target list.
-                */
-               rte->inFromCl = false;
-       }
-       rt_length = length(rtable);
-
-       rtable = nconc(rtable, copyObject(rule_action->rtable));
-       parsetree->rtable = rtable;
-
-       /* FOR UPDATE of view... */
-       foreach (l, parsetree->rowMark)
-       {
-               if (((RowMark*)lfirst(l))->rti == rt_index)
-                       break;
-       }
-       if (l != NULL)  /* oh, hell -:) */
-       {
-               RowMark    *newrm;
-               Index           rti = 1;
-               List       *l2;
-
-               CheckSelectForUpdate(rule_action);
-               /* 
-                * We believe that rt_index is VIEW - nothing should be
-                * marked for VIEW, but ACL check must be done.
-                * As for real tables of VIEW - their rows must be marked, but
-                * we have to skip ACL check for them.
-                */
-               ((RowMark*)lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE;
-               foreach (l2, rule_action->rtable)
-               {
-                       /*
-                        * RTable of VIEW has two entries of VIEW itself -
-                        * we use relid to skip them.
-                        */
-                       if (relation->rd_id != ((RangeTblEntry*)lfirst(l2))->relid)
-                       {
-                               newrm = makeNode(RowMark);
-                               newrm->rti = rti + rt_length;
-                               newrm->info = ROW_MARK_FOR_UPDATE;
-                               lnext(l) = lcons(newrm, lnext(l));
-                               l = lnext(l);
-                       }
-                       rti++;
-               }
-       }
-
-       rule_action->rtable = rtable;
-       OffsetVarNodes((Node *) rule_qual,   rt_length, 0);
-       OffsetVarNodes((Node *) rule_action, rt_length, 0);
-
-       ChangeVarNodes((Node *) rule_qual, 
-                                  PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
-       ChangeVarNodes((Node *) rule_action,
-                                  PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
-
-       if (relation_level)
-       {
-         apply_RIR_view((Node **) &parsetree, rt_index, 
-                       (RangeTblEntry *)nth(rt_index - 1, rtable),
-                       rule_action->targetList, modified, 0);
-         apply_RIR_view((Node **) &rule_action, rt_index, 
-                       (RangeTblEntry *)nth(rt_index - 1, rtable),
-                       rule_action->targetList, modified, 0);
-       }
-       else
-       {
-         HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
-                                rt_index, rule->attrno, modified, &badsql);
-       }
-       if (*modified && !badsql) {
-         AddQual(parsetree, rule_action->qual);
-         /* This will only work if the query made to the view defined by the following
-          * groupClause groups by the same attributes or does not use group at all! */
-         if (parsetree->groupClause == NULL)
-           parsetree->groupClause=rule_action->groupClause;
-         AddHavingQual(parsetree, rule_action->havingQual);
-         parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
-         parsetree->hasSubLinks = (rule_action->hasSubLinks ||  parsetree->hasSubLinks);
-       }       
-}
-
-
-static void
-fireRIRonSubselect(Node *node)
-{
-       if (node == NULL)
-               return;
-
-       switch(nodeTag(node)) {
-               case T_TargetEntry:
-                       {
-                               TargetEntry     *tle = (TargetEntry *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(tle->expr));
-                       }
-                       break;
-
-               case T_Aggref:
-                       {
-                               Aggref  *aggref = (Aggref *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(aggref->target));
-                       }
-                       break;
-
-               case T_GroupClause:
-                       {
-                               GroupClause     *grp = (GroupClause *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(grp->entry));
-                       }
-                       break;
-
-               case T_Expr:
-                       {
-                               Expr    *exp = (Expr *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(exp->args));
-                       }
-                       break;
-
-               case T_Iter:
-                       {
-                               Iter    *iter = (Iter *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(iter->iterexpr));
-                       }
-                       break;
-
-               case T_ArrayRef:
-                       {
-                               ArrayRef        *ref = (ArrayRef *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(ref->refupperindexpr));
-                               fireRIRonSubselect(
-                                               (Node *)(ref->reflowerindexpr));
-                               fireRIRonSubselect(
-                                               (Node *)(ref->refexpr));
-                               fireRIRonSubselect(
-                                               (Node *)(ref->refassgnexpr));
-                       }
-                       break;
-
-               case T_Var:
-                       break;
-
-               case T_Param:
-                       break;
-
-               case T_Const:
-                       break;
-
-               case T_List:
-                       {
-                               List    *l;
-
-                               foreach (l, (List *)node)
-                                       fireRIRonSubselect(
-                                                       (Node *)(lfirst(l)));
-                       }
-                       break;
-
-               case T_SubLink:
-                       {
-                               SubLink *sub = (SubLink *)node;
-                               Query   *qry;
-
-                               fireRIRonSubselect(
-                                               (Node *)(sub->lefthand));
-
-                               qry = fireRIRrules((Query *)(sub->subselect));
-
-                               fireRIRonSubselect(
-                                               (Node *)qry);
-
-                               sub->subselect = (Node *) qry;
-                       }
-                       break;
-
-               case T_CaseExpr:
-                       {
-                               CaseExpr        *exp = (CaseExpr *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(exp->args));
-
-                               fireRIRonSubselect(
-                                               (Node *)(exp->defresult));
-                       }
-                       break;
-
-               case T_CaseWhen:
-                       {
-                               CaseWhen        *exp = (CaseWhen *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(exp->expr));
-
-                               fireRIRonSubselect(
-                                               (Node *)(exp->result));
-                       }
-                       break;
-
-               case T_Query:
-                       {
-                               Query   *qry = (Query *)node;
-
-                               fireRIRonSubselect(
-                                               (Node *)(qry->targetList));
-
-                               fireRIRonSubselect(
-                                               (Node *)(qry->qual));
-
-                               fireRIRonSubselect(
-                                               (Node *)(qry->havingQual));
-
-                               fireRIRonSubselect(
-                                               (Node *)(qry->groupClause));
-                       }
-                       break;
-
-               default:
-                       elog(NOTICE, "unknown node tag %d in fireRIRonSubselect()", nodeTag(node));
-                       elog(NOTICE, "Node is: %s", nodeToString(node));
-                       break;
-
-
-       }
-}
-
-
-/*
- * fireRIRrules -
- *     Apply all RIR rules on each rangetable entry in a query
- */
-static Query *
-fireRIRrules(Query *parsetree)
-{
-       int             rt_index;
-       RangeTblEntry   *rte;
-       Relation        rel;
-       List            *locks;
-       RuleLock        *rules;
-       RewriteRule     *rule;
-       RewriteRule     RIRonly;
-       int             modified;
-       int             i;
-       List            *l;
-
-       rt_index = 0;
-       while(rt_index < length(parsetree->rtable)) {
-               ++rt_index;
+                * If the table is not referenced in the query, then we ignore it.
+                * This prevents infinite expansion loop due to new rtable entries
+                * inserted by expansion of a rule. A table is referenced if it is
+                * part of the join set (a source table), or is referenced by any
+                * Var nodes, or is the result table.
+                */
+               relIsUsed = rangeTableEntry_used((Node *) parsetree, rt_index, 0);
 
-               if (!rangeTableEntry_used((Node *)parsetree, rt_index, 0))
-                       continue;
-               
-               rte = nth(rt_index - 1, parsetree->rtable);
-               rel = heap_openr(rte->relname);
-               if (rel->rd_rules == NULL) {
-                       heap_close(rel);
+               if (!relIsUsed && rt_index != parsetree->resultRelation)
                        continue;
-               }
 
-               rules = rel->rd_rules;
-               locks = NIL;
+               /*
+                * This may well be the first access to the relation during the
+                * current statement (it will be, if this Query was extracted from
+                * a rule or somehow got here other than via the parser).
+                * Therefore, grab the appropriate lock type for the relation, and
+                * do not release it until end of transaction.  This protects the
+                * rewriter and planner against schema changes mid-query.
+                *
+                * If the relation is the query's result relation, then
+                * RewriteQuery() already got the right lock on it, so we need no
+                * additional lock. Otherwise, check to see if the relation is
+                * accessed FOR UPDATE or not.
+                */
+               if (rt_index == parsetree->resultRelation)
+                       lockmode = NoLock;
+               else if (intMember(rt_index, parsetree->rowMarks))
+                       lockmode = RowShareLock;
+               else
+                       lockmode = AccessShareLock;
+
+               rel = heap_open(rte->relid, lockmode);
 
                /*
                 * Collect the RIR rules that we must apply
                 */
-               for (i = 0; i < rules->numLocks; i++) {
+               rules = rel->rd_rules;
+               if (rules == NULL)
+               {
+                       heap_close(rel, NoLock);
+                       continue;
+               }
+               locks = NIL;
+               for (i = 0; i < rules->numLocks; i++)
+               {
                        rule = rules->rules[i];
                        if (rule->event != CMD_SELECT)
                                continue;
-                       
-                       if (rule->attrno > 0 &&
-                                       !attribute_used((Node *)parsetree,
-                                                       rt_index,
-                                                       rule->attrno, 0))
-                               continue;
+
+                       if (rule->attrno > 0)
+                       {
+                               /* per-attr rule; do we need it? */
+                               if (!attribute_used((Node *) parsetree, rt_index,
+                                                                       rule->attrno, 0))
+                                       continue;
+                       }
 
                        locks = lappend(locks, rule);
                }
 
                /*
-                * Check permissions
+                * If we found any, apply them --- but first check for recursion!
                 */
-               checkLockPerms(locks, parsetree, rt_index);
+               if (locks != NIL)
+               {
+                       List       *newActiveRIRs;
+                       List       *l;
 
-               /*
-                * Now apply them
-                */
-               foreach (l, locks) {
-                       rule = lfirst(l);
-
-                       RIRonly.event   = rule->event;
-                       RIRonly.attrno  = rule->attrno;
-                       RIRonly.qual    = rule->qual;
-                       RIRonly.actions = rule->actions;
-
-                       ApplyRetrieveRule(parsetree,
-                                       &RIRonly,
-                                       rt_index,
-                                       RIRonly.attrno == -1,
-                                       rel,
-                                       &modified);
+                       if (oidMember(RelationGetRelid(rel), activeRIRs))
+                               elog(ERROR, "Infinite recursion detected in rules for relation %s",
+                                        RelationGetRelationName(rel));
+                       newActiveRIRs = lconso(RelationGetRelid(rel), activeRIRs);
+
+                       foreach(l, locks)
+                       {
+                               rule = lfirst(l);
+
+                               parsetree = ApplyRetrieveRule(parsetree,
+                                                                                         rule,
+                                                                                         rt_index,
+                                                                                         rule->attrno == -1,
+                                                                                         rel,
+                                                                                         relIsUsed,
+                                                                                         newActiveRIRs);
+                       }
                }
 
-               heap_close(rel);
+               heap_close(rel, NoLock);
        }
 
-       fireRIRonSubselect((Node *) parsetree);
-       modifyAggrefQual((Node **) &(parsetree->qual), parsetree);
+       /*
+        * Recurse into sublink subqueries, too.  But we already did the ones
+        * in the rtable.
+        */
+       if (parsetree->hasSubLinks)
+               query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs,
+                                                 QTW_IGNORE_RT_SUBQUERIES);
+
+       /*
+        * If the query was marked having aggregates, check if this is still
+        * true after rewriting.  Ditto for sublinks.  Note there should be no
+        * aggs in the qual at this point.      (Does this code still do anything
+        * useful?      The view-becomes-subselect-in-FROM approach doesn't look
+        * like it could remove aggs or sublinks...)
+        */
+       if (parsetree->hasAggs)
+       {
+               parsetree->hasAggs = checkExprHasAggs((Node *) parsetree);
+               if (parsetree->hasAggs)
+                       if (checkExprHasAggs((Node *) parsetree->jointree))
+                               elog(ERROR, "fireRIRrules: failed to remove aggs from qual");
+       }
+       if (parsetree->hasSubLinks)
+               parsetree->hasSubLinks = checkExprHasSubLink((Node *) parsetree);
 
        return parsetree;
 }
 
 
 /*
- * idea is to fire regular rules first, then qualified instead
- * rules and unqualified instead rules last. Any lemming is counted for.
+ * Modify the given query by adding 'AND rule_qual IS NOT TRUE' to its
+ * qualification.  This is used to generate suitable "else clauses" for
+ * conditional INSTEAD rules.  (Unfortunately we must use "x IS NOT TRUE",
+ * not just "NOT x" which the planner is much smarter about, else we will
+ * do the wrong thing when the qual evaluates to NULL.)
+ *
+ * The rule_qual may contain references to OLD or NEW. OLD references are
+ * replaced by references to the specified rt_index (the relation that the
+ * rule applies to).  NEW references are only possible for INSERT and UPDATE
+ * queries on the relation itself, and so they should be replaced by copies
+ * of the related entries in the query's own targetlist.
  */
-static List *
-orderRules(List *locks)
-{
-       List       *regular = NIL;
-       List       *instead_rules = NIL;
-       List       *instead_qualified = NIL;
-       List       *i;
-
-       foreach(i, locks)
-       {
-               RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
-
-               if (rule_lock->isInstead)
-               {
-                       if (rule_lock->qual == NULL)
-                               instead_rules = lappend(instead_rules, rule_lock);
-                       else
-                               instead_qualified = lappend(instead_qualified, rule_lock);
-               }
-               else
-                       regular = lappend(regular, rule_lock);
-       }
-       regular = nconc(regular, instead_qualified);
-       return nconc(regular, instead_rules);
-}
-
-
-
 static Query *
-CopyAndAddQual(Query *parsetree,
-                          List *actions,
-                          Node *rule_qual,
-                          int rt_index,
-                          CmdType event)
+CopyAndAddInvertedQual(Query *parsetree,
+                                          Node *rule_qual,
+                                          int rt_index,
+                                          CmdType event)
 {
        Query      *new_tree = (Query *) copyObject(parsetree);
-       Node       *new_qual = NULL;
-       Query      *rule_action = NULL;
-
-       if (actions)
-               rule_action = lfirst(actions);
-       if (rule_qual != NULL)
-               new_qual = (Node *) copyObject(rule_qual);
-       if (rule_action != NULL)
-       {
-               List       *rtable;
-               int                     rt_length;
-
-               rtable = new_tree->rtable;
-               rt_length = length(rtable);
-               rtable = nconc(rtable, copyObject(rule_action->rtable));
-               new_tree->rtable = rtable;
-               OffsetVarNodes(new_qual, rt_length, 0);
-               ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
-       }
-       /* XXX -- where current doesn't work for instead nothing.... yet */
-       AddNotQual(new_tree, new_qual);
+       Node       *new_qual = (Node *) copyObject(rule_qual);
+
+       /* Fix references to OLD */
+       ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
+       /* Fix references to NEW */
+       if (event == CMD_INSERT || event == CMD_UPDATE)
+               new_qual = ResolveNew(new_qual,
+                                                         PRS2_NEW_VARNO,
+                                                         0,
+                                                         parsetree->targetList,
+                                                         event,
+                                                         rt_index);
+       /* And attach the fixed qual */
+       AddInvertedQual(new_tree, new_qual);
 
        return new_tree;
 }
 
 
-
 /*
  *     fireRules -
  *        Iterate through rule locks applying rules.
- *        All rules create their own parsetrees. Instead rules
- *        with rule qualification save the original parsetree
- *        and add their negated qualification to it. Real instead
- *        rules finally throw away the original parsetree.
  *
- *        remember: reality is for dead birds -- glass
+ * Input arguments:
+ *     parsetree - original query
+ *     rt_index - RT index of result relation in original query
+ *     event - type of rule event
+ *     locks - list of rules to fire
+ * Output arguments:
+ *     *instead_flag - set TRUE if any unqualified INSTEAD rule is found
+ *                                     (must be initialized to FALSE)
+ *     *qual_product - filled with modified original query if any qualified
+ *                                     INSTEAD rule is found (must be initialized to NULL)
+ * Return value:
+ *     list of rule actions adjusted for use with this query
  *
+ * Qualified INSTEAD rules generate their action with the qualification
+ * condition added.  They also generate a modified version of the original
+ * query with the negated qualification added, so that it will run only for
+ * rows that the qualified action doesn't act on.  (If there are multiple
+ * qualified INSTEAD rules, we AND all the negated quals onto a single
+ * modified original query.)  We won't execute the original, unmodified
+ * query if we find either qualified or unqualified INSTEAD rules.  If
+ * we find both, the modified original query is discarded too.
  */
 static List *
 fireRules(Query *parsetree,
                  int rt_index,
                  CmdType event,
-                 bool *instead_flag,
                  List *locks,
-                 List **qual_products)
+                 bool *instead_flag,
+                 Query **qual_product)
 {
-       RewriteInfo *info;
        List       *results = NIL;
        List       *i;
 
-       /* choose rule to fire from list of rules */
-       if (locks == NIL)
-       {
-               return NIL;
-       }
-
-       locks = orderRules(locks);      /* real instead rules last */
        foreach(i, locks)
        {
                RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
-               Node       *qual,
-                                  *event_qual;
-               List       *actions;
+               Node       *event_qual = rule_lock->qual;
+               List       *actions = rule_lock->actions;
+               QuerySource     qsrc;
                List       *r;
 
-               /*
-                * Instead rules change the resultRelation of the query. So the
-                * permission checks on the initial resultRelation would never be
-                * done (this is normally done in the executor deep down). So we
-                * must do it here. The result relations resulting from earlier
-                * rewrites are already checked against the rules eventrelation
-                * owner (during matchLocks) and have the skipAcl flag set.
-                */
-               if (rule_lock->isInstead &&
-                       parsetree->commandType != CMD_SELECT)
+               /* Determine correct QuerySource value for actions */
+               if (rule_lock->isInstead)
                {
-                       RangeTblEntry *rte;
-                       int32           acl_rc;
-                       int32           reqperm;
-
-                       switch (parsetree->commandType)
-                       {
-                               case CMD_INSERT:
-                                       reqperm = ACL_AP;
-                                       break;
-                               default:
-                                       reqperm = ACL_WR;
-                                       break;
-                       }
-
-                       rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
-                                                                               parsetree->rtable);
-                       if (!rte->skipAcl)
+                       if (event_qual != NULL)
+                               qsrc = QSRC_QUAL_INSTEAD_RULE;
+                       else
                        {
-                               acl_rc = pg_aclcheck(rte->relname,
-                                                                        GetPgUserName(), reqperm);
-                               if (acl_rc != ACLCHECK_OK)
-                               {
-                                       elog(ERROR, "%s: %s",
-                                                rte->relname,
-                                                aclcheck_error_strings[acl_rc]);
-                               }
+                               qsrc = QSRC_INSTEAD_RULE;
+                               *instead_flag = true; /* report unqualified INSTEAD */
                        }
                }
+               else
+                       qsrc = QSRC_NON_INSTEAD_RULE;
 
-               /* multiple rule action time */
-               *instead_flag = rule_lock->isInstead;
-               event_qual = rule_lock->qual;
-               actions = rule_lock->actions;
-               if (event_qual != NULL && *instead_flag)
+               if (qsrc == QSRC_QUAL_INSTEAD_RULE)
                {
-                       Query      *qual_product;
-                       RewriteInfo qual_info;
-
-                       /* ----------
-                        * If there are instead rules with qualifications,
-                        * the original query is still performed. But all
-                        * the negated rule qualifications of the instead
-                        * rules are added so it does it's actions only
-                        * in cases where the rule quals of all instead
-                        * rules are false. Think of it as the default
-                        * action in a case. We save this in *qual_products
-                        * so deepRewriteQuery() can add it to the query
-                        * list after we mangled it up enough.
-                        * ----------
+                       /*
+                        * If there are INSTEAD rules with qualifications, the
+                        * original query is still performed. But all the negated rule
+                        * qualifications of the INSTEAD rules are added so it does
+                        * its actions only in cases where the rule quals of all
+                        * INSTEAD rules are false. Think of it as the default action
+                        * in a case. We save this in *qual_product so
+                        * RewriteQuery() can add it to the query list after we
+                        * mangled it up enough.
+                        *
+                        * If we have already found an unqualified INSTEAD rule,
+                        * then *qual_product won't be used, so don't bother building it.
                         */
-                       if (*qual_products == NIL)
-                               qual_product = parsetree;
-                       else
-                               qual_product = (Query *) nth(0, *qual_products);
-
-                       qual_info.event = qual_product->commandType;
-                       qual_info.new_varno = length(qual_product->rtable) + 2;
-                       qual_product = CopyAndAddQual(qual_product,
-                                                                                 actions,
-                                                                                 event_qual,
-                                                                                 rt_index,
-                                                                                 event);
-
-                       qual_info.rule_action = qual_product;
-
-                       if (event == CMD_INSERT || event == CMD_UPDATE)
-                               FixNew(&qual_info, qual_product);
-
-                       *qual_products = lappend(NIL, qual_product);
+                       if (! *instead_flag)
+                       {
+                               if (*qual_product == NULL)
+                                       *qual_product = parsetree;
+                               *qual_product = CopyAndAddInvertedQual(*qual_product,
+                                                                                                          event_qual,
+                                                                                                          rt_index,
+                                                                                                          event);
+                       }
                }
 
+               /* Now process the rule's actions and add them to the result list */
                foreach(r, actions)
                {
                        Query      *rule_action = lfirst(r);
-                       Node       *rule_qual = copyObject(event_qual);
 
                        if (rule_action->commandType == CMD_NOTHING)
                                continue;
 
-                       /*--------------------------------------------------
-                        * We copy the qualifications of the parsetree
-                        * to the action and vice versa. So force
-                        * hasSubLinks if one of them has it.
-                        *
-                        * As of 6.4 only parsetree qualifications can
-                        * have sublinks. If this changes, we must make
-                        * this a node lookup at the end of rewriting.
-                        *
-                        * Jan
-                        *--------------------------------------------------
-                        */
-                       if (parsetree->hasSubLinks && !rule_action->hasSubLinks)
-                       {
-                               rule_action = copyObject(rule_action);
-                               rule_action->hasSubLinks = TRUE;
-                       }
-                       if (!parsetree->hasSubLinks && rule_action->hasSubLinks)
-                       {
-                               parsetree->hasSubLinks = TRUE;
-                       }
-
-                       /*--------------------------------------------------
-                        * Step 1:
-                        *        Rewrite current.attribute or current to tuple variable
-                        *        this appears to be done in parser?
-                        *--------------------------------------------------
-                        */
-                       info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
-                                                                        rt_index, event, instead_flag);
-
-                       /* handle escapable cases, or those handled by other code */
-                       if (info->nothing)
-                       {
-                               if (*instead_flag)
-                                       return NIL;
-                               else
-                                       continue;
-                       }
-
-                       if (info->action == info->event &&
-                               info->event == CMD_SELECT)
-                               continue;
-
-                       /*
-                        * Event Qualification forces copying of parsetree and
-                        * splitting into two queries one w/rule_qual, one w/NOT
-                        * rule_qual. Also add user query qual onto rule action
-                        */
-                       qual = parsetree->qual;
-                       AddQual(info->rule_action, qual);
-
-                       if (info->rule_qual != NULL)
-                               AddQual(info->rule_action, info->rule_qual);
-
-                       /*--------------------------------------------------
-                        * Step 2:
-                        *        Rewrite new.attribute w/ right hand side of target-list
-                        *        entry for appropriate field name in insert/update
-                        *--------------------------------------------------
-                        */
-                       if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
-                               FixNew(info, parsetree);
+                       rule_action = rewriteRuleAction(parsetree, rule_action,
+                                                                                       event_qual, rt_index, event);
 
-                       /*--------------------------------------------------
-                        * Step 3:
-                        *        rewriting due to retrieve rules
-                        *--------------------------------------------------
-                        */
-                       info->rule_action->rtable = info->rt;
-                       /*
-                       ProcessRetrieveQuery(info->rule_action, info->rt,
-                                                                &orig_instead_flag, TRUE);
-                       */
-
-                       /*--------------------------------------------------
-                        * Step 4
-                        *        Simplify? hey, no algorithm for simplification... let
-                        *        the planner do it.
-                        *--------------------------------------------------
-                        */
-                       results = lappend(results, info->rule_action);
+                       rule_action->querySource = qsrc;
+                       rule_action->canSetTag = false;         /* might change later */
 
-                       pfree(info);
+                       results = lappend(results, rule_action);
                }
-
-               /* ----------
-                * If this was an unqualified instead rule,
-                * throw away an eventually saved 'default' parsetree
-                * ----------
-                */
-               if (event_qual == NULL && *instead_flag)
-                       *qual_products = NIL;
        }
+
        return results;
 }
 
 
-
+/*
+ * RewriteQuery -
+ *       rewrites the query and apply the rules again on the queries rewritten
+ *
+ * rewrite_events is a list of open query-rewrite actions, so we can detect
+ * infinite recursion.
+ */
 static List *
-RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
+RewriteQuery(Query *parsetree, List *rewrite_events)
 {
-       CmdType         event;
-       List            *product_queries = NIL;
-       int             result_relation = 0;
-       RangeTblEntry   *rt_entry;
-       Relation        rt_entry_relation = NULL;
-       RuleLock        *rt_entry_locks = NULL;
-
-       Assert(parsetree != NULL);
-
-       event = parsetree->commandType;
-
-       /*
-        * SELECT rules are handled later when we have all the
-        * queries that should get executed
-        */
-       if (event == CMD_SELECT)
-               return NIL;
-
-       /*
-        * Utilities aren't rewritten at all - why is this here?
-        */
-       if (event == CMD_UTILITY)
-               return NIL;
-
-       /*
-        * only for a delete may the targetlist be NULL
-        */
-       if (event != CMD_DELETE)
-               Assert(parsetree->targetList != NULL);
-
-       result_relation = parsetree->resultRelation;
+       CmdType         event = parsetree->commandType;
+       bool            instead = false;
+       Query      *qual_product = NULL;
+       List       *rewritten = NIL;
 
        /*
-        * the statement is an update, insert or delete - fire rules
-        * on it.
+        * If the statement is an update, insert or delete - fire rules on it.
+        *
+        * SELECT rules are handled later when we have all the queries that
+        * should get executed.  Also, utilities aren't rewritten at all
+        * (do we still need that check?)
         */
-       rt_entry = rt_fetch(result_relation, parsetree->rtable);
-       rt_entry_relation = heap_openr(rt_entry->relname);
-       rt_entry_locks = rt_entry_relation->rd_rules;
-       heap_close(rt_entry_relation);
-
-       if (rt_entry_locks != NULL)
+       if (event != CMD_SELECT && event != CMD_UTILITY)
        {
-               List       *locks = matchLocks(event, rt_entry_locks, result_relation, parsetree);
-
-               product_queries = fireRules(parsetree,
-                                         result_relation,
-                                         event,
-                                         instead_flag,
-                                         locks,
-                                         qual_products);
-       }
+               int                     result_relation;
+               RangeTblEntry *rt_entry;
+               Relation        rt_entry_relation;
+               List       *locks;
 
-       return product_queries;
+               result_relation = parsetree->resultRelation;
+               Assert(result_relation != 0);
+               rt_entry = rt_fetch(result_relation, parsetree->rtable);
+               Assert(rt_entry->rtekind == RTE_RELATION);
 
-}
+               /*
+                * This may well be the first access to the result relation during the
+                * current statement (it will be, if this Query was extracted from a
+                * rule or somehow got here other than via the parser). Therefore,
+                * grab the appropriate lock type for a result relation, and do not
+                * release it until end of transaction.  This protects the rewriter
+                * and planner against schema changes mid-query.
+                */
+               rt_entry_relation = heap_open(rt_entry->relid, RowExclusiveLock);
 
+               /*
+                * If it's an INSERT or UPDATE, rewrite the targetlist into standard
+                * form.  This will be needed by the planner anyway, and doing it now
+                * ensures that any references to NEW.field will behave sanely.
+                */
+               if (event == CMD_INSERT || event == CMD_UPDATE)
+                       rewriteTargetList(parsetree, rt_entry_relation);
 
-/*
- * to avoid infinite recursion, we restrict the number of times a query
- * can be rewritten. Detecting cycles is left for the reader as an excercise.
- */
-#ifndef REWRITE_INVOKE_MAX
-#define REWRITE_INVOKE_MAX             10
-#endif
+               /*
+                * Collect and apply the appropriate rules.
+                */
+               locks = matchLocks(event, rt_entry_relation->rd_rules,
+                                                  result_relation, parsetree);
 
-static int     numQueryRewriteInvoked = 0;
+               if (locks != NIL)
+               {
+                       List       *product_queries;
 
-/*
- * deepRewriteQuery -
- *       rewrites the query and apply the rules again on the queries rewritten
- */
-static List *
-deepRewriteQuery(Query *parsetree)
-{
-       List       *n;
-       List       *rewritten = NIL;
-       List       *result = NIL;
-       bool            instead;
-       List       *qual_products = NIL;
+                       product_queries = fireRules(parsetree,
+                                                                               result_relation,
+                                                                               event,
+                                                                               locks,
+                                                                               &instead,
+                                                                               &qual_product);
 
+                       /*
+                        * If we got any product queries, recursively rewrite them
+                        * --- but first check for recursion!
+                        */
+                       if (product_queries != NIL)
+                       {
+                               List       *n;
+                               rewrite_event *rev;
 
+                               foreach(n, rewrite_events)
+                               {
+                                       rev = (rewrite_event *) lfirst(n);
+                                       if (rev->relation == RelationGetRelid(rt_entry_relation) &&
+                                               rev->event == event)
+                                               elog(ERROR, "Infinite recursion detected in rules for relation %s",
+                                                        RelationGetRelationName(rt_entry_relation));
+                               }
 
-       if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
-       {
-               elog(ERROR, "query rewritten %d times, may contain cycles",
-                        numQueryRewriteInvoked - 1);
-       }
+                               rev = (rewrite_event *) palloc(sizeof(rewrite_event));
+                               rev->relation = RelationGetRelid(rt_entry_relation);
+                               rev->event = event;
+                               rewrite_events = lcons(rev, rewrite_events);
 
-       instead = FALSE;
-       result = RewriteQuery(parsetree, &instead, &qual_products);
+                               foreach(n, product_queries)
+                               {
+                                       Query      *pt = (Query *) lfirst(n);
+                                       List       *newstuff;
 
-       foreach(n, result)
-       {
-               Query      *pt = lfirst(n);
-               List       *newstuff = NIL;
+                                       newstuff = RewriteQuery(pt, rewrite_events);
+                                       rewritten = nconc(rewritten, newstuff);
+                               }
+                       }
+               }
 
-               newstuff = deepRewriteQuery(pt);
-               if (newstuff != NIL)
-                       rewritten = nconc(rewritten, newstuff);
+               heap_close(rt_entry_relation, NoLock);          /* keep lock! */
        }
 
-       /* ----------
-        * qual_products are the original query with the negated
-        * rule qualification of an instead rule
-        * ----------
-        */
-       if (qual_products != NIL)
-               rewritten = nconc(rewritten, qual_products);
-
-       /* ----------
-        * The original query is appended last if not instead
-        * because update and delete rule actions might not do
-        * anything if they are invoked after the update or
-        * delete is performed. The command counter increment
-        * between the query execution makes the deleted (and
-        * maybe the updated) tuples disappear so the scans
-        * for them in the rule actions cannot find them.
-        * ----------
-        */
-       if (!instead)
-               rewritten = lappend(rewritten, parsetree);
-
-       return rewritten;
-}
-
-
-/*
- * QueryOneRewrite -
- *       rewrite one query
- */
-static List *
-QueryRewriteOne(Query *parsetree)
-{
-       numQueryRewriteInvoked = 0;
-
        /*
-        * take a deep breath and apply all the rewrite rules - ay
-        */
-       return deepRewriteQuery(parsetree);
-}
-
-
-/* ----------
- * RewritePreprocessQuery -
- *     adjust details in the parsetree, the rule system
- *     depends on
- * ----------
- */
-static void
-RewritePreprocessQuery(Query *parsetree)
-{
-       /* ----------
-        * if the query has a resultRelation, reassign the
-        * result domain numbers to the attribute numbers in the
-        * target relation. FixNew() depends on it when replacing
-        * *new* references in a rule action by the expressions
-        * from the rewritten query.
-        * ----------
+        * For INSERTs, the original query is done first; for UPDATE/DELETE,
+        * it is done last.  This is needed because update and delete rule
+        * actions might not do anything if they are invoked after the update
+        * or delete is performed. The command counter increment between the
+        * query executions makes the deleted (and maybe the updated) tuples
+        * disappear so the scans for them in the rule actions cannot find
+        * them.
+        *
+        * If we found any unqualified INSTEAD, the original query is not
+        * done at all, in any form.  Otherwise, we add the modified form
+        * if qualified INSTEADs were found, else the unmodified form.
         */
-       if (parsetree->resultRelation > 0)
+       if (!instead)
        {
-               RangeTblEntry *rte;
-               Relation        rd;
-               List       *tl;
-               TargetEntry *tle;
-               int                     resdomno;
-
-               rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
-                                                                       parsetree->rtable);
-               rd = heap_openr(rte->relname);
-
-               foreach(tl, parsetree->targetList)
+               if (parsetree->commandType == CMD_INSERT)
                {
-                       tle = (TargetEntry *) lfirst(tl);
-                       resdomno = attnameAttNum(rd, tle->resdom->resname);
-                       tle->resdom->resno = resdomno;
+                       if (qual_product != NULL)
+                               rewritten = lcons(qual_product, rewritten);
+                       else
+                               rewritten = lcons(parsetree, rewritten);
+               }
+               else
+               {
+                       if (qual_product != NULL)
+                               rewritten = lappend(rewritten, qual_product);
+                       else
+                               rewritten = lappend(rewritten, parsetree);
                }
-
-               heap_close(rd);
        }
+
+       return rewritten;
 }
 
 
 /*
  * QueryRewrite -
- *       rewrite one query via query rewrite system, possibly returning 0
- *       or many queries
+ *       Primary entry point to the query rewriter.
+ *       Rewrite one query via query rewrite system, possibly returning 0
+ *       or many queries.
+ *
+ * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was
+ * moved here so that it would be invoked during EXPLAIN.
  */
 List *
 QueryRewrite(Query *parsetree)
 {
-       List            *querylist;
-       List            *results = NIL;
-       List            *l;
-       Query           *query;
+       List       *querylist;
+       List       *results = NIL;
+       List       *l;
+       CmdType         origCmdType;
+       bool            foundOriginalQuery;
+       Query      *lastInstead;
 
        /*
         * Step 1
         *
-        * There still seems something broken with the resdom numbers
-        * so we reassign them first.
-        */
-       RewritePreprocessQuery(parsetree);
-
-       /*
-        * Step 2
-        *
         * Apply all non-SELECT rules possibly getting 0 or many queries
         */
-       querylist = QueryRewriteOne(parsetree);
+       querylist = RewriteQuery(parsetree, NIL);
 
        /*
-        * Step 3
+        * Step 2
         *
         * Apply all the RIR rules on each query
         */
-       foreach (l, querylist) {
-               query = (Query *)lfirst(l);
-               results = lappend(results, fireRIRrules(query));
-       }
-       return results;
-}
-/***S*I***/
-/* This function takes two targetlists as arguments and checks if the targetlists are compatible
- * (i.e. both select for the same number of attributes and the types are compatible 
- */
-void check_targetlists_are_compatible(List *prev_target, List *current_target)
-{
-  List *next_target;
-  
-  if (length(prev_target) != 
-      length(current_target))
-    elog(ERROR,"Each UNION | EXCEPT | INTERSECT query must have the same number of columns.");               
-  foreach(next_target, current_target)
-    {
-      Oid                      itype;
-      Oid                      otype;
-      
-      otype = ((TargetEntry *) lfirst(prev_target))->resdom->restype;
-      itype = ((TargetEntry *) lfirst(next_target))->resdom->restype;
-             
-      /* one or both is a NULL column? then don't convert... */
-      if (otype == InvalidOid)
-       {
-         /* propagate a known type forward, if available */
-         if (itype != InvalidOid)
-           ((TargetEntry *) lfirst(prev_target))->resdom->restype = itype;
-#ifdef NOT_USED
-         else
-           {
-             ((TargetEntry *) lfirst(prev_target))->resdom->restype = UNKNOWNOID;
-             ((TargetEntry *) lfirst(next_target))->resdom->restype = UNKNOWNOID;
-           }
-#endif
-       }
-      else if (itype == InvalidOid)
-       {
-       }
-      /* they don't match in type? then convert... */
-      else if (itype != otype)
-       {
-         Node     *expr;
-         
-         expr = ((TargetEntry *) lfirst(next_target))->expr;
-         expr = CoerceTargetExpr(NULL, expr, itype, otype);
-         if (expr == NULL)
-           {
-             elog(ERROR, "Unable to transform %s to %s"
-                  "\n\tEach UNION | EXCEPT | INTERSECT clause must have compatible target types",
-                  typeidTypeName(itype),
-                  typeidTypeName(otype));
-           }
-         ((TargetEntry *) lfirst(next_target))->expr = expr;
-         ((TargetEntry *) lfirst(next_target))->resdom->restype = otype;
-       }
-             
-      /* both are UNKNOWN? then evaluate as text... */
-      else if (itype == UNKNOWNOID)
+       foreach(l, querylist)
        {
-         ((TargetEntry *) lfirst(next_target))->resdom->restype = TEXTOID;
-         ((TargetEntry *) lfirst(prev_target))->resdom->restype = TEXTOID;
-       }
-      prev_target = lnext(prev_target);
-    }
-}
+               Query      *query = (Query *) lfirst(l);
 
-/***S*I***/
-/* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent
- * queries that use IN and NOT IN subselects. 
- * 
- * The operator tree is attached to 'intersectClause' (see rule
- * 'SelectStmt' in gram.y) of the 'parsetree' given as an
- * argument. First we remember some clauses (the sortClause, the
- * unique flag etc.)  Then we translate the operator tree to DNF
- * (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces
- * CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr()
- * earlier we get DNF after exchanging ANDs and ORs again in the
- * result.) Now we create a new query by evaluating the new operator
- * tree which is in DNF now. For every AND we create an entry in the
- * union list and for every OR we create an IN subselect. (NOT IN
- * subselects are created for OR NOT nodes). The first entry of the
- * union list is handed back but before that the remembered clauses
- * (sortClause etc) are attached to the new top Node (Note that the 
- * new top Node can differ from the parsetree given as argument because of
- * the translation to DNF. That's why we have to remember the sortClause or
- * unique flag!) */
-Query *
-Except_Intersect_Rewrite (Query *parsetree)
-{
-  SubLink *n;
-  Query          *result, *intersect_node;
-  List   *elist, *intersect_list = NIL, *intersect, *intersectClause;
-  List   *union_list = NIL, *sortClause;  
-  List   *left_expr, *right_expr, *resnames = NIL;
-  char   *op, *uniqueFlag, *into;
-  bool   isBinary, isPortal, isTemp;  
-  CmdType commandType = CMD_SELECT;
-  List   *rtable_insert = NIL;  
-
-  List   *prev_target = NIL;
-
-  /* Remember the Resnames of the given parsetree's targetlist
-   * (these are the resnames of the first Select Statement of 
-   * the query formulated by the user and he wants the columns
-   * named by these strings. The transformation to DNF can
-   * cause another Select Statment to be the top one which
-   * uses other names for its columns. Therefore we remeber
-   * the original names and attach them to the targetlist
-   * of the new topmost Node at the end of this function */
-  foreach(elist, parsetree->targetList)
-    {
-      TargetEntry *tent = (TargetEntry *)lfirst(elist);
-      
-      resnames = lappend(resnames, tent->resdom->resname);     
-    }
-  
-  /* If the Statement is an INSERT INTO ... (SELECT...) statement
-   * using UNIONs, INTERSECTs or EXCEPTs and the transformation
-   * to DNF makes another Node to the top node we have to transform
-   * the new top node to an INSERT node and the original INSERT node
-   * to a SELECT node */
-  if (parsetree->commandType == CMD_INSERT) 
-    {
-      parsetree->commandType = CMD_SELECT;
-      commandType = CMD_INSERT;
-      parsetree->resultRelation         = 0;
-      
-      /* The result relation ( = the one to insert into) has to be
-       * attached to the rtable list of the new top node */
-      rtable_insert = nth(length(parsetree->rtable) - 1, parsetree->rtable);    
-    }
-    
-  /* Save some items, to be able to attach them to the resulting top node
-   * at the end of the function */
-  sortClause = parsetree->sortClause;
-  uniqueFlag = parsetree->uniqueFlag;
-  into = parsetree->into;
-  isBinary = parsetree->isBinary;
-  isPortal = parsetree->isPortal;  
-  isTemp = parsetree->isTemp;
-
-  /* The operator tree attached to parsetree->intersectClause is still 'raw'
-   * ( = the leaf nodes are still SelectStmt nodes instead of Query nodes)
-   * So step through the tree and transform the nodes using parse_analyze().
-   *
-   * The parsetree (given as an argument to
-   * Except_Intersect_Rewrite()) has already been transformed and
-   * transforming it again would cause troubles.  So we give the 'raw'
-   * version (of the cooked parsetree) to the function to
-   * prevent an additional transformation. Instead we hand back the
-   * 'cooked' version also given as an argument to
-   * intersect_tree_analyze() */
-  intersectClause = 
-    (List *)intersect_tree_analyze((Node *)parsetree->intersectClause, 
-                                  (Node *)lfirst(parsetree->unionClause),
-                                  (Node *)parsetree);
-  
-  /* intersectClause is no longer needed so set it to NIL */
-  parsetree->intersectClause = NIL;  
-  /* unionClause will be needed later on but the list it delivered
-   * is no longer needed, so set it to NIL */
-  parsetree->unionClause = NIL;         
-  
-  /* Transform the operator tree to DNF (remember ANDs and ORs have been exchanged,
-   * that's why we get DNF by using cnfify) 
-   * 
-   * After the call, explicit ANDs are removed and all AND operands
-   * are simply items in the intersectClause list */
-  intersectClause = cnfify((Expr *)intersectClause, true);
-  /* For every entry of the intersectClause list we generate one entry in 
-   * the union_list */
-  foreach(intersect, intersectClause)
-    {     
-      /* for every OR we create an IN subselect and for every OR NOT
-       * we create a NOT IN subselect, so first extract all the Select
-       * Query nodes from the tree (that contains only OR or OR NOTs
-       * any more because we did a transformation to DNF 
-       *
-       * There must be at least one node that is not negated
-       * (i.e. just OR and not OR NOT) and this node will be the first
-       * in the list returned */
-      intersect_list = NIL; 
-      create_list((Node *)lfirst(intersect), &intersect_list);
-      
-      /* This one will become the Select Query node, all other
-       * nodes are transformed into subselects under this node! */
-      intersect_node = (Query *)lfirst(intersect_list);
-      intersect_list = lnext(intersect_list);
-      
-      /* Check if all Select Statements use the same number of attributes and
-       * if all corresponding attributes are of the same type */
-      if (prev_target)
-       check_targetlists_are_compatible(prev_target, intersect_node->targetList);           
-      prev_target = intersect_node->targetList;         
-      /* End of check for corresponding targetlists */
-      
-      /* Transform all nodes remaining into subselects and add them to
-       * the qualifications of the Select Query node */
-      while(intersect_list != NIL) { 
-       
-       n = makeNode(SubLink);
-       
-       /* Here we got an OR so transform it to an IN subselect */
-       if(IsA(lfirst(intersect_list), Query)) 
-         {           
-           /* Check if all Select Statements use the same number of attributes and
-            * if all corresponding attributes are of the same type */
-           check_targetlists_are_compatible(prev_target, 
-                                  ((Query *)lfirst(intersect_list))->targetList);  
-           /* End of check for corresponding targetlists */
-           
-           n->subselect = lfirst(intersect_list);
-           op = "=";      
-           n->subLinkType = ANY_SUBLINK;  
-           n->useor = false;
-         }
-       /* Here we got an OR NOT node so transform it to a NOT IN  subselect */
-       else 
-         {
-           /* Check if all Select Statements use the same number of attributes and
-            * if all corresponding attributes are of the same type */
-           check_targetlists_are_compatible(prev_target,
-                   ((Query *)lfirst(((Expr *)lfirst(intersect_list))->args))->targetList);
-           /* End of check for corresponding targetlists */
-           
-           n->subselect = (Node *)lfirst(((Expr *)lfirst(intersect_list))->args);
-           op = "<>";    
-           n->subLinkType = ALL_SUBLINK;  
-           n->useor = true;
-         }
-       
-       /* Prepare the lefthand side of the Sublinks: All the entries of the
-        * targetlist must be (IN) or must not be (NOT IN) the subselect */
-       foreach(elist, intersect_node->targetList)
-         {
-           Node          *expr = lfirst(elist);
-           TargetEntry *tent = (TargetEntry *)expr;
-           
-           n->lefthand = lappend(n->lefthand, tent->expr);       
-         }
-       
-       /* The first arguments of oper also have to be created for the
-        * sublink (they are the same as the lefthand side!) */
-       left_expr = n->lefthand;
-       right_expr = ((Query *)(n->subselect))->targetList;
-       
-       foreach(elist, left_expr)
-         {
-           Node           *lexpr = lfirst(elist);
-           Node           *rexpr = lfirst(right_expr);
-           TargetEntry *tent = (TargetEntry *) rexpr;
-           Expr           *op_expr;
-           
-           op_expr = make_op(op, lexpr, tent->expr);
-           
-           n->oper = lappend(n->oper, op_expr);
-           right_expr = lnext(right_expr);
-         }
-       
-       /* If the Select Query node has aggregates in use
-        * add all the subselects to the HAVING qual else to
-        * the WHERE qual */
-       if(intersect_node->hasAggs == false) {    
-         AddQual(intersect_node, (Node *)n);
-       }
-       else {
-         AddHavingQual(intersect_node, (Node *)n);
-       } 
-       
-       /* Now we got sublinks */
-       intersect_node->hasSubLinks = true;       
-       intersect_list = lnext(intersect_list);      
-      }      
-      intersect_node->intersectClause = NIL;
-      union_list = lappend(union_list, intersect_node);
-    }
-  
-  /* The first entry to union_list is our new top node */
-  result = (Query *)lfirst(union_list);
-  /* attach the rest to unionClause */
-  result->unionClause = lnext(union_list);  
-  /* Attach all the items remembered in the beginning of the function */
-  result->sortClause = sortClause;  
-  result->uniqueFlag = uniqueFlag;  
-  result->into = into;
-  result->isPortal = isPortal;
-  result->isBinary = isBinary;
-  result->isTemp = isTemp;
-
-  /* The relation to insert into is attached to the range table
-   * of the new top node */
-  if (commandType == CMD_INSERT)  
-    {     
-      result->rtable = lappend(result->rtable, rtable_insert); 
-      result->resultRelation = length(result->rtable);
-      result->commandType = commandType;  
-    }  
-  /* The resnames of the originally first SelectStatement are 
-   * attached to the new first SelectStatement */
-  foreach(elist, result->targetList)
-    {
-      TargetEntry *tent = (TargetEntry *)lfirst(elist);
-      
-      tent->resdom->resname = lfirst(resnames);
-      resnames = lnext(resnames);
-    }
-  return  result;  
-}
+               query = fireRIRrules(query, NIL);
+
+               /*
+                * If the query target was rewritten as a view, complain.
+                */
+               if (query->resultRelation)
+               {
+                       RangeTblEntry *rte = rt_fetch(query->resultRelation,
+                                                                                 query->rtable);
 
-/* Create a list of nodes that are either Query nodes of NOT Expr
- * nodes followed by a Query node. The tree given in ptr contains at
- * least one non negated Query node. This node is attached to the
- * beginning of the list */
+                       if (rte->rtekind == RTE_SUBQUERY)
+                       {
+                               switch (query->commandType)
+                               {
+                                       case CMD_INSERT:
+                                               elog(ERROR, "Cannot insert into a view"
+                                                        "\n\tYou need an unconditional ON INSERT DO INSTEAD rule");
+                                               break;
+                                       case CMD_UPDATE:
+                                               elog(ERROR, "Cannot update a view"
+                                                        "\n\tYou need an unconditional ON UPDATE DO INSTEAD rule");
+                                               break;
+                                       case CMD_DELETE:
+                                               elog(ERROR, "Cannot delete from a view"
+                                                        "\n\tYou need an unconditional ON DELETE DO INSTEAD rule");
+                                               break;
+                                       default:
+                                               elog(ERROR, "QueryRewrite: unexpected commandType %d",
+                                                        (int) query->commandType);
+                                               break;
+                               }
+                       }
+               }
 
-void create_list(Node *ptr, List **intersect_list)
-{
-  List *arg;
-  
-  if(IsA(ptr,Query))
-    {
-      /* The non negated node is attached at the beginning (lcons) */
-      *intersect_list = lcons(ptr, *intersect_list);
-      return;     
-    }
-  
-  if(IsA(ptr,Expr))
-    {
-      if(((Expr *)ptr)->opType == NOT_EXPR)
-       {
-         /* negated nodes are appended to the end (lappend) */
-         *intersect_list = lappend(*intersect_list, ptr);        
-         return;         
+               results = lappend(results, query);
        }
-      else
-       {
-         foreach(arg, ((Expr *)ptr)->args)
-           {
-             create_list(lfirst(arg), intersect_list);
-           }     
-         return;         
-       }
-      return;     
-    }
-}
 
-/* The nodes given in 'tree' are still 'raw' so 'cook' them using parse_analyze().
- * The node given in first_select has already been cooked, so don't transform
- * it again but return a pointer to the previously cooked version given in 'parsetree' 
- * instead. */
-Node *intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
-{
-  Node *result = (Node *)NIL;
-  List *arg;
-  
-  if(IsA(tree, SelectStmt))
-    {
-      QueryTreeList *qtree;
-      
-      /* If we get to the tree given in first_select return
-       * parsetree instead of performing parse_analyze() */
-      if(tree == first_select){
-       result = parsetree;
-      }
-      else {   
-       /* transform the 'raw' nodes to 'cooked' Query nodes */ 
-       qtree = parse_analyze(lcons(tree, NIL), NULL);
-       result = (Node *)qtree->qtrees[0];      
-      }
-      
-    }  
-  if(IsA(tree,Expr))
-    {
-      /* Call recursively for every argument of the node */
-      foreach(arg, ((Expr *)tree)->args)
-       {
-         lfirst(arg) = intersect_tree_analyze(lfirst(arg), first_select, parsetree);
-       }
-      result = tree;     
-    }
-  return result;  
-}
+       /*
+        * Step 3
+        *
+        * Determine which, if any, of the resulting queries is supposed to set
+        * the command-result tag; and update the canSetTag fields accordingly.
+        *
+        * If the original query is still in the list, it sets the command tag.
+        * Otherwise, the last INSTEAD query of the same kind as the original
+        * is allowed to set the tag.  (Note these rules can leave us with no
+        * query setting the tag.  The tcop code has to cope with this by
+        * setting up a default tag based on the original un-rewritten query.)
+        *
+        * The Asserts verify that at most one query in the result list is marked
+        * canSetTag.  If we aren't checking asserts, we can fall out of the loop
+        * as soon as we find the original query.
+        */
+       origCmdType = parsetree->commandType;
+       foundOriginalQuery = false;
+       lastInstead = NULL;
 
+       foreach(l, results)
+       {
+               Query      *query = (Query *) lfirst(l);
 
+               if (query->querySource == QSRC_ORIGINAL)
+               {
+                       Assert(query->canSetTag);
+                       Assert(!foundOriginalQuery);
+                       foundOriginalQuery = true;
+#ifndef USE_ASSERT_CHECKING
+                       break;
+#endif
+               }
+               else
+               {
+                       Assert(!query->canSetTag);
+                       if (query->commandType == origCmdType &&
+                               (query->querySource == QSRC_INSTEAD_RULE ||
+                                query->querySource == QSRC_QUAL_INSTEAD_RULE))
+                               lastInstead = query;
+               }
+       }
 
+       if (!foundOriginalQuery && lastInstead != NULL)
+               lastInstead->canSetTag = true;
 
+       return results;
+}