]> granicus.if.org Git - postgresql/commitdiff
Improve the representation of FOR UPDATE/FOR SHARE so that we can
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 30 Apr 2006 18:30:40 +0000 (18:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 30 Apr 2006 18:30:40 +0000 (18:30 +0000)
support both FOR UPDATE and FOR SHARE in one command, as well as both
NOWAIT and normal WAIT behavior.  The more general code is actually
simpler and cleaner.

29 files changed:
doc/src/sgml/ref/select.sgml
doc/src/sgml/ref/select_into.sgml
doc/src/sgml/sql.sgml
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/preptlist.c
src/backend/optimizer/prep/prepunion.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_relation.c
src/backend/parser/parse_type.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/tcop/utility.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/analyze.h
src/include/parser/parse_node.h
src/include/parser/parsetree.h

index f862d5f87374e4380346f11662965c1dbdbc92cb..b15aa361a0f656102b7ee4856ce64fad6f91e372 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.90 2005/11/01 21:09:51 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.91 2006/04/30 18:30:38 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -30,7 +30,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac
     [ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [, ...] ]
     [ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ]
     [ OFFSET <replaceable class="parameter">start</replaceable> ]
-    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
+    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ]
 
 where <replaceable class="parameter">from_item</replaceable> can be one of:
 
@@ -142,8 +142,8 @@ where <replaceable class="parameter">from_item</replaceable> can be one of:
 
     <listitem>
      <para>
-      If the <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
-      clause is specified, the
+      If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
+      is specified, the
       <command>SELECT</command> statement locks the selected rows
       against concurrent updates.  (See <xref linkend="sql-for-update-share"
       endterm="sql-for-update-share-title"> below.)
@@ -852,19 +852,27 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
     from performing <command>SELECT FOR SHARE</command>.
    </para>
 
-   <para>
-    It is currently not allowed for a single <command>SELECT</command>
-    statement to include both <literal>FOR UPDATE</literal> and
-    <literal>FOR SHARE</literal>, nor can different parts of the statement use
-    both <literal>NOWAIT</> and normal waiting mode.
-   </para>
-
    <para>
     If specific tables are named in <literal>FOR UPDATE</literal>
     or <literal>FOR SHARE</literal>,
     then only rows coming from those tables are locked; any other
     tables used in the <command>SELECT</command> are simply read as
-    usual.
+    usual.  A <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>
+    clause without a table list affects all tables used in the command.
+    If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> is
+    applied to a view or sub-query, it affects all tables used in
+    the view or sub-query.
+   </para>
+
+   <para>
+    Multiple <literal>FOR UPDATE</literal> and <literal>FOR SHARE</literal>
+    clauses can be written if it is necessary to specify different locking
+    behavior for different tables.  If the same table is mentioned (or
+    implicitly affected) by both <literal>FOR UPDATE</literal> and
+    <literal>FOR SHARE</literal> clauses, then it is processed as
+    <literal>FOR UPDATE</literal>.  Similarly, a table is processed
+    as <literal>NOWAIT</> if that is specified in any of the clauses
+    affecting it.
    </para>
 
    <para>
index d9967197b873fadc14cb2c67bf68cfba85ae398e..283fd4f8b2050b01b56b2235673e6bdd4360776d 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.36 2005/08/01 20:31:04 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.37 2006/04/30 18:30:38 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -31,7 +31,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac
     [ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ]
     [ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
     [ OFFSET <replaceable class="PARAMETER">start</replaceable> ]
-    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
+    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ]
 </synopsis>
  </refsynopsisdiv>
 
index 86539ded117a4fbe4330a3f37af0917502547e9c..b27400b4a456064e4cf879cd0dcea3d75c87105d 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.39 2006/03/10 19:10:49 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.40 2006/04/30 18:30:38 tgl Exp $ -->
 
  <chapter id="sql-intro">
   <title>SQL</title>
@@ -863,7 +863,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac
     [ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ]
     [ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
     [ OFFSET <replaceable class="PARAMETER">start</replaceable> ]
-    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
+    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ]
      </synopsis>
     </para>
 
index 31951db4c264216477e0861fb622462e9c428818..e8e0c8bd56d19fbe71e6bee1df214ec6fce29712 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.269 2006/03/05 15:58:25 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.270 2006/04/30 18:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -452,6 +452,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
        Relation        intoRelationDesc;
        bool            do_select_into;
        TupleDesc       tupType;
+       ListCell   *l;
 
        /*
         * Do permissions checks.  It's sufficient to examine the query's top
@@ -486,7 +487,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
                         * parseTree->resultRelations identifies them all
                         */
                        ResultRelInfo *resultRelInfo;
-                       ListCell   *l;
 
                        numResultRelations = list_length(resultRelations);
                        resultRelInfos = (ResultRelInfo *)
@@ -549,26 +549,21 @@ InitPlan(QueryDesc *queryDesc, int eflags)
         * Have to lock relations selected FOR UPDATE/FOR SHARE
         */
        estate->es_rowMarks = NIL;
-       estate->es_forUpdate = parseTree->forUpdate;
-       estate->es_rowNoWait = parseTree->rowNoWait;
-       if (parseTree->rowMarks != NIL)
+       foreach(l, parseTree->rowMarks)
        {
-               ListCell   *l;
-
-               foreach(l, parseTree->rowMarks)
-               {
-                       Index           rti = lfirst_int(l);
-                       Oid                     relid = getrelid(rti, rangeTable);
-                       Relation        relation;
-                       ExecRowMark *erm;
-
-                       relation = heap_open(relid, RowShareLock);
-                       erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
-                       erm->relation = relation;
-                       erm->rti = rti;
-                       snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rti);
-                       estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
-               }
+               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+               Oid                     relid = getrelid(rc->rti, rangeTable);
+               Relation        relation;
+               ExecRowMark *erm;
+
+               relation = heap_open(relid, RowShareLock);
+               erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
+               erm->relation = relation;
+               erm->rti = rc->rti;
+               erm->forUpdate = rc->forUpdate;
+               erm->noWait = rc->noWait;
+               snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rc->rti);
+               estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
        }
 
        /*
@@ -1222,7 +1217,7 @@ lnext:    ;
 
                                        tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
 
-                                       if (estate->es_forUpdate)
+                                       if (erm->forUpdate)
                                                lockmode = LockTupleExclusive;
                                        else
                                                lockmode = LockTupleShared;
@@ -1230,7 +1225,7 @@ lnext:    ;
                                        test = heap_lock_tuple(erm->relation, &tuple, &buffer,
                                                                                   &update_ctid, &update_xmax,
                                                                                   estate->es_snapshot->curcid,
-                                                                                  lockmode, estate->es_rowNoWait);
+                                                                                  lockmode, erm->noWait);
                                        ReleaseBuffer(buffer);
                                        switch (test)
                                        {
@@ -2258,8 +2253,6 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
                epqstate->es_param_exec_vals = (ParamExecData *)
                        palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
        epqstate->es_rowMarks = estate->es_rowMarks;
-       epqstate->es_forUpdate = estate->es_forUpdate;
-       epqstate->es_rowNoWait = estate->es_rowNoWait;
        epqstate->es_instrument = estate->es_instrument;
        epqstate->es_select_into = estate->es_select_into;
        epqstate->es_into_oids = estate->es_into_oids;
index 3696b5a208c47d05356d80a994a06b9bbdc92a42..d1a294f9bb8b2dd0f8d282714315ee0ed42fc230 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.133 2006/03/05 15:58:26 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.134 2006/04/30 18:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -204,8 +204,6 @@ CreateExecutorState(void)
        estate->es_processed = 0;
        estate->es_lastoid = InvalidOid;
        estate->es_rowMarks = NIL;
-       estate->es_forUpdate = false;
-       estate->es_rowNoWait = false;
 
        estate->es_instrument = false;
        estate->es_select_into = false;
index b060d38b5edb0ccb0e1f48c77d2c48bac109ece7..6bf342ab92c00c5b9de0569a9d0e5e051958843e 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.334 2006/04/22 01:25:58 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.335 2006/04/30 18:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1409,6 +1409,18 @@ _copyGroupClause(GroupClause *from)
        return newnode;
 }
 
+static RowMarkClause *
+_copyRowMarkClause(RowMarkClause *from)
+{
+       RowMarkClause *newnode = makeNode(RowMarkClause);
+
+       COPY_SCALAR_FIELD(rti);
+       COPY_SCALAR_FIELD(forUpdate);
+       COPY_SCALAR_FIELD(noWait);
+
+       return newnode;
+}
+
 static A_Expr *
 _copyAExpr(A_Expr *from)
 {
@@ -1650,7 +1662,7 @@ _copyLockingClause(LockingClause *from)
 
        COPY_NODE_FIELD(lockedRels);
        COPY_SCALAR_FIELD(forUpdate);
-       COPY_SCALAR_FIELD(nowait);
+       COPY_SCALAR_FIELD(noWait);
 
        return newnode;
 }
@@ -1673,9 +1685,6 @@ _copyQuery(Query *from)
        COPY_SCALAR_FIELD(hasSubLinks);
        COPY_NODE_FIELD(rtable);
        COPY_NODE_FIELD(jointree);
-       COPY_NODE_FIELD(rowMarks);
-       COPY_SCALAR_FIELD(forUpdate);
-       COPY_SCALAR_FIELD(rowNoWait);
        COPY_NODE_FIELD(targetList);
        COPY_NODE_FIELD(groupClause);
        COPY_NODE_FIELD(havingQual);
@@ -1683,6 +1692,7 @@ _copyQuery(Query *from)
        COPY_NODE_FIELD(sortClause);
        COPY_NODE_FIELD(limitOffset);
        COPY_NODE_FIELD(limitCount);
+       COPY_NODE_FIELD(rowMarks);
        COPY_NODE_FIELD(setOperations);
        COPY_NODE_FIELD(resultRelations);
 
@@ -3284,6 +3294,9 @@ copyObject(void *from)
                case T_GroupClause:
                        retval = _copyGroupClause(from);
                        break;
+               case T_RowMarkClause:
+                       retval = _copyRowMarkClause(from);
+                       break;
                case T_FkConstraint:
                        retval = _copyFkConstraint(from);
                        break;
index 94799d0c85f50a2b0129f70b60250911e27a8c41..7f7eb06fbcd8c37299293dd460f71cb6b1b4c68e 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.270 2006/04/22 01:25:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.271 2006/04/30 18:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -665,9 +665,6 @@ _equalQuery(Query *a, Query *b)
        COMPARE_SCALAR_FIELD(hasSubLinks);
        COMPARE_NODE_FIELD(rtable);
        COMPARE_NODE_FIELD(jointree);
-       COMPARE_NODE_FIELD(rowMarks);
-       COMPARE_SCALAR_FIELD(forUpdate);
-       COMPARE_SCALAR_FIELD(rowNoWait);
        COMPARE_NODE_FIELD(targetList);
        COMPARE_NODE_FIELD(groupClause);
        COMPARE_NODE_FIELD(havingQual);
@@ -675,6 +672,7 @@ _equalQuery(Query *a, Query *b)
        COMPARE_NODE_FIELD(sortClause);
        COMPARE_NODE_FIELD(limitOffset);
        COMPARE_NODE_FIELD(limitCount);
+       COMPARE_NODE_FIELD(rowMarks);
        COMPARE_NODE_FIELD(setOperations);
        COMPARE_NODE_FIELD(resultRelations);
 
@@ -1688,7 +1686,7 @@ _equalLockingClause(LockingClause *a, LockingClause *b)
 {
        COMPARE_NODE_FIELD(lockedRels);
        COMPARE_SCALAR_FIELD(forUpdate);
-       COMPARE_SCALAR_FIELD(nowait);
+       COMPARE_SCALAR_FIELD(noWait);
 
        return true;
 }
@@ -1723,6 +1721,16 @@ _equalSortClause(SortClause *a, SortClause *b)
        return true;
 }
 
+static bool
+_equalRowMarkClause(RowMarkClause *a, RowMarkClause *b)
+{
+       COMPARE_SCALAR_FIELD(rti);
+       COMPARE_SCALAR_FIELD(forUpdate);
+       COMPARE_SCALAR_FIELD(noWait);
+
+       return true;
+}
+
 static bool
 _equalFkConstraint(FkConstraint *a, FkConstraint *b)
 {
@@ -2297,6 +2305,9 @@ equal(void *a, void *b)
                        /* GroupClause is equivalent to SortClause */
                        retval = _equalSortClause(a, b);
                        break;
+               case T_RowMarkClause:
+                       retval = _equalRowMarkClause(a, b);
+                       break;
                case T_FkConstraint:
                        retval = _equalFkConstraint(a, b);
                        break;
index 314d68d2ef9076a614fa1556d77723412ce8bae4..25d3a112799e9b1802d7f32bf81f59ef5ff3ea06 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.273 2006/04/22 01:25:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.274 2006/04/30 18:30:39 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -1418,7 +1418,7 @@ _outLockingClause(StringInfo str, LockingClause *node)
 
        WRITE_NODE_FIELD(lockedRels);
        WRITE_BOOL_FIELD(forUpdate);
-       WRITE_BOOL_FIELD(nowait);
+       WRITE_BOOL_FIELD(noWait);
 }
 
 static void
@@ -1514,9 +1514,6 @@ _outQuery(StringInfo str, Query *node)
        WRITE_BOOL_FIELD(hasSubLinks);
        WRITE_NODE_FIELD(rtable);
        WRITE_NODE_FIELD(jointree);
-       WRITE_NODE_FIELD(rowMarks);
-       WRITE_BOOL_FIELD(forUpdate);
-       WRITE_BOOL_FIELD(rowNoWait);
        WRITE_NODE_FIELD(targetList);
        WRITE_NODE_FIELD(groupClause);
        WRITE_NODE_FIELD(havingQual);
@@ -1524,6 +1521,7 @@ _outQuery(StringInfo str, Query *node)
        WRITE_NODE_FIELD(sortClause);
        WRITE_NODE_FIELD(limitOffset);
        WRITE_NODE_FIELD(limitCount);
+       WRITE_NODE_FIELD(rowMarks);
        WRITE_NODE_FIELD(setOperations);
        WRITE_NODE_FIELD(resultRelations);
 }
@@ -1546,6 +1544,16 @@ _outGroupClause(StringInfo str, GroupClause *node)
        WRITE_OID_FIELD(sortop);
 }
 
+static void
+_outRowMarkClause(StringInfo str, RowMarkClause *node)
+{
+       WRITE_NODE_TYPE("ROWMARKCLAUSE");
+
+       WRITE_UINT_FIELD(rti);
+       WRITE_BOOL_FIELD(forUpdate);
+       WRITE_BOOL_FIELD(noWait);
+}
+
 static void
 _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
 {
@@ -2113,6 +2121,9 @@ _outNode(StringInfo str, void *obj)
                        case T_GroupClause:
                                _outGroupClause(str, obj);
                                break;
+                       case T_RowMarkClause:
+                               _outRowMarkClause(str, obj);
+                               break;
                        case T_SetOperationStmt:
                                _outSetOperationStmt(str, obj);
                                break;
index 9fdf02fd5682c13e3276cd5357d822414b8a1f8d..dd6995a1722e766137e478f858c096466586835e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.188 2006/04/22 01:25:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.189 2006/04/30 18:30:39 tgl Exp $
  *
  * NOTES
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -147,9 +147,6 @@ _readQuery(void)
        READ_BOOL_FIELD(hasSubLinks);
        READ_NODE_FIELD(rtable);
        READ_NODE_FIELD(jointree);
-       READ_NODE_FIELD(rowMarks);
-       READ_BOOL_FIELD(forUpdate);
-       READ_BOOL_FIELD(rowNoWait);
        READ_NODE_FIELD(targetList);
        READ_NODE_FIELD(groupClause);
        READ_NODE_FIELD(havingQual);
@@ -157,6 +154,7 @@ _readQuery(void)
        READ_NODE_FIELD(sortClause);
        READ_NODE_FIELD(limitOffset);
        READ_NODE_FIELD(limitCount);
+       READ_NODE_FIELD(rowMarks);
        READ_NODE_FIELD(setOperations);
        READ_NODE_FIELD(resultRelations);
 
@@ -219,6 +217,21 @@ _readGroupClause(void)
        READ_DONE();
 }
 
+/*
+ * _readRowMarkClause
+ */
+static RowMarkClause *
+_readRowMarkClause(void)
+{
+       READ_LOCALS(RowMarkClause);
+
+       READ_UINT_FIELD(rti);
+       READ_BOOL_FIELD(forUpdate);
+       READ_BOOL_FIELD(noWait);
+
+       READ_DONE();
+}
+
 /*
  * _readSetOperationStmt
  */
@@ -934,6 +947,8 @@ parseNodeString(void)
                return_value = _readSortClause();
        else if (MATCH("GROUPCLAUSE", 11))
                return_value = _readGroupClause();
+       else if (MATCH("ROWMARKCLAUSE", 13))
+               return_value = _readRowMarkClause();
        else if (MATCH("SETOPERATIONSTMT", 16))
                return_value = _readSetOperationStmt();
        else if (MATCH("ALIAS", 5))
index 9ce72837b9c788b6c3c4743b5b88f881df211e52..f35e87962dac21f7eafa04f64e27a2b5680538f7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.144 2006/03/05 15:58:28 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.145 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -268,7 +268,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
         * currently supposes that every rowMark relation is involved in every
         * row returned by the query.)
         */
-       if (list_member_int(root->parse->rowMarks, parentRTindex))
+       if (get_rowmark(root->parse, parentRTindex))
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
index 9750789f0c2cac9a39f8fd05b047e0617b806c1e..174b20f622a6963a17ff46771b3e458d21f7f9cb 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.209 2006/04/25 16:54:09 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.210 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -843,7 +843,7 @@ create_indexscan_plan(PlannerInfo *root,
                        if (best_path->indexinfo->indpred)
                        {
                                if (baserelid != root->parse->resultRelation &&
-                                       !list_member_int(root->parse->rowMarks, baserelid))
+                                       get_rowmark(root->parse, baserelid) == NULL)
                                        if (predicate_implied_by(clausel,
                                                                                         best_path->indexinfo->indpred))
                                                continue;
@@ -962,7 +962,7 @@ create_bitmap_scan_plan(PlannerInfo *root,
                                if (ipath->indexinfo->indpred)
                                {
                                        if (baserelid != root->parse->resultRelation &&
-                                               !list_member_int(root->parse->rowMarks, baserelid))
+                                               get_rowmark(root->parse, baserelid) == NULL)
                                                if (predicate_implied_by(clausel,
                                                                                                 ipath->indexinfo->indpred))
                                                        continue;
index ae538c9cc00e62e16869f4e7e79f6b3acd109873..f3b6df46f2165fc25914f10fc0953e5b546cd14f 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.37 2006/03/07 01:00:15 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.38 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -409,29 +409,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 
        /*
         * Pull up any FOR UPDATE/SHARE markers, too.  (OffsetVarNodes
-        * already adjusted the marker values, so just list_concat the
-        * list.)
-        *
-        * Executor can't handle multiple FOR UPDATE/SHARE/NOWAIT flags,
-        * so complain if they are valid but different
+        * already adjusted the marker rtindexes, so just concat the lists.)
         */
-       if (parse->rowMarks && subquery->rowMarks)
-       {
-               if (parse->forUpdate != subquery->forUpdate)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
-               if (parse->rowNoWait != subquery->rowNoWait)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cannot use both wait and NOWAIT in one query")));
-       }
        parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
-       if (subquery->rowMarks)
-       {
-               parse->forUpdate = subquery->forUpdate;
-               parse->rowNoWait = subquery->rowNoWait;
-       }
 
        /*
         * We also have to fix the relid sets of any parent InClauseInfo
index 5a37b86eb5bcb6f6f54c3edc6bc100411f1da1b2..0e06d0e88818470350f9e063438cfccf850d1759 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.81 2006/04/05 22:11:55 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.82 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -116,7 +116,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
                 * invalid.  This is also checked at parse time, but that's
                 * insufficient because of rule substitution, query pullup, etc.
                 */
-               CheckSelectLocking(parse, parse->forUpdate);
+               CheckSelectLocking(parse);
 
                /*
                 * Currently the executor only supports FOR UPDATE/SHARE at top level
@@ -128,19 +128,19 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
 
                foreach(l, parse->rowMarks)
                {
-                       Index           rti = lfirst_int(l);
+                       RowMarkClause *rc = (RowMarkClause *) lfirst(l);
                        Var                *var;
                        char       *resname;
                        TargetEntry *tle;
 
-                       var = makeVar(rti,
+                       var = makeVar(rc->rti,
                                                  SelfItemPointerAttributeNumber,
                                                  TIDOID,
                                                  -1,
                                                  0);
 
                        resname = (char *) palloc(32);
-                       snprintf(resname, 32, "ctid%u", rti);
+                       snprintf(resname, 32, "ctid%u", rc->rti);
 
                        tle = makeTargetEntry((Expr *) var,
                                                                  list_length(tlist) + 1,
index 8c0457295c67ef3632e20f73a37f0519051bb75c..3b9e740cadff34fb4d405dcf9953480ebbe795fe 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.131 2006/03/05 15:58:31 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.132 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -816,7 +816,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
         */
        if (rti == parse->resultRelation)
                lockmode = RowExclusiveLock;
-       else if (list_member_int(parse->rowMarks, rti))
+       else if (get_rowmark(parse, rti))
                lockmode = RowShareLock;
        else
                lockmode = AccessShareLock;
index d69c0f17864c62a582f89296f9b2f3d1eccdabee..566c9a0488df68c94effea9e3f59d82da930eb18 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.333 2006/04/22 01:25:59 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.334 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1805,6 +1805,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 {
        Query      *qry = makeNode(Query);
        Node       *qual;
+       ListCell   *l;
 
        qry->commandType = CMD_SELECT;
 
@@ -1870,8 +1871,10 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
                parseCheckAggregates(pstate, qry);
 
-       if (stmt->lockingClause)
-               transformLockingClause(qry, stmt->lockingClause);
+       foreach(l, stmt->lockingClause)
+       {
+               transformLockingClause(qry, (LockingClause *) lfirst(l));
+       }
 
        return qry;
 }
@@ -1899,10 +1902,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        List       *sortClause;
        Node       *limitOffset;
        Node       *limitCount;
-       LockingClause *lockingClause;
+       List       *lockingClause;
        Node       *node;
        ListCell   *left_tlist,
-                          *dtlist;
+                          *dtlist,
+                          *l;
        List       *targetvars,
                           *targetnames,
                           *sv_relnamespace,
@@ -1942,7 +1946,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        stmt->sortClause = NIL;
        stmt->limitOffset = NULL;
        stmt->limitCount = NULL;
-       stmt->lockingClause = NULL;
+       stmt->lockingClause = NIL;
 
        /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
        if (lockingClause)
@@ -2084,8 +2088,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
                parseCheckAggregates(pstate, qry);
 
-       if (lockingClause)
-               transformLockingClause(qry, lockingClause);
+       foreach(l, lockingClause)
+       {
+               transformLockingClause(qry, (LockingClause *) lfirst(l));
+       }
 
        return qry;
 }
@@ -2743,44 +2749,34 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
 
 /* exported so planner can check again after rewriting, query pullup, etc */
 void
-CheckSelectLocking(Query *qry, bool forUpdate)
+CheckSelectLocking(Query *qry)
 {
-       const char *operation;
-
-       if (forUpdate)
-               operation = "SELECT FOR UPDATE";
-       else
-               operation = "SELECT FOR SHARE";
-
        if (qry->setOperations)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-               /* translator: %s is a SQL command, like SELECT FOR UPDATE */
-               errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", operation)));
+                                errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
        if (qry->distinctClause != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-               /* translator: %s is a SQL command, like SELECT FOR UPDATE */
-                          errmsg("%s is not allowed with DISTINCT clause", operation)));
+                                errmsg("SELECT FOR UPDATE/SHARE is not allowed with DISTINCT clause")));
        if (qry->groupClause != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-               /* translator: %s is a SQL command, like SELECT FOR UPDATE */
-                          errmsg("%s is not allowed with GROUP BY clause", operation)));
+                                errmsg("SELECT FOR UPDATE/SHARE is not allowed with GROUP BY clause")));
        if (qry->havingQual != NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-               /* translator: %s is a SQL command, like SELECT FOR UPDATE */
-                                errmsg("%s is not allowed with HAVING clause", operation)));
+                                errmsg("SELECT FOR UPDATE/SHARE is not allowed with HAVING clause")));
        if (qry->hasAggs)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-               /* translator: %s is a SQL command, like SELECT FOR UPDATE */
-                  errmsg("%s is not allowed with aggregate functions", operation)));
+                                errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions")));
 }
 
 /*
- * Convert FOR UPDATE/SHARE name list into rowMarks list of integer relids
+ * Transform a FOR UPDATE/SHARE clause
+ *
+ * This basically involves replacing names by integer relids.
  *
  * NB: if you need to change this, see also markQueryForLocking()
  * in rewriteHandler.c.
@@ -2789,35 +2785,18 @@ static void
 transformLockingClause(Query *qry, LockingClause *lc)
 {
        List       *lockedRels = lc->lockedRels;
-       List       *rowMarks;
        ListCell   *l;
        ListCell   *rt;
        Index           i;
        LockingClause *allrels;
 
-       if (qry->rowMarks)
-       {
-               if (lc->forUpdate != qry->forUpdate)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                       errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
-               if (lc->nowait != qry->rowNoWait)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cannot use both wait and NOWAIT in one query")));
-       }
-       qry->forUpdate = lc->forUpdate;
-       qry->rowNoWait = lc->nowait;
-
-       CheckSelectLocking(qry, lc->forUpdate);
+       CheckSelectLocking(qry);
 
        /* make a clause we can pass down to subqueries to select all rels */
        allrels = makeNode(LockingClause);
        allrels->lockedRels = NIL;      /* indicates all rels */
        allrels->forUpdate = lc->forUpdate;
-       allrels->nowait = lc->nowait;
-
-       rowMarks = qry->rowMarks;
+       allrels->noWait = lc->noWait;
 
        if (lockedRels == NIL)
        {
@@ -2831,8 +2810,7 @@ transformLockingClause(Query *qry, LockingClause *lc)
                        switch (rte->rtekind)
                        {
                                case RTE_RELATION:
-                                       /* use list_append_unique to avoid duplicates */
-                                       rowMarks = list_append_unique_int(rowMarks, i);
+                                       applyLockingClause(qry, i, lc->forUpdate, lc->noWait);
                                        rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                                        break;
                                case RTE_SUBQUERY:
@@ -2867,8 +2845,8 @@ transformLockingClause(Query *qry, LockingClause *lc)
                                        switch (rte->rtekind)
                                        {
                                                case RTE_RELATION:
-                                                       /* use list_append_unique to avoid duplicates */
-                                                       rowMarks = list_append_unique_int(rowMarks, i);
+                                                       applyLockingClause(qry, i,
+                                                                                          lc->forUpdate, lc->noWait);
                                                        rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                                                        break;
                                                case RTE_SUBQUERY:
@@ -2909,8 +2887,41 @@ transformLockingClause(Query *qry, LockingClause *lc)
                                                                relname)));
                }
        }
+}
+
+/*
+ * Record locking info for a single rangetable item
+ */
+void
+applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
+{
+       RowMarkClause *rc;
+
+       /* Check for pre-existing entry for same rtindex */
+       if ((rc = get_rowmark(qry, rtindex)) != NULL)
+       {
+               /*
+                * If the same RTE is specified both FOR UPDATE and FOR SHARE,
+                * treat it as FOR UPDATE.  (Reasonable, since you can't take
+                * both a shared and exclusive lock at the same time; it'll
+                * end up being exclusive anyway.)
+                *
+                * We also consider that NOWAIT wins if it's specified both ways.
+                * This is a bit more debatable but raising an error doesn't
+                * seem helpful.  (Consider for instance SELECT FOR UPDATE NOWAIT
+                * from a view that internally contains a plain FOR UPDATE spec.)
+                */
+               rc->forUpdate |= forUpdate;
+               rc->noWait |= noWait;
+               return;
+       }
 
-       qry->rowMarks = rowMarks;
+       /* Make a new RowMarkClause */
+       rc = makeNode(RowMarkClause);
+       rc->rti = rtindex;
+       rc->forUpdate = forUpdate;
+       rc->noWait = noWait;
+       qry->rowMarks = lappend(qry->rowMarks, rc);
 }
 
 
index 8870261e44134192c24eee648c3ef067160b67b0..69e7a2014259c43b0f7e622fb87039655d5d4f54 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.543 2006/04/27 00:33:45 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.544 2006/04/30 18:30:39 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -96,7 +96,7 @@ static List *check_func_name(List *names);
 static List *extractArgTypes(List *parameters);
 static SelectStmt *findLeftmostSelect(SelectStmt *node);
 static void insertSelectOptions(SelectStmt *stmt,
-                                                               List *sortClause, Node *lockingClause,
+                                                               List *sortClause, List *lockingClause,
                                                                Node *limitOffset, Node *limitCount);
 static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
 static Node *doNegate(Node *n, int location);
@@ -253,7 +253,8 @@ static void doNegateFloat(Value *v);
 %type <oncommit> OnCommitOption
 %type <withoids> OptWithOids
 
-%type <node>   for_locking_clause opt_for_locking_clause
+%type <node>   for_locking_item
+%type <list>   for_locking_clause opt_for_locking_clause for_locking_items
 %type <list>   locked_rels_list
 %type <boolean>        opt_all
 
@@ -5400,7 +5401,7 @@ select_no_parens:
                        simple_select                                           { $$ = $1; }
                        | select_clause sort_clause
                                {
-                                       insertSelectOptions((SelectStmt *) $1, $2, NULL,
+                                       insertSelectOptions((SelectStmt *) $1, $2, NIL,
                                                                                NULL, NULL);
                                        $$ = $1;
                                }
@@ -5644,12 +5645,27 @@ having_clause:
                ;
 
 for_locking_clause:
+                       for_locking_items                                               { $$ = $1; }
+                       | FOR READ ONLY                                                 { $$ = NIL; }
+               ;
+
+opt_for_locking_clause:
+                       for_locking_clause                                              { $$ = $1; }
+                       | /* EMPTY */                                                   { $$ = NIL; }
+               ;
+
+for_locking_items:
+                       for_locking_item                                                { $$ = list_make1($1); }
+                       | for_locking_items for_locking_item    { $$ = lappend($1, $2); }
+               ;
+
+for_locking_item:
                        FOR UPDATE locked_rels_list opt_nowait
                                {
                                        LockingClause *n = makeNode(LockingClause);
                                        n->lockedRels = $3;
                                        n->forUpdate = TRUE;
-                                       n->nowait = $4;
+                                       n->noWait = $4;
                                        $$ = (Node *) n;
                                }
                        | FOR SHARE locked_rels_list opt_nowait
@@ -5657,15 +5673,9 @@ for_locking_clause:
                                        LockingClause *n = makeNode(LockingClause);
                                        n->lockedRels = $3;
                                        n->forUpdate = FALSE;
-                                       n->nowait = $4;
+                                       n->noWait = $4;
                                        $$ = (Node *) n;
                                }
-                       | FOR READ ONLY                                                 { $$ = NULL; }
-               ;
-
-opt_for_locking_clause:
-                       for_locking_clause                                              { $$ = $1; }
-                       | /* EMPTY */                                                   { $$ = NULL; }
                ;
 
 locked_rels_list:
@@ -8976,7 +8986,7 @@ findLeftmostSelect(SelectStmt *node)
  */
 static void
 insertSelectOptions(SelectStmt *stmt,
-                                       List *sortClause, Node *lockingClause,
+                                       List *sortClause, List *lockingClause,
                                        Node *limitOffset, Node *limitCount)
 {
        /*
@@ -8991,14 +9001,8 @@ insertSelectOptions(SelectStmt *stmt,
                                         errmsg("multiple ORDER BY clauses not allowed")));
                stmt->sortClause = sortClause;
        }
-       if (lockingClause)
-       {
-               if (stmt->lockingClause)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed")));
-               stmt->lockingClause = (LockingClause *) lockingClause;
-       }
+       /* We can handle multiple locking clauses, though */
+       stmt->lockingClause = list_concat(stmt->lockingClause, lockingClause);
        if (limitOffset)
        {
                if (stmt->limitOffset)
index d3e138e8ecb7a87909409479fa6916da99553060..10f71712ff6a83a4bd823dff5f4ebfd57ec26a38 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.122 2006/03/23 00:19:30 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.123 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1001,6 +1001,8 @@ addRangeTableEntryForJoin(ParseState *pstate,
 
 /*
  * Has the specified refname been selected FOR UPDATE/FOR SHARE?
+ *
+ * Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE.
  */
 static bool
 isLockedRel(ParseState *pstate, char *refname)
@@ -1008,9 +1010,13 @@ isLockedRel(ParseState *pstate, char *refname)
        /* Outer loop to check parent query levels as well as this one */
        while (pstate != NULL)
        {
-               if (pstate->p_locking_clause)
+               ListCell   *l;
+
+               foreach(l, pstate->p_locking_clause)
                {
-                       if (pstate->p_locking_clause->lockedRels == NIL)
+                       LockingClause *lc = (LockingClause *) lfirst(l);
+
+                       if (lc->lockedRels == NIL)
                        {
                                /* all tables used in query */
                                return true;
@@ -1018,11 +1024,11 @@ isLockedRel(ParseState *pstate, char *refname)
                        else
                        {
                                /* just the named tables */
-                               ListCell   *l;
+                               ListCell   *l2;
 
-                               foreach(l, pstate->p_locking_clause->lockedRels)
+                               foreach(l2, lc->lockedRels)
                                {
-                                       char       *rname = strVal(lfirst(l));
+                                       char       *rname = strVal(lfirst(l2));
 
                                        if (strcmp(refname, rname) == 0)
                                                return true;
@@ -1702,6 +1708,26 @@ get_tle_by_resno(List *tlist, AttrNumber resno)
        return NULL;
 }
 
+/*
+ * Given a Query and rangetable index, return relation's RowMarkClause if any
+ *
+ * Returns NULL if relation is not selected FOR UPDATE/SHARE
+ */
+RowMarkClause *
+get_rowmark(Query *qry, Index rtindex)
+{
+       ListCell   *l;
+
+       foreach(l, qry->rowMarks)
+       {
+               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+               if (rc->rti == rtindex)
+                       return rc;
+       }
+       return NULL;
+}
+
 /*
  *     given relation and att name, return attnum of variable
  *
index fec4552c9c4129c9b9ce467244e8072ec4c93ae7..5965bf5ae41b6bae55755fffe1ee952c803a7f72 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.80 2006/04/04 19:35:35 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.81 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -432,7 +432,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
                stmt->sortClause != NIL ||
                stmt->limitOffset != NULL ||
                stmt->limitCount != NULL ||
-               stmt->lockingClause != NULL ||
+               stmt->lockingClause != NIL ||
                stmt->op != SETOP_NONE)
                goto fail;
        if (list_length(stmt->targetList) != 1)
index 6d1ace66f1afe07b666f24c5dee3c886e742882d..d3052f8da2aefe0a2d68493b752807c38f84fc27 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.162 2006/04/05 22:11:55 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.163 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -130,7 +130,7 @@ AcquireRewriteLocks(Query *parsetree)
                                 */
                                if (rt_index == parsetree->resultRelation)
                                        lockmode = RowExclusiveLock;
-                               else if (list_member_int(parsetree->rowMarks, rt_index))
+                               else if (get_rowmark(parsetree, rt_index))
                                        lockmode = RowShareLock;
                                else
                                        lockmode = AccessShareLock;
@@ -907,6 +907,7 @@ ApplyRetrieveRule(Query *parsetree,
        Query      *rule_action;
        RangeTblEntry *rte,
                           *subrte;
+       RowMarkClause *rc;
 
        if (list_length(rule->actions) != 1)
                elog(ERROR, "expected just one rule action");
@@ -954,20 +955,20 @@ ApplyRetrieveRule(Query *parsetree,
        /*
         * FOR UPDATE/SHARE of view?
         */
-       if (list_member_int(parsetree->rowMarks, rt_index))
+       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 UPDATE/SHARE by the executor.  It will still be access-checked
                 * for write access, though.
                 */
-               parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index);
+               parsetree->rowMarks = list_delete_ptr(parsetree->rowMarks, rc);
 
                /*
                 * Set up the view's referenced tables as if FOR UPDATE/SHARE.
                 */
-               markQueryForLocking(rule_action, parsetree->forUpdate,
-                                                       parsetree->rowNoWait, true);
+               markQueryForLocking(rule_action, rc->forUpdate,
+                                                       rc->noWait, true);
        }
 
        return parsetree;
@@ -987,20 +988,6 @@ markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew)
        Index           rti = 0;
        ListCell   *l;
 
-       if (qry->rowMarks)
-       {
-               if (forUpdate != qry->forUpdate)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                       errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
-               if (noWait != qry->rowNoWait)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cannot use both wait and NOWAIT in one query")));
-       }
-       qry->forUpdate = forUpdate;
-       qry->rowNoWait = noWait;
-
        foreach(l, qry->rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
@@ -1014,7 +1001,7 @@ markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew)
 
                if (rte->rtekind == RTE_RELATION)
                {
-                       qry->rowMarks = list_append_unique_int(qry->rowMarks, rti);
+                       applyLockingClause(qry, rti, forUpdate, noWait);
                        rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                }
                else if (rte->rtekind == RTE_SUBQUERY)
index 175df7a695a1ffda9e048410a655b016b760a4d9..906e7c4a04be16d99506f584014d129687c83e6d 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.98 2006/04/05 22:11:55 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.99 2006/04/30 18:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -240,7 +240,11 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
                        if (qry->resultRelation)
                                qry->resultRelation += offset;
                        foreach(l, qry->rowMarks)
-                               lfirst_int(l) += offset;
+                       {
+                               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+                               rc->rti += offset;
+                       }
                }
                query_tree_walker(qry, OffsetVarNodes_walker,
                                                  (void *) &context, 0);
@@ -395,8 +399,10 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
                                qry->resultRelation = new_index;
                        foreach(l, qry->rowMarks)
                        {
-                               if (lfirst_int(l) == rt_index)
-                                       lfirst_int(l) = new_index;
+                               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+                               if (rc->rti == rt_index)
+                                       rc->rti = new_index;
                        }
                }
                query_tree_walker(qry, ChangeVarNodes_walker,
index b54d50702b0fc08f242e196c96a45b25bf326c72..5729b58450a651e4ce7214d2504f831a55b24ad2 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.256 2006/04/15 17:45:41 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.257 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1796,7 +1796,7 @@ CreateQueryTag(Query *parsetree)
                                tag = "SELECT INTO";
                        else if (parsetree->rowMarks != NIL)
                        {
-                               if (parsetree->forUpdate)
+                               if (((RowMarkClause *) linitial(parsetree->rowMarks))->forUpdate)
                                        tag = "SELECT FOR UPDATE";
                                else
                                        tag = "SELECT FOR SHARE";
index c0db64bf8960d45c40eb12d6a789f9a351967038..67897a2938a61c87b9b9897df7b2bbe90b853b0b 100644 (file)
@@ -2,7 +2,7 @@
  * ruleutils.c - Functions to convert stored expressions/querytrees
  *                             back to source text
  *
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.220 2006/04/22 01:26:00 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.221 2006/04/30 18:30:40 tgl Exp $
  **********************************************************************/
 
 #include "postgres.h"
@@ -1858,27 +1858,21 @@ get_select_query_def(Query *query, deparse_context *context,
                        get_rule_expr(query->limitCount, context, false);
        }
 
-       /* Add the FOR UPDATE/SHARE clause if present */
-       if (query->rowMarks != NIL)
+       /* Add FOR UPDATE/SHARE clauses if present */
+       foreach(l, query->rowMarks)
        {
-               if (query->forUpdate)
-                       appendContextKeyword(context, " FOR UPDATE OF ",
+               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+               RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
+
+               if (rc->forUpdate)
+                       appendContextKeyword(context, " FOR UPDATE",
                                                                 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
                else
-                       appendContextKeyword(context, " FOR SHARE OF ",
+                       appendContextKeyword(context, " FOR SHARE",
                                                                 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
-               sep = "";
-               foreach(l, query->rowMarks)
-               {
-                       int                     rtindex = lfirst_int(l);
-                       RangeTblEntry *rte = rt_fetch(rtindex, query->rtable);
-
-                       appendStringInfo(buf, "%s%s",
-                                                        sep,
-                                                        quote_identifier(rte->eref->aliasname));
-                       sep = ", ";
-               }
-               if (query->rowNoWait)
+               appendStringInfo(buf, " OF %s",
+                                                quote_identifier(rte->eref->aliasname));
+               if (rc->noWait)
                        appendStringInfo(buf, " NOWAIT");
        }
 }
index f3c037f97c071460fc8a37a8181dcb8fe7feba96..4c76aa70fa82ab23ec9467e52308a9bd0f359fc9 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.327 2006/04/30 02:09:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.328 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200604291
+#define CATALOG_VERSION_NO     200604301
 
 #endif
index 2d346df3596f02de5fd777cae8a308de958fd3f9..7a9a651e1e5d173bf372ea68c292308e555e9d29 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.149 2006/03/05 15:58:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.150 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -321,8 +321,6 @@ typedef struct EState
        uint32          es_processed;   /* # of tuples processed */
        Oid                     es_lastoid;             /* last oid processed (by INSERT) */
        List       *es_rowMarks;        /* not good place, but there is no other */
-       bool            es_forUpdate;   /* true = FOR UPDATE, false = FOR SHARE */
-       bool            es_rowNoWait;   /* FOR UPDATE/SHARE NOWAIT option */
 
        bool            es_instrument;  /* true requests runtime instrumentation */
        bool            es_select_into; /* true if doing SELECT INTO */
@@ -351,6 +349,8 @@ typedef struct ExecRowMark
 {
        Relation        relation;               /* opened and RowShareLock'd relation */
        Index           rti;                    /* its range table index */
+       bool            forUpdate;              /* true = FOR UPDATE, false = FOR SHARE */
+       bool            noWait;                 /* NOWAIT option */
        char            resname[32];    /* name for its ctid junk attribute */
 } ExecRowMark;
 
index fe5ab018f5daf137a04911e0d31cb58ecb68be37..53f3ee1d610e9efaea1819c7d90c4d02d7a34c8c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.185 2006/04/15 17:45:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.186 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -320,6 +320,7 @@ typedef enum NodeTag
        T_InhRelation,
        T_FunctionParameter,
        T_LockingClause,
+       T_RowMarkClause,
 
        /*
         * TAGS FOR RANDOM OTHER STUFF
index a0a31be51be06a0b005f135f5348682aa843f5c3..896b426370dbf1dd7f06c4ad4b33dabbfcbe9562 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.309 2006/04/30 02:09:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.310 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,7 +57,7 @@ typedef uint32 AclMode;                       /* a bitmask of privilege bits */
 #define ACL_USAGE              (1<<8)  /* for languages and namespaces */
 #define ACL_CREATE             (1<<9)  /* for namespaces and databases */
 #define ACL_CREATE_TEMP (1<<10) /* for databases */
-#define ACL_CONNECT    (1<<11) /* for database connection privilege */
+#define ACL_CONNECT            (1<<11) /* for databases */
 #define N_ACL_RIGHTS   12              /* 1 plus the last 1<<x */
 #define ACL_NO_RIGHTS  0
 /* Currently, SELECT ... FOR UPDATE/FOR SHARE requires UPDATE privileges */
@@ -102,13 +102,6 @@ typedef struct Query
        List       *rtable;                     /* list of range table entries */
        FromExpr   *jointree;           /* table join tree (FROM and WHERE clauses) */
 
-       List       *rowMarks;           /* integer list of RT indexes of relations
-                                                                * that are selected FOR UPDATE/SHARE */
-
-       bool            forUpdate;              /* true if rowMarks are FOR UPDATE, false if
-                                                                * they are FOR SHARE */
-       bool            rowNoWait;              /* FOR UPDATE/SHARE NOWAIT option */
-
        List       *targetList;         /* target list (of TargetEntry) */
 
        List       *groupClause;        /* a list of GroupClause's */
@@ -122,6 +115,8 @@ typedef struct Query
        Node       *limitOffset;        /* # of result tuples to skip */
        Node       *limitCount;         /* # of result tuples to return */
 
+       List       *rowMarks;           /* a list of RowMarkClause's */
+
        Node       *setOperations;      /* set-operation tree if this is top level of
                                                                 * a UNION/INTERSECT/EXCEPT query */
 
@@ -448,7 +443,7 @@ typedef struct LockingClause
        NodeTag         type;
        List       *lockedRels;         /* FOR UPDATE or FOR SHARE relations */
        bool            forUpdate;              /* true = FOR UPDATE, false = FOR SHARE */
-       bool            nowait;                 /* NOWAIT option */
+       bool            noWait;                 /* NOWAIT option */
 } LockingClause;
 
 
@@ -615,6 +610,19 @@ typedef struct SortClause
  */
 typedef SortClause GroupClause;
 
+/*
+ * RowMarkClause -
+ *        representation of FOR UPDATE/SHARE clauses
+ *
+ * We create a separate RowMarkClause node for each target relation
+ */
+typedef struct RowMarkClause
+{
+       NodeTag         type;
+       Index           rti;                    /* range table index of target relation */
+       bool            forUpdate;              /* true = FOR UPDATE, false = FOR SHARE */
+       bool            noWait;                 /* NOWAIT option */
+} RowMarkClause;
 
 /*****************************************************************************
  *             Optimizable Statements
@@ -724,7 +732,7 @@ typedef struct SelectStmt
        List       *sortClause;         /* sort clause (a list of SortBy's) */
        Node       *limitOffset;        /* # of result tuples to skip */
        Node       *limitCount;         /* # of result tuples to return */
-       LockingClause *lockingClause;           /* FOR UPDATE/FOR SHARE */
+       List       *lockingClause;      /* FOR UPDATE (list of LockingClause's) */
 
        /*
         * These fields are used only in upper-level SelectStmts.
index 57188b9fce96976da4c97a7ba364d617a3435e93..0b245c77e8390b36f0b27cb859d655aa85a2205b 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.32 2006/03/14 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.33 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,8 @@ extern List *parse_analyze_varparams(Node *parseTree, const char *sourceText,
                                                                         Oid **paramTypes, int *numParams);
 extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
 extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
-extern void CheckSelectLocking(Query *qry, bool forUpdate);
+extern void CheckSelectLocking(Query *qry);
+extern void applyLockingClause(Query *qry, Index rtindex,
+                                                          bool forUpdate, bool noWait);
 
 #endif   /* ANALYZE_H */
index 2c23cf78946600cb32fc331c4fc78d692aa58bb7..1a28d099546f170b81fe50d03065d0e5bc7c92ef 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.48 2006/03/14 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.49 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -71,8 +71,8 @@ typedef struct ParseState
        Oid                *p_paramtypes;       /* OIDs of types for $n parameter symbols */
        int                     p_numparams;    /* allocated size of p_paramtypes[] */
        int                     p_next_resno;   /* next targetlist resno to assign */
-       LockingClause *p_locking_clause;        /* FOR UPDATE/FOR SHARE info */
-       Node       *p_value_substitute;         /* what to replace VALUE with, if any */
+       List       *p_locking_clause;   /* raw FOR UPDATE/FOR SHARE info */
+       Node       *p_value_substitute; /* what to replace VALUE with, if any */
        bool            p_variableparams;
        bool            p_hasAggs;
        bool            p_hasSubLinks;
index 5cd5002109fb4a3e8e2f42c97f00ed3f7a2f9814..d9b65aec8c8245cbd0ac184bfd2d707a70e5de38 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.32 2006/03/05 15:58:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.33 2006/04/30 18:30:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,4 +70,11 @@ extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
 
 extern TargetEntry *get_tle_by_resno(List *tlist, AttrNumber resno);
 
+/* ----------------
+ *             FOR UPDATE/SHARE info
+ * ----------------
+ */
+
+extern RowMarkClause *get_rowmark(Query *qry, Index rtindex);
+
 #endif   /* PARSETREE_H */