Make application of FOR UPDATE to a view work exactly like the parser's
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 7 Dec 2000 01:22:25 +0000 (01:22 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 7 Dec 2000 01:22:25 +0000 (01:22 +0000)
transformForUpdate does: it should recurse into subqueries.

src/backend/rewrite/rewriteHandler.c

index 21372e0794df0c407728b8552e9c2811668184fa..753475aa820889eb877d16b16a2f2cb13b4f58cb 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.85 2000/12/06 23:55:18 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.86 2000/12/07 01:22:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@ static RewriteInfo *gatherRewriteMeta(Query *parsetree,
                  CmdType event,
                  bool instead_flag);
 static List *adjustJoinTreeList(Query *parsetree, int rt_index, bool *found);
+static void markQueryForUpdate(Query *qry, bool skipOldNew);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
                        int varno, Query *parsetree);
 static Query *fireRIRrules(Query *parsetree);
@@ -263,7 +264,6 @@ ApplyRetrieveRule(Query *parsetree,
    Query      *rule_action;
    RangeTblEntry *rte,
               *subrte;
-   List       *l;
 
    if (length(rule->actions) != 1)
        elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
@@ -308,8 +308,6 @@ ApplyRetrieveRule(Query *parsetree,
     */
    if (intMember(rt_index, parsetree->rowMarks))
    {
-       Index       innerrti = 1;
-
        /*
         * Remove the view from the list of rels that will actually be
         * marked FOR UPDATE by the executor.  It will still be access-
@@ -320,29 +318,51 @@ ApplyRetrieveRule(Query *parsetree,
        /*
         * Set up the view's referenced tables as if FOR UPDATE.
         */
-       foreach(l, rule_action->rtable)
-       {
-           subrte = (RangeTblEntry *) lfirst(l);
-
-           /*
-            * RTable of VIEW has two entries of VIEW itself - skip them!
-            * Also keep hands off of sub-subqueries.
-            */
-           if (innerrti != PRS2_OLD_VARNO && innerrti != PRS2_NEW_VARNO &&
-               subrte->relid != InvalidOid)
-           {
-               if (!intMember(innerrti, rule_action->rowMarks))
-                   rule_action->rowMarks = lappendi(rule_action->rowMarks,
-                                                    innerrti);
-               subrte->checkForWrite = true;
-           }
-           innerrti++;
-       }
+       markQueryForUpdate(rule_action, true);
    }
 
    return parsetree;
 }
 
+/*
+ * 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
+markQueryForUpdate(Query *qry, bool skipOldNew)
+{
+   Index       rti = 0;
+   List       *l;
+
+   foreach(l, qry->rtable)
+   {
+       RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+
+       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->subquery)
+       {
+           /* FOR UPDATE of subquery is propagated to subquery's rels */
+           markQueryForUpdate(rte->subquery, false);
+       }
+       else
+       {
+           if (!intMember(rti, qry->rowMarks))
+               qry->rowMarks = lappendi(qry->rowMarks, rti);
+           rte->checkForWrite = true;
+       }
+   }
+}
+
 
 /*
  * fireRIRonSubLink -