]> granicus.if.org Git - postgresql/commitdiff
Clean up handling of FOR UPDATE inside views and subselects ... make it
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 6 Dec 2000 23:55:19 +0000 (23:55 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 6 Dec 2000 23:55:19 +0000 (23:55 +0000)
work where we can (given that the executor only handles it at top level)
and generate an error where we can't.  Note that while the parser has
been allowing views to say SELECT FOR UPDATE for a few weeks now, that
hasn't actually worked until just now.

src/backend/optimizer/plan/planner.c
src/backend/parser/analyze.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/include/parser/analyze.h

index 7a1151f0c9a90018b78264a72e6f2d2c5009becd..7c73a878242b9dff5ae5d6e8671d09a26e4d7549 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.96 2000/11/12 00:36:58 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.97 2000/12/06 23:55:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,7 @@
 #include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
+#include "parser/analyze.h"
 #include "parser/parsetree.h"
 #include "parser/parse_expr.h"
 #include "rewrite/rewriteManip.h"
@@ -255,6 +256,7 @@ pull_up_subqueries(Query *parse, Node *jtnode)
                        int             rtoffset;
                        Node   *subjointree;
                        List   *subtlist;
+                       List   *l;
 
                        /*
                         * First, recursively pull up the subquery's subqueries,
@@ -289,10 +291,22 @@ pull_up_subqueries(Query *parse, Node *jtnode)
                        parse->havingQual =
                                ResolveNew(parse->havingQual,
                                                   varno, 0, subtlist, CMD_SELECT, 0);
+                       /*
+                        * Pull up any FOR UPDATE markers, too.
+                        */
+                       foreach(l, subquery->rowMarks)
+                       {
+                               int             submark = lfirsti(l);
+
+                               parse->rowMarks = lappendi(parse->rowMarks,
+                                                                                  submark + rtoffset);
+                       }
                        /*
                         * Miscellaneous housekeeping.
                         */
                        parse->hasSubLinks |= subquery->hasSubLinks;
+                       /* subquery won't be pulled up if it hasAggs, so no work there */
+
                        /*
                         * Return the adjusted subquery jointree to replace the
                         * RangeTblRef entry in my jointree.
@@ -340,11 +354,6 @@ is_simple_subquery(Query *subquery)
                subquery->into != NULL ||
                subquery->isPortal)
                elog(ERROR, "is_simple_subquery: subquery is bogus");
-       /*
-        * Also check for currently-unsupported features.
-        */
-       if (subquery->rowMarks)
-               elog(ERROR, "FOR UPDATE is not supported in subselects");
        /*
         * Can't currently pull up a query with setops.
         * Maybe after querytree redesign...
@@ -707,6 +716,13 @@ grouping_planner(Query *parse, double tuple_fraction)
 
                tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
 
+               /*
+                * Can't handle FOR UPDATE here (parser should have checked already,
+                * but let's make sure).
+                */
+               if (parse->rowMarks)
+                       elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
+
                /*
                 * We set current_pathkeys NIL indicating we do not know sort
                 * order.  This is correct when the top set operation is UNION ALL,
@@ -744,6 +760,18 @@ grouping_planner(Query *parse, double tuple_fraction)
                {
                        List       *l;
 
+                       /*
+                        * We've got trouble if the FOR UPDATE appears inside grouping,
+                        * since grouping renders a reference to individual tuple CTIDs
+                        * invalid.  This is also checked at parse time, but that's
+                        * insufficient because of rule substitution, query pullup, etc.
+                        */
+                       CheckSelectForUpdate(parse);
+
+                       /* Currently the executor only supports FOR UPDATE at top level */
+                       if (PlannerQueryLevel > 1)
+                               elog(ERROR, "SELECT FOR UPDATE is not allowed in subselects");
+
                        foreach(l, parse->rowMarks)
                        {
                                Index           rti = lfirsti(l);
index e4834158765d3da6e621fc366c8768e8ce29ed12..2c33dba0ec5dbba8db82721b5d4fa718a5a5bbea 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: analyze.c,v 1.170 2000/12/05 19:57:55 tgl Exp $
+ *     $Id: analyze.c,v 1.171 2000/12/06 23:55:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,8 +37,6 @@
 #include "mb/pg_wchar.h"
 #endif
 
-void           CheckSelectForUpdate(Query *qry);       /* no points for style... */
-
 static Query *transformStmt(ParseState *pstate, Node *stmt);
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
 static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
index 6b842582b4325e98f43474017b9f35a15d92aafe..21372e0794df0c407728b8552e9c2811668184fa 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.84 2000/12/05 19:15:09 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.85 2000/12/06 23:55:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,9 +31,6 @@
 #include "utils/lsyscache.h"
 
 
-extern void CheckSelectForUpdate(Query *rule_action);  /* in analyze.c */
-
-
 static RewriteInfo *gatherRewriteMeta(Query *parsetree,
                                  Query *rule_action,
                                  Node *rule_qual,
@@ -100,29 +97,6 @@ gatherRewriteMeta(Query *parsetree,
        ChangeVarNodes(info->rule_qual,
                                   PRS2_OLD_VARNO + rt_length, rt_index, 0);
 
-       /*
-        * Update resultRelation too ... perhaps this should be done by
-        * Offset/ChangeVarNodes?
-        */
-       if (sub_action->resultRelation)
-       {
-               int                     result_reln;
-               int                     new_result_reln;
-
-               result_reln = sub_action->resultRelation;
-               switch (result_reln)
-               {
-                       case PRS2_OLD_VARNO:
-                               new_result_reln = rt_index;
-                               break;
-                       case PRS2_NEW_VARNO:
-                       default:
-                               new_result_reln = result_reln + rt_length;
-                               break;
-               }
-               sub_action->resultRelation = new_result_reln;
-       }
-
        /*
         * We want the main parsetree's rtable to end up as the concatenation
         * of its original contents plus those of all the relevant rule
@@ -336,8 +310,6 @@ ApplyRetrieveRule(Query *parsetree,
        {
                Index           innerrti = 1;
 
-               CheckSelectForUpdate(rule_action);
-
                /*
                 * Remove the view from the list of rels that will actually be
                 * marked FOR UPDATE by the executor.  It will still be access-
index 02aa2d76653cb6f9df5e826c1386dc16d84e4dde..0e84c5fa0409ec8b094d9fdf76357df3f49b2377 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.52 2000/12/05 19:15:09 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.53 2000/12/06 23:55:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -169,8 +169,29 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
         * sublevels_up doesn't get incremented prematurely.
         */
        if (node && IsA(node, Query))
-               query_tree_walker((Query *) node, OffsetVarNodes_walker,
+       {
+               Query  *qry = (Query *) node;
+               List   *l;
+
+               /*
+                * If we are starting at a Query, and sublevels_up is zero, then we
+                * must also fix rangetable indexes in the Query itself --- namely
+                * resultRelation and rowMarks entries.  sublevels_up cannot be zero
+                * when recursing into a subquery, so there's no need to have the
+                * same logic inside OffsetVarNodes_walker.
+                */
+               if (sublevels_up == 0)
+               {
+                       if (qry->resultRelation)
+                               qry->resultRelation += offset;
+                       foreach(l, qry->rowMarks)
+                       {
+                               lfirsti(l) += offset;
+                       }
+               }
+               query_tree_walker(qry, OffsetVarNodes_walker,
                                                  (void *) &context, true);
+       }
        else
                OffsetVarNodes_walker(node, &context);
 }
@@ -252,8 +273,30 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
         * sublevels_up doesn't get incremented prematurely.
         */
        if (node && IsA(node, Query))
-               query_tree_walker((Query *) node, ChangeVarNodes_walker,
+       {
+               Query  *qry = (Query *) node;
+               List   *l;
+
+               /*
+                * If we are starting at a Query, and sublevels_up is zero, then we
+                * must also fix rangetable indexes in the Query itself --- namely
+                * resultRelation and rowMarks entries.  sublevels_up cannot be zero
+                * when recursing into a subquery, so there's no need to have the
+                * same logic inside ChangeVarNodes_walker.
+                */
+               if (sublevels_up == 0)
+               {
+                       if (qry->resultRelation == rt_index)
+                               qry->resultRelation = new_index;
+                       foreach(l, qry->rowMarks)
+                       {
+                               if (lfirsti(l) == rt_index)
+                                       lfirsti(l) = new_index;
+                       }
+               }
+               query_tree_walker(qry, ChangeVarNodes_walker,
                                                  (void *) &context, true);
+       }
        else
                ChangeVarNodes_walker(node, &context);
 }
index 9d60e0f64c8f65c85a328feebcb20d93c5beea51..baf5b570529a0295e136a11c2a1494890f5b363a 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: analyze.h,v 1.12 2000/10/07 00:58:21 tgl Exp $
+ * $Id: analyze.h,v 1.13 2000/12/06 23:55:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,4 +17,6 @@
 
 extern List *parse_analyze(Node *parseTree, ParseState *parentParseState);
 
+extern void CheckSelectForUpdate(Query *qry);
+
 #endif  /* ANALYZE_H */