]> granicus.if.org Git - postgresql/blobdiff - src/backend/rewrite/rewriteHandler.c
Implement an API to let foreign-data wrappers actually be functional.
[postgresql] / src / backend / rewrite / rewriteHandler.c
index e5a1d4c74725f172e826eb917f683fef9a9d8693..3a50642fce8008d6b99b816515022cd10773d232 100644 (file)
@@ -3,28 +3,30 @@
  * rewriteHandler.c
  *             Primary module of query rewriter.
  *
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.172 2007/03/17 00:11:04 tgl Exp $
+ *       src/backend/rewrite/rewriteHandler.c
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "access/sysattr.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
-#include "optimizer/clauses.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
 #include "parser/parse_coerce.h"
-#include "parser/parse_expr.h"
 #include "parser/parsetree.h"
+#include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "commands/trigger.h"
 
 
 /* We use a list of these to detect recursion in RewriteQuery */
@@ -42,19 +44,22 @@ static Query *rewriteRuleAction(Query *parsetree,
                                  CmdType event,
                                  bool *returning_flag);
 static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
-static void rewriteTargetList(Query *parsetree, Relation target_relation,
-                                 List **attrno_list);
+static void rewriteTargetListIU(Query *parsetree, Relation target_relation,
+                                       List **attrno_list);
 static TargetEntry *process_matched_tle(TargetEntry *src_tle,
                                        TargetEntry *prior_tle,
                                        const char *attrName);
 static Node *get_assignment_input(Node *node);
 static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation,
                                 List *attrnos);
+static void rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
+                                                               Relation target_relation);
 static void markQueryForLocking(Query *qry, Node *jtnode,
-                                                               bool forUpdate, bool noWait);
+                                       bool forUpdate, bool noWait, bool pushedDown);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
                   int varno, Query *parsetree);
-static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
+static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
+                        bool forUpdatePushedDown);
 
 
 /*
@@ -63,6 +68,10 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
  *       These locks will ensure that the relation schemas don't change under us
  *       while we are rewriting and planning the query.
  *
+ * forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE applies
+ * to the current subquery, requiring all rels to be opened with RowShareLock.
+ * This should always be false at the start of the recursion.
+ *
  * A secondary purpose of this routine is to fix up JOIN RTE references to
  * dropped columns (see details below).  Because the RTEs are modified in
  * place, it is generally appropriate for the caller of this routine to have
@@ -90,7 +99,7 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
  * construction of a nested join was O(N^2) in the nesting depth.)
  */
 void
-AcquireRewriteLocks(Query *parsetree)
+AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
 {
        ListCell   *l;
        int                     rt_index;
@@ -128,7 +137,8 @@ AcquireRewriteLocks(Query *parsetree)
                                 */
                                if (rt_index == parsetree->resultRelation)
                                        lockmode = RowExclusiveLock;
-                               else if (get_rowmark(parsetree, rt_index))
+                               else if (forUpdatePushedDown ||
+                                                get_parse_rowmark(parsetree, rt_index) != NULL)
                                        lockmode = RowShareLock;
                                else
                                        lockmode = AccessShareLock;
@@ -191,7 +201,7 @@ AcquireRewriteLocks(Query *parsetree)
                                                         * now-dropped type OID, but it doesn't really
                                                         * matter what type the Const claims to be.
                                                         */
-                                                       aliasvar = (Var *) makeNullConst(INT4OID);
+                                                       aliasvar = (Var *) makeNullConst(INT4OID, -1);
                                                }
                                        }
                                        newaliasvars = lappend(newaliasvars, aliasvar);
@@ -205,7 +215,9 @@ AcquireRewriteLocks(Query *parsetree)
                                 * The subquery RTE itself is all right, but we have to
                                 * recurse to process the represented subquery.
                                 */
-                               AcquireRewriteLocks(rte->subquery);
+                               AcquireRewriteLocks(rte->subquery,
+                                                                       (forUpdatePushedDown ||
+                                                       get_parse_rowmark(parsetree, rt_index) != NULL));
                                break;
 
                        default:
@@ -214,13 +226,21 @@ AcquireRewriteLocks(Query *parsetree)
                }
        }
 
+       /* Recurse into subqueries in WITH */
+       foreach(l, parsetree->cteList)
+       {
+               CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
+
+               AcquireRewriteLocks((Query *) cte->ctequery, false);
+       }
+
        /*
         * Recurse into sublink subqueries, too.  But we already did the ones in
-        * the rtable.
+        * the rtable and cteList.
         */
        if (parsetree->hasSubLinks)
                query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
-                                                 QTW_IGNORE_RT_SUBQUERIES);
+                                                 QTW_IGNORE_RC_SUBQUERIES);
 }
 
 /*
@@ -236,7 +256,7 @@ acquireLocksOnSubLinks(Node *node, void *context)
                SubLink    *sub = (SubLink *) node;
 
                /* Do what we came for */
-               AcquireRewriteLocks((Query *) sub->subselect);
+               AcquireRewriteLocks((Query *) sub->subselect, false);
                /* Fall through to process lefthand args of SubLink */
        }
 
@@ -289,7 +309,7 @@ rewriteRuleAction(Query *parsetree,
        /*
         * Acquire necessary locks and fix any deleted JOIN RTE entries.
         */
-       AcquireRewriteLocks(rule_action);
+       AcquireRewriteLocks(rule_action, false);
        (void) acquireLocksOnSubLinks(rule_qual, NULL);
 
        current_varno = rt_index;
@@ -308,7 +328,7 @@ rewriteRuleAction(Query *parsetree,
 
        OffsetVarNodes((Node *) sub_action, rt_length, 0);
        OffsetVarNodes(rule_qual, rt_length, 0);
-       /* but references to *OLD* should point at original rt_index */
+       /* 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,
@@ -345,6 +365,37 @@ rewriteRuleAction(Query *parsetree,
        sub_action->rtable = list_concat((List *) copyObject(parsetree->rtable),
                                                                         sub_action->rtable);
 
+       /*
+        * There could have been some SubLinks in parsetree's rtable, in which
+        * case we'd better mark the sub_action correctly.
+        */
+       if (parsetree->hasSubLinks && !sub_action->hasSubLinks)
+       {
+               ListCell   *lc;
+
+               foreach(lc, parsetree->rtable)
+               {
+                       RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+
+                       switch (rte->rtekind)
+                       {
+                               case RTE_FUNCTION:
+                                       sub_action->hasSubLinks =
+                                               checkExprHasSubLink(rte->funcexpr);
+                                       break;
+                               case RTE_VALUES:
+                                       sub_action->hasSubLinks =
+                                               checkExprHasSubLink((Node *) rte->values_lists);
+                                       break;
+                               default:
+                                       /* other RTE types don't contain bare expressions */
+                                       break;
+                       }
+                       if (sub_action->hasSubLinks)
+                               break;                  /* no need to keep scanning rtable */
+               }
+       }
+
        /*
         * Each rule action's jointree should be the main parsetree's jointree
         * plus that rule's jointree, but usually *without* the original rtindex
@@ -423,7 +474,8 @@ rewriteRuleAction(Query *parsetree,
                                                                                                   sub_action->rtable),
                                                                                  parsetree->targetList,
                                                                                  event,
-                                                                                 current_varno);
+                                                                                 current_varno,
+                                                                                 NULL);
                if (sub_action_ptr)
                        *sub_action_ptr = sub_action;
                else
@@ -453,7 +505,16 @@ rewriteRuleAction(Query *parsetree,
                                                                parsetree->rtable),
                                           rule_action->returningList,
                                           CMD_SELECT,
-                                          0);
+                                          0,
+                                          &rule_action->hasSubLinks);
+
+               /*
+                * There could have been some SubLinks in parsetree's returningList,
+                * in which case we'd better mark the rule_action correctly.
+                */
+               if (parsetree->hasSubLinks && !rule_action->hasSubLinks)
+                       rule_action->hasSubLinks =
+                               checkExprHasSubLink((Node *) rule_action->returningList);
        }
 
        return rule_action;
@@ -496,7 +557,7 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
 
 
 /*
- * rewriteTargetList - rewrite INSERT/UPDATE targetlist into standard form
+ * rewriteTargetListIU - rewrite INSERT/UPDATE targetlist into standard form
  *
  * This has the following responsibilities:
  *
@@ -508,7 +569,14 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
  * and UPDATE, replace explicit DEFAULT specifications with column default
  * expressions.
  *
- * 2. Merge multiple entries for the same target attribute, or declare error
+ * 2. For an UPDATE on a view, add tlist entries for any unassigned-to
+ * attributes, assigning them their old values.  These will later get
+ * expanded to the output values of the view.  (This is equivalent to what
+ * the planner's expand_targetlist() will do for UPDATE on a regular table,
+ * but it's more convenient to do it here while we still have easy access
+ * to the view's original RT index.)
+ *
+ * 3. Merge multiple entries for the same target attribute, or declare error
  * if we can't.  Multiple entries are only allowed for INSERT/UPDATE of
  * portions of an array or record field, for example
  *                     UPDATE table SET foo[2] = 42, foo[4] = 43;
@@ -516,13 +584,13 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
  * 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,
+ * 4. 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
+ * We must do items 1,2,3 before firing rewrite rules, else rewritten
+ * references to NEW.foo will produce wrong or incomplete results.     Item 4
  * 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.
+ * can do it essentially for free while handling the other items.
  *
  * If attrno_list isn't NULL, we return an additional output besides the
  * rewritten targetlist: an integer list of the assigned-to attnums, in
@@ -530,8 +598,8 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
  * processing VALUES RTEs.
  */
 static void
-rewriteTargetList(Query *parsetree, Relation target_relation,
-                                 List **attrno_list)
+rewriteTargetListIU(Query *parsetree, Relation target_relation,
+                                       List **attrno_list)
 {
        CmdType         commandType = parsetree->commandType;
        TargetEntry **new_tles;
@@ -653,6 +721,7 @@ rewriteTargetList(Query *parsetree, Relation target_relation,
                                                                                                InvalidOid, -1,
                                                                                                att_tup->atttypid,
                                                                                                COERCE_IMPLICIT_CAST,
+                                                                                               -1,
                                                                                                false,
                                                                                                false);
                                }
@@ -665,6 +734,28 @@ rewriteTargetList(Query *parsetree, Relation target_relation,
                                                                                  false);
                }
 
+               /*
+                * For an UPDATE on a view, provide a dummy entry whenever there is
+                * no explicit assignment.
+                */
+               if (new_tle == NULL && commandType == CMD_UPDATE &&
+                       target_relation->rd_rel->relkind == RELKIND_VIEW)
+               {
+                       Node       *new_expr;
+
+                       new_expr = (Node *) makeVar(parsetree->resultRelation,
+                                                                               attrno,
+                                                                               att_tup->atttypid,
+                                                                               att_tup->atttypmod,
+                                                                               att_tup->attcollation,
+                                                                               0);
+
+                       new_tle = makeTargetEntry((Expr *) new_expr,
+                                                                         attrno,
+                                                                         pstrdup(NameStr(att_tup->attname)),
+                                                                         false);
+               }
+
                if (new_tle)
                        new_tlist = lappend(new_tlist, new_tle);
        }
@@ -885,7 +976,8 @@ build_column_default(Relation rel, int attrno)
                                                                 expr, exprtype,
                                                                 atttype, atttypmod,
                                                                 COERCION_ASSIGNMENT,
-                                                                COERCE_IMPLICIT_CAST);
+                                                                COERCE_IMPLICIT_CAST,
+                                                                -1);
        if (expr == NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -925,8 +1017,8 @@ searchForDefault(RangeTblEntry *rte)
 /*
  * When processing INSERT ... VALUES with a VALUES RTE (ie, multiple VALUES
  * lists), we have to replace any DEFAULT items in the VALUES lists with
- * the appropriate default expressions.  The other aspects of rewriteTargetList
- * need be applied only to the query's targetlist proper.
+ * the appropriate default expressions.  The other aspects of targetlist
+ * rewriting need be applied only to the query's targetlist proper.
  *
  * Note that we currently can't support subscripted or field assignment
  * in the multi-VALUES case.  The targetlist will contain simple Vars
@@ -992,6 +1084,7 @@ rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos)
                                                                                                InvalidOid, -1,
                                                                                                att_tup->atttypid,
                                                                                                COERCE_IMPLICIT_CAST,
+                                                                                               -1,
                                                                                                false,
                                                                                                false);
                                }
@@ -1006,6 +1099,62 @@ rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos)
 }
 
 
+/*
+ * rewriteTargetListUD - rewrite UPDATE/DELETE targetlist as needed
+ *
+ * This function adds a "junk" TLE that is needed to allow the executor to
+ * find the original row for the update or delete.  When the target relation
+ * is a regular table, the junk TLE emits the ctid attribute of the original
+ * row.  When the target relation is a view, there is no ctid, so we instead
+ * emit a whole-row Var that will contain the "old" values of the view row.
+ *
+ * For UPDATE queries, this is applied after rewriteTargetListIU.  The
+ * ordering isn't actually critical at the moment.
+ */
+static void
+rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
+                                       Relation target_relation)
+{
+       Var                *var;
+       const char *attrname;
+       TargetEntry *tle;
+
+       if (target_relation->rd_rel->relkind == RELKIND_RELATION)
+       {
+               /*
+                * Emit CTID so that executor can find the row to update or delete.
+                */
+               var = makeVar(parsetree->resultRelation,
+                                         SelfItemPointerAttributeNumber,
+                                         TIDOID,
+                                         -1,
+                                         InvalidOid,
+                                         0);
+
+               attrname = "ctid";
+       }
+       else
+       {
+               /*
+                * Emit whole-row Var so that executor will have the "old" view row
+                * to pass to the INSTEAD OF trigger.
+                */
+               var = makeWholeRowVar(target_rte,
+                                                         parsetree->resultRelation,
+                                                         0);
+
+               attrname = "wholerow";
+       }
+
+       tle = makeTargetEntry((Expr *) var,
+                                                 list_length(parsetree->targetList) + 1,
+                                                 pstrdup(attrname),
+                                                 true);
+
+       parsetree->targetList = lappend(parsetree->targetList, tle);
+}
+
+
 /*
  * matchLocks -
  *       match the list of locks and returns the matching rules
@@ -1035,6 +1184,28 @@ matchLocks(CmdType event,
        {
                RewriteRule *oneLock = rulelocks->rules[i];
 
+               /*
+                * Suppress ON INSERT/UPDATE/DELETE rules that are disabled or
+                * configured to not fire during the current sessions replication
+                * role. ON SELECT rules will always be applied in order to keep views
+                * working even in LOCAL or REPLICA role.
+                */
+               if (oneLock->event != CMD_SELECT)
+               {
+                       if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
+                       {
+                               if (oneLock->enabled == RULE_FIRES_ON_ORIGIN ||
+                                       oneLock->enabled == RULE_DISABLED)
+                                       continue;
+                       }
+                       else    /* ORIGIN or LOCAL ROLE */
+                       {
+                               if (oneLock->enabled == RULE_FIRES_ON_REPLICA ||
+                                       oneLock->enabled == RULE_DISABLED)
+                                       continue;
+                       }
+               }
+
                if (oneLock->event == event)
                {
                        if (parsetree->commandType != CMD_SELECT ||
@@ -1059,7 +1230,8 @@ ApplyRetrieveRule(Query *parsetree,
                                  int rt_index,
                                  bool relation_level,
                                  Relation relation,
-                                 List *activeRIRs)
+                                 List *activeRIRs,
+                                 bool forUpdatePushedDown)
 {
        Query      *rule_action;
        RangeTblEntry *rte,
@@ -1073,22 +1245,90 @@ ApplyRetrieveRule(Query *parsetree,
        if (!relation_level)
                elog(ERROR, "cannot handle per-attribute ON SELECT rule");
 
+       if (rt_index == parsetree->resultRelation)
+       {
+               /*
+                * We have a view as the result relation of the query, and it wasn't
+                * rewritten by any rule.  This case is supported if there is an
+                * INSTEAD OF trigger that will trap attempts to insert/update/delete
+                * view rows.  The executor will check that; for the moment just plow
+                * ahead.  We have two cases:
+                *
+                * For INSERT, we needn't do anything.  The unmodified RTE will serve
+                * fine as the result relation.
+                *
+                * For UPDATE/DELETE, we need to expand the view so as to have source
+                * data for the operation.  But we also need an unmodified RTE to
+                * serve as the target.  So, copy the RTE and add the copy to the
+                * rangetable.  Note that the copy does not get added to the jointree.
+                * Also note that there's a hack in fireRIRrules to avoid calling
+                * this function again when it arrives at the copied RTE.
+                */
+               if (parsetree->commandType == CMD_INSERT)
+                       return parsetree;
+               else if (parsetree->commandType == CMD_UPDATE ||
+                                parsetree->commandType == CMD_DELETE)
+               {
+                       RangeTblEntry *newrte;
+
+                       rte = rt_fetch(rt_index, parsetree->rtable);
+                       newrte = copyObject(rte);
+                       parsetree->rtable = lappend(parsetree->rtable, newrte);
+                       parsetree->resultRelation = list_length(parsetree->rtable);
+
+                       /*
+                        * There's no need to do permissions checks twice, so wipe out
+                        * the permissions info for the original RTE (we prefer to keep
+                        * the bits set on the result RTE).
+                        */
+                       rte->requiredPerms = 0;
+                       rte->checkAsUser = InvalidOid;
+                       rte->selectedCols = NULL;
+                       rte->modifiedCols = NULL;
+
+                       /*
+                        * For the most part, Vars referencing the view should remain as
+                        * they are, meaning that they implicitly represent OLD values.
+                        * But in the RETURNING list if any, we want such Vars to
+                        * represent NEW values, so change them to reference the new RTE.
+                        *
+                        * Since ChangeVarNodes scribbles on the tree in-place, copy the
+                        * RETURNING list first for safety.
+                        */
+                       parsetree->returningList = copyObject(parsetree->returningList);
+                       ChangeVarNodes((Node *) parsetree->returningList, rt_index,
+                                                  parsetree->resultRelation, 0);
+
+                       /* Now, continue with expanding the original view RTE */
+               }
+               else
+                       elog(ERROR, "unrecognized commandType: %d",
+                                (int) parsetree->commandType);
+       }
+
+       /*
+        * If FOR UPDATE/SHARE of view, be sure we get right initial lock on the
+        * relations it references.
+        */
+       rc = get_parse_rowmark(parsetree, rt_index);
+       forUpdatePushedDown |= (rc != NULL);
+
        /*
         * Make a modifiable copy of the view query, and acquire needed locks on
         * the relations it mentions.
         */
        rule_action = copyObject(linitial(rule->actions));
 
-       AcquireRewriteLocks(rule_action);
+       AcquireRewriteLocks(rule_action, forUpdatePushedDown);
 
        /*
         * Recursively expand any view references inside the view.
         */
-       rule_action = fireRIRrules(rule_action, activeRIRs);
+       rule_action = fireRIRrules(rule_action, activeRIRs, forUpdatePushedDown);
 
        /*
-        * VIEWs are really easy --- just plug the view query in as a subselect,
-        * replacing the relation's original RTE.
+        * Now, plug the view query in as a subselect, replacing the relation's
+        * original RTE.
         */
        rte = rt_fetch(rt_index, parsetree->rtable);
 
@@ -1099,34 +1339,31 @@ ApplyRetrieveRule(Query *parsetree,
 
        /*
         * We move the view's permission check data down to its rangetable. The
-        * checks will actually be done against the *OLD* entry therein.
+        * 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->requiredPerms = rte->requiredPerms;
        subrte->checkAsUser = rte->checkAsUser;
+       subrte->selectedCols = rte->selectedCols;
+       subrte->modifiedCols = rte->modifiedCols;
 
        rte->requiredPerms = 0;         /* no permission check on subquery itself */
        rte->checkAsUser = InvalidOid;
+       rte->selectedCols = NULL;
+       rte->modifiedCols = NULL;
 
        /*
-        * FOR UPDATE/SHARE of view?
+        * If FOR UPDATE/SHARE of view, mark all the contained tables as implicit
+        * FOR UPDATE/SHARE, the same as the parser would have done if the view's
+        * subquery had been written out explicitly.
+        *
+        * Note: we don't consider forUpdatePushedDown here; such marks will be
+        * made by recursing from the upper level in markQueryForLocking.
         */
-       if ((rc = get_rowmark(parsetree, rt_index)) != NULL)
-       {
-               /*
-                * Remove the view from the list of rels that will actually be marked
-                * FOR UPDATE/SHARE by the executor.  It will still be access-checked
-                * for write access, though.
-                */
-               parsetree->rowMarks = list_delete_ptr(parsetree->rowMarks, rc);
-
-               /*
-                * Set up the view's referenced tables as if FOR UPDATE/SHARE.
-                */
+       if (rc != NULL)
                markQueryForLocking(rule_action, (Node *) rule_action->jointree,
-                                                       rc->forUpdate, rc->noWait);
-       }
+                                                       rc->forUpdate, rc->noWait, true);
 
        return parsetree;
 }
@@ -1143,7 +1380,8 @@ ApplyRetrieveRule(Query *parsetree,
  * to scan the jointree to determine which rels are used.
  */
 static void
-markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
+markQueryForLocking(Query *qry, Node *jtnode,
+                                       bool forUpdate, bool noWait, bool pushedDown)
 {
        if (jtnode == NULL)
                return;
@@ -1154,15 +1392,21 @@ markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
 
                if (rte->rtekind == RTE_RELATION)
                {
-                       applyLockingClause(qry, rti, forUpdate, noWait);
-                       rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                       /* ignore foreign tables */
+                       if (get_rel_relkind(rte->relid) != RELKIND_FOREIGN_TABLE)
+                       {
+                               applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
+                               rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                       }
                }
                else if (rte->rtekind == RTE_SUBQUERY)
                {
+                       applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
                        /* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
                        markQueryForLocking(rte->subquery, (Node *) rte->subquery->jointree,
-                                                               forUpdate, noWait);
+                                                               forUpdate, noWait, true);
                }
+               /* other RTE types are unaffected by FOR UPDATE */
        }
        else if (IsA(jtnode, FromExpr))
        {
@@ -1170,14 +1414,14 @@ markQueryForLocking(Query *qry, Node *jtnode, bool forUpdate, bool noWait)
                ListCell   *l;
 
                foreach(l, f->fromlist)
-                       markQueryForLocking(qry, lfirst(l), forUpdate, noWait);
+                       markQueryForLocking(qry, lfirst(l), forUpdate, noWait, pushedDown);
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
 
-               markQueryForLocking(qry, j->larg, forUpdate, noWait);
-               markQueryForLocking(qry, j->rarg, forUpdate, noWait);
+               markQueryForLocking(qry, j->larg, forUpdate, noWait, pushedDown);
+               markQueryForLocking(qry, j->rarg, forUpdate, noWait, pushedDown);
        }
        else
                elog(ERROR, "unrecognized node type: %d",
@@ -1209,7 +1453,7 @@ fireRIRonSubLink(Node *node, List *activeRIRs)
 
                /* Do what we came for */
                sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect,
-                                                                                          activeRIRs);
+                                                                                          activeRIRs, false);
                /* Fall through to process lefthand args of SubLink */
        }
 
@@ -1227,9 +1471,11 @@ fireRIRonSubLink(Node *node, List *activeRIRs)
  *     Apply all RIR rules on each rangetable entry in a query
  */
 static Query *
-fireRIRrules(Query *parsetree, List *activeRIRs)
+fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 {
+       int                     origResultRelation = parsetree->resultRelation;
        int                     rt_index;
+       ListCell   *lc;
 
        /*
         * don't try to convert this into a foreach loop, because rtable list can
@@ -1256,7 +1502,9 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
                 */
                if (rte->rtekind == RTE_SUBQUERY)
                {
-                       rte->subquery = fireRIRrules(rte->subquery, activeRIRs);
+                       rte->subquery = fireRIRrules(rte->subquery, activeRIRs,
+                                                                                (forUpdatePushedDown ||
+                                                       get_parse_rowmark(parsetree, rt_index) != NULL));
                        continue;
                }
 
@@ -1277,6 +1525,14 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
                        !rangeTableEntry_used((Node *) parsetree, rt_index, 0))
                        continue;
 
+               /*
+                * Also, if this is a new result relation introduced by
+                * ApplyRetrieveRule, we don't want to do anything more with it.
+                */
+               if (rt_index == parsetree->resultRelation &&
+                       rt_index != origResultRelation)
+                       continue;
+
                /*
                 * We can use NoLock here since either the parser or
                 * AcquireRewriteLocks should have locked the rel already.
@@ -1333,7 +1589,8 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
                                                                                          rt_index,
                                                                                          rule->attrno == -1,
                                                                                          rel,
-                                                                                         activeRIRs);
+                                                                                         activeRIRs,
+                                                                                         forUpdatePushedDown);
                        }
 
                        activeRIRs = list_delete_first(activeRIRs);
@@ -1342,13 +1599,22 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
                heap_close(rel, NoLock);
        }
 
+       /* Recurse into subqueries in WITH */
+       foreach(lc, parsetree->cteList)
+       {
+               CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+
+               cte->ctequery = (Node *)
+                       fireRIRrules((Query *) cte->ctequery, activeRIRs, false);
+       }
+
        /*
         * Recurse into sublink subqueries, too.  But we already did the ones in
-        * the rtable.
+        * the rtable and cteList.
         */
        if (parsetree->hasSubLinks)
                query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs,
-                                                 QTW_IGNORE_RT_SUBQUERIES);
+                                                 QTW_IGNORE_RC_SUBQUERIES);
 
        return parsetree;
 }
@@ -1394,7 +1660,8 @@ CopyAndAddInvertedQual(Query *parsetree,
                                                          rt_fetch(rt_index, parsetree->rtable),
                                                          parsetree->targetList,
                                                          event,
-                                                         rt_index);
+                                                         rt_index,
+                                                         &parsetree->hasSubLinks);
        /* And attach the fixed qual */
        AddInvertedQual(parsetree, new_qual);
 
@@ -1529,7 +1796,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
        List       *rewritten = NIL;
 
        /*
-        * If the statement is an update, insert or delete - fire rules on it.
+        * If the statement is an insert, update, or delete, adjust its targetlist
+        * as needed, and then fire INSERT/UPDATE/DELETE 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
@@ -1554,13 +1822,9 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
                rt_entry_relation = heap_open(rt_entry->relid, NoLock);
 
                /*
-                * 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.
+                * Rewrite the targetlist as needed for the command type.
                 */
-               if (event == CMD_UPDATE)
-                       rewriteTargetList(parsetree, rt_entry_relation, NULL);
-               else if (event == CMD_INSERT)
+               if (event == CMD_INSERT)
                {
                        RangeTblEntry *values_rte = NULL;
 
@@ -1587,16 +1851,27 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
                                List       *attrnos;
 
                                /* Process the main targetlist ... */
-                               rewriteTargetList(parsetree, rt_entry_relation, &attrnos);
+                               rewriteTargetListIU(parsetree, rt_entry_relation, &attrnos);
                                /* ... and the VALUES expression lists */
                                rewriteValuesRTE(values_rte, rt_entry_relation, attrnos);
                        }
                        else
                        {
                                /* Process just the main targetlist */
-                               rewriteTargetList(parsetree, rt_entry_relation, NULL);
+                               rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
                        }
                }
+               else if (event == CMD_UPDATE)
+               {
+                       rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
+                       rewriteTargetListUD(parsetree, rt_entry, rt_entry_relation);
+               }
+               else if (event == CMD_DELETE)
+               {
+                       rewriteTargetListUD(parsetree, rt_entry, rt_entry_relation);
+               }
+               else
+                       elog(ERROR, "unrecognized commandType: %d", (int) event);
 
                /*
                 * Collect and apply the appropriate rules.
@@ -1670,22 +1945,22 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
                                case CMD_INSERT:
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("cannot perform INSERT RETURNING on relation \"%s\"",
-                                                               RelationGetRelationName(rt_entry_relation)),
+                                                        errmsg("cannot perform INSERT RETURNING on relation \"%s\"",
+                                                                RelationGetRelationName(rt_entry_relation)),
                                                         errhint("You need an unconditional ON INSERT DO INSTEAD rule with a RETURNING clause.")));
                                        break;
                                case CMD_UPDATE:
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("cannot perform UPDATE RETURNING on relation \"%s\"",
-                                                               RelationGetRelationName(rt_entry_relation)),
+                                                        errmsg("cannot perform UPDATE RETURNING on relation \"%s\"",
+                                                                RelationGetRelationName(rt_entry_relation)),
                                                         errhint("You need an unconditional ON UPDATE DO INSTEAD rule with a RETURNING clause.")));
                                        break;
                                case CMD_DELETE:
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("cannot perform DELETE RETURNING on relation \"%s\"",
-                                                               RelationGetRelationName(rt_entry_relation)),
+                                                        errmsg("cannot perform DELETE RETURNING on relation \"%s\"",
+                                                                RelationGetRelationName(rt_entry_relation)),
                                                         errhint("You need an unconditional ON DELETE DO INSTEAD rule with a RETURNING clause.")));
                                        break;
                                default:
@@ -1745,7 +2020,7 @@ List *
 QueryRewrite(Query *parsetree)
 {
        List       *querylist;
-       List       *results = NIL;
+       List       *results;
        ListCell   *l;
        CmdType         origCmdType;
        bool            foundOriginalQuery;
@@ -1763,50 +2038,12 @@ QueryRewrite(Query *parsetree)
         *
         * Apply all the RIR rules on each query
         */
+       results = NIL;
        foreach(l, querylist)
        {
                Query      *query = (Query *) lfirst(l);
 
-               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);
-
-                       if (rte->rtekind == RTE_SUBQUERY)
-                       {
-                               switch (query->commandType)
-                               {
-                                       case CMD_INSERT:
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                errmsg("cannot insert into a view"),
-                                                                errhint("You need an unconditional ON INSERT DO INSTEAD rule.")));
-                                               break;
-                                       case CMD_UPDATE:
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                errmsg("cannot update a view"),
-                                                                errhint("You need an unconditional ON UPDATE DO INSTEAD rule.")));
-                                               break;
-                                       case CMD_DELETE:
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                errmsg("cannot delete from a view"),
-                                                                errhint("You need an unconditional ON DELETE DO INSTEAD rule.")));
-                                               break;
-                                       default:
-                                               elog(ERROR, "unrecognized commandType: %d",
-                                                        (int) query->commandType);
-                                               break;
-                               }
-                       }
-               }
-
+               query = fireRIRrules(query, NIL, false);
                results = lappend(results, query);
        }