]> granicus.if.org Git - postgresql/commitdiff
Add NOWAIT option to SELECT FOR UPDATE/SHARE.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Aug 2005 20:31:16 +0000 (20:31 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Aug 2005 20:31:16 +0000 (20:31 +0000)
Original patch by Hans-Juergen Schoenig, revisions by Karel Zak
and Tom Lane.

28 files changed:
doc/src/sgml/ref/select.sgml
doc/src/sgml/ref/select_into.sgml
doc/src/sgml/sql.sgml
src/backend/access/heap/heapam.c
src/backend/access/transam/multixact.c
src/backend/commands/trigger.c
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/prep/prepjointree.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/storage/lmgr/lmgr.c
src/backend/utils/adt/ruleutils.c
src/include/access/heapam.h
src/include/access/multixact.h
src/include/catalog/catversion.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/parse_node.h
src/include/storage/lmgr.h

index d29f8e48e0e5d8da4a2621f08aca1296b9959023..48a192d1b90fe0a0c7531403b0f93157395d177e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.88 2005/07/14 06:17:36 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.89 2005/08/01 20:31:04 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> [, ...] ] ]
+    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
 
 where <replaceable class="parameter">from_item</replaceable> can be one of:
 
@@ -151,7 +151,7 @@ where <replaceable class="parameter">from_item</replaceable> can be one of:
     </listitem>
    </orderedlist>
   </para>
-   
+
   <para>
    You must have <literal>SELECT</literal> privilege on a table to
    read its values.  The use of <literal>FOR UPDATE</literal> or
@@ -506,7 +506,7 @@ HAVING <replaceable class="parameter">condition</replaceable>
     <replaceable class="parameter">select_statement</replaceable> is
     any <command>SELECT</command> statement without an <literal>ORDER
     BY</>, <literal>LIMIT</>, <literal>FOR UPDATE</literal>, or
-    <literal>FOR SHARE</literal> clause. 
+    <literal>FOR SHARE</literal> clause.
     (<literal>ORDER BY</> and <literal>LIMIT</> can be attached to a
     subexpression if it is enclosed in parentheses.  Without
     parentheses, these clauses will be taken to apply to the result of
@@ -803,14 +803,14 @@ OFFSET <replaceable class="parameter">start</replaceable>
    <para>
     The <literal>FOR UPDATE</literal> clause has this form:
 <synopsis>
-FOR UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
+FOR UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ]
 </synopsis>
    </para>
 
    <para>
     The closely related <literal>FOR SHARE</literal> clause has this form:
 <synopsis>
-FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
+FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ]
 </synopsis>
    </para>
 
@@ -831,6 +831,18 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
     linkend="mvcc">.
    </para>
 
+   <para>
+    To prevent the operation from waiting for other transactions to commit,
+    use the <literal>NOWAIT</> option.  <command>SELECT FOR UPDATE
+    NOWAIT</command> reports an error, rather than waiting, if a selected row
+    cannot be locked immediately.  Note that <literal>NOWAIT</> applies only
+    to the row-level lock(s) &mdash; the required <literal>ROW SHARE</literal>
+    table-level lock is still taken in the ordinary way (see
+    <xref linkend="mvcc">).  You can use the <literal>NOWAIT</> option of
+    <xref linkend="sql-lock" endterm="sql-lock-title">
+    if you need to acquire the table-level lock without waiting.
+   </para>
+
    <para>
     <literal>FOR SHARE</literal> behaves similarly, except that it
     acquires a shared rather than exclusive lock on each retrieved
@@ -843,7 +855,8 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
    <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>.
+    <literal>FOR SHARE</literal>, nor can different parts of the statement use
+    both <literal>NOWAIT</> and normal waiting mode.
    </para>
 
    <para>
@@ -861,8 +874,8 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
    </para>
 
    <para>
-    It is possible for a <command>SELECT</> command using both 
-    <literal>LIMIT</literal> and  <literal>FOR UPDATE/SHARE</literal> 
+    It is possible for a <command>SELECT</> command using both
+    <literal>LIMIT</literal> and  <literal>FOR UPDATE/SHARE</literal>
     clauses to return fewer rows than specified by <literal>LIMIT</literal>.
     This is because <literal>LIMIT</> is applied first.  The command
     selects the specified number of rows,
index 7e6a4807b7b4d136894f984e80c0ed1c0f0652f1..d9967197b873fadc14cb2c67bf68cfba85ae398e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.35 2005/04/28 21:47:10 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.36 2005/08/01 20:31:04 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">tablename</replaceable> [, ...] ] ]
+    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
 </synopsis>
  </refsynopsisdiv>
 
index 070ba7080ce51817d38f8ef0c19924b9e0459e3a..1bd6c3cff9d3a1e47d61fdf5bfae5f6092f3899f 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.37 2005/07/14 06:17:35 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.38 2005/08/01 20:31:05 tgl Exp $
 -->
 
  <chapter id="sql-intro">
@@ -865,7 +865,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">class_name</replaceable> [, ...] ] ]
+    [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
      </synopsis>
     </para>
 
index 843b2909ef27afdb0a1a93249c601fc20cfabb45..c5851765f2584bf408b56c8d81ebae4a0a8d91f3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.195 2005/06/20 18:37:01 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.196 2005/08/01 20:31:05 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1945,7 +1945,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
  */
 HTSU_Result
 heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
-                                CommandId cid, LockTupleMode mode)
+                                CommandId cid, LockTupleMode mode, bool nowait)
 {
        HTSU_Result     result;
        ItemPointer tid = &(tuple->t_self);
@@ -1998,7 +1998,16 @@ l3:
                 */
                if (!have_tuple_lock)
                {
-                       LockTuple(relation, tid, tuple_lock_type);
+                       if (nowait)
+                       {
+                               if (!ConditionalLockTuple(relation, tid, tuple_lock_type))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+                                                        errmsg("could not obtain lock on row in relation \"%s\"",
+                                                                       RelationGetRelationName(relation))));
+                       }
+                       else
+                               LockTuple(relation, tid, tuple_lock_type);
                        have_tuple_lock = true;
                }
 
@@ -2020,7 +2029,17 @@ l3:
                else if (infomask & HEAP_XMAX_IS_MULTI)
                {
                        /* wait for multixact to end */
-                       MultiXactIdWait((MultiXactId) xwait);
+                       if (nowait)
+                       {
+                               if (!ConditionalMultiXactIdWait((MultiXactId) xwait))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+                                                        errmsg("could not obtain lock on row in relation \"%s\"",
+                                                                       RelationGetRelationName(relation))));
+                       }
+                       else
+                               MultiXactIdWait((MultiXactId) xwait);
+
                        LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
 
                        /*
@@ -2045,7 +2064,17 @@ l3:
                else
                {
                        /* wait for regular transaction to end */
-                       XactLockTableWait(xwait);
+                       if (nowait)
+                       {
+                               if (!ConditionalXactLockTableWait(xwait))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+                                                        errmsg("could not obtain lock on row in relation \"%s\"",
+                                                                       RelationGetRelationName(relation))));
+                       }
+                       else
+                               XactLockTableWait(xwait);
+
                        LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
 
                        /*
index 41773625af4cecf351b84db4ae3e034468e4ed01..8f107d42c6e7e8af1be59eb02df0d888dcc9701a 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.5 2005/06/08 15:50:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.6 2005/08/01 20:31:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -558,6 +558,43 @@ MultiXactIdWait(MultiXactId multi)
        }
 }
 
+/*
+ * ConditionalMultiXactIdWait
+ *             As above, but only lock if we can get the lock without blocking.
+ */
+bool
+ConditionalMultiXactIdWait(MultiXactId multi)
+{
+       bool    result = true;
+       TransactionId *members;
+       int             nmembers;
+
+       nmembers = GetMultiXactIdMembers(multi, &members);
+
+       if (nmembers >= 0)
+       {
+               int             i;
+
+               for (i = 0; i < nmembers; i++)
+               {
+                       TransactionId   member = members[i];
+
+                       debug_elog4(DEBUG2, "ConditionalMultiXactIdWait: trying %d (%u)",
+                                               i, member);
+                       if (!TransactionIdIsCurrentTransactionId(member))
+                       {
+                               result = ConditionalXactLockTableWait(member);
+                               if (!result)
+                                       break;
+                       }
+               }
+
+               pfree(members);
+       }
+
+       return result;
+}
+
 /*
  * CreateMultiXactId
  *             Make a new MultiXactId
@@ -590,7 +627,7 @@ CreateMultiXactId(int nxids, TransactionId *xids)
         */
        multi = mXactCacheGetBySet(nxids, xids);
        if (MultiXactIdIsValid(multi))
-       {       
+       {
                debug_elog2(DEBUG2, "Create: in cache!");
                return multi;
        }
index e0cfd33485547230eb987259370e45a90fe3b6b8..9df00d8cbeaf45b41751d00f8430f85294271dde 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.189 2005/05/30 07:20:58 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.190 2005/08/01 20:31:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1598,7 +1598,8 @@ GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
                *newSlot = NULL;
                tuple.t_self = *tid;
 ltrmark:;
-               test = heap_lock_tuple(relation, &tuple, &buffer, cid, LockTupleExclusive);
+               test = heap_lock_tuple(relation, &tuple, &buffer, cid,
+                                                          LockTupleExclusive, false);
                switch (test)
                {
                        case HeapTupleSelfUpdated:
index 3ef33cfd39fc8278fcf20d0512a601014352e1d4..bcf7d82d30de2d304178c9bf10340dd7c88d101b 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.251 2005/06/28 05:08:55 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.252 2005/08/01 20:31:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -559,8 +559,9 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
        /*
         * Have to lock relations selected FOR UPDATE/FOR SHARE
         */
-       estate->es_rowMark = NIL;
+       estate->es_rowMarks = NIL;
        estate->es_forUpdate = parseTree->forUpdate;
+       estate->es_rowNoWait = parseTree->rowNoWait;
        if (parseTree->rowMarks != NIL)
        {
                ListCell   *l;
@@ -577,7 +578,7 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
                        erm->relation = relation;
                        erm->rti = rti;
                        snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rti);
-                       estate->es_rowMark = lappend(estate->es_rowMark, erm);
+                       estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
                }
        }
 
@@ -1010,12 +1011,12 @@ ExecEndPlan(PlanState *planstate, EState *estate)
                }
 
                heap_close(estate->es_into_relation_descriptor, NoLock);
-   }
+       }
 
        /*
         * close any relations selected FOR UPDATE/FOR SHARE, again keeping locks
         */
-       foreach(l, estate->es_rowMark)
+       foreach(l, estate->es_rowMarks)
        {
                execRowMark *erm = lfirst(l);
 
@@ -1156,12 +1157,12 @@ lnext:  ;
                        /*
                         * Process any FOR UPDATE or FOR SHARE locking requested.
                         */
-                       else if (estate->es_rowMark != NIL)
+                       else if (estate->es_rowMarks != NIL)
                        {
                                ListCell   *l;
 
                lmark:  ;
-                               foreach(l, estate->es_rowMark)
+                               foreach(l, estate->es_rowMarks)
                                {
                                        execRowMark *erm = lfirst(l);
                                        Buffer          buffer;
@@ -1190,7 +1191,7 @@ lnext:    ;
                                        tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
                                        test = heap_lock_tuple(erm->relation, &tuple, &buffer,
                                                                                  estate->es_snapshot->curcid,
-                                                                                 lockmode);
+                                                                                 lockmode, estate->es_rowNoWait);
                                        ReleaseBuffer(buffer);
                                        switch (test)
                                        {
@@ -1823,7 +1824,7 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
                ListCell   *l;
 
                relation = NULL;
-               foreach(l, estate->es_rowMark)
+               foreach(l, estate->es_rowMarks)
                {
                        if (((execRowMark *) lfirst(l))->rti == rti)
                        {
@@ -2128,8 +2129,9 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
        if (estate->es_topPlan->nParamExec > 0)
                epqstate->es_param_exec_vals = (ParamExecData *)
                        palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
-       epqstate->es_rowMark = estate->es_rowMark;
+       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 8eaff494e3ea1d8e738aabc93adee353a376e1d9..feeffe70520744b94dde5c6bfdaf4611eb655496 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.124 2005/06/20 18:37:01 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.125 2005/08/01 20:31:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -199,8 +199,9 @@ CreateExecutorState(void)
 
        estate->es_processed = 0;
        estate->es_lastoid = InvalidOid;
-       estate->es_rowMark = NIL;
+       estate->es_rowMarks = NIL;
        estate->es_forUpdate = false;
+       estate->es_rowNoWait = false;
 
        estate->es_instrument = false;
        estate->es_select_into = false;
index f3189fc14a95f42ca18ebc925005cbe70c5aa78c..9c21c2f977a08633accd0ccc4dd2566dc1f4a22e 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.314 2005/08/01 04:03:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.315 2005/08/01 20:31:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1589,6 +1589,18 @@ _copyDefElem(DefElem *from)
        return newnode;
 }
 
+static LockingClause *
+_copyLockingClause(LockingClause *from)
+{
+       LockingClause *newnode = makeNode(LockingClause);
+
+       COPY_NODE_FIELD(lockedRels);
+       COPY_SCALAR_FIELD(forUpdate);
+       COPY_SCALAR_FIELD(nowait);
+
+       return newnode;
+}
+
 static Query *
 _copyQuery(Query *from)
 {
@@ -1607,6 +1619,7 @@ _copyQuery(Query *from)
        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);
@@ -1675,8 +1688,7 @@ _copySelectStmt(SelectStmt *from)
        COPY_NODE_FIELD(sortClause);
        COPY_NODE_FIELD(limitOffset);
        COPY_NODE_FIELD(limitCount);
-       COPY_NODE_FIELD(lockedRels);
-       COPY_SCALAR_FIELD(forUpdate);
+       COPY_NODE_FIELD(lockingClause);
        COPY_SCALAR_FIELD(op);
        COPY_SCALAR_FIELD(all);
        COPY_NODE_FIELD(larg);
@@ -3185,6 +3197,9 @@ copyObject(void *from)
                case T_DefElem:
                        retval = _copyDefElem(from);
                        break;
+               case T_LockingClause:
+                       retval = _copyLockingClause(from);
+                       break;
                case T_RangeTblEntry:
                        retval = _copyRangeTblEntry(from);
                        break;
index 126647a77571813d3b2e16ccb85c05f3eef24d23..326eb9c62aa003849ac785d3ec820764c3f27140 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.251 2005/08/01 04:03:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.252 2005/08/01 20:31:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -644,6 +644,7 @@ _equalQuery(Query *a, Query *b)
        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);
@@ -704,8 +705,7 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
        COMPARE_NODE_FIELD(sortClause);
        COMPARE_NODE_FIELD(limitOffset);
        COMPARE_NODE_FIELD(limitCount);
-       COMPARE_NODE_FIELD(lockedRels);
-       COMPARE_SCALAR_FIELD(forUpdate);
+       COMPARE_NODE_FIELD(lockingClause);
        COMPARE_SCALAR_FIELD(op);
        COMPARE_SCALAR_FIELD(all);
        COMPARE_NODE_FIELD(larg);
@@ -1649,6 +1649,16 @@ _equalDefElem(DefElem *a, DefElem *b)
        return true;
 }
 
+static bool
+_equalLockingClause(LockingClause *a, LockingClause *b)
+{
+       COMPARE_NODE_FIELD(lockedRels);
+       COMPARE_SCALAR_FIELD(forUpdate);
+       COMPARE_SCALAR_FIELD(nowait);
+
+       return true;
+}
+
 static bool
 _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
 {
@@ -2229,6 +2239,9 @@ equal(void *a, void *b)
                case T_DefElem:
                        retval = _equalDefElem(a, b);
                        break;
+               case T_LockingClause:
+                       retval = _equalLockingClause(a, b);
+                       break;
                case T_RangeTblEntry:
                        retval = _equalRangeTblEntry(a, b);
                        break;
index 0b905dd043d3e95a37c34856b65157581cb45262..c3a19431c480b5133b1e097d98e267ff347a7298 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.258 2005/07/02 23:00:39 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.259 2005/08/01 20:31:08 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -1343,8 +1343,7 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
        WRITE_NODE_FIELD(sortClause);
        WRITE_NODE_FIELD(limitOffset);
        WRITE_NODE_FIELD(limitCount);
-       WRITE_NODE_FIELD(lockedRels);
-       WRITE_BOOL_FIELD(forUpdate);
+       WRITE_NODE_FIELD(lockingClause);
        WRITE_ENUM_FIELD(op, SetOperation);
        WRITE_BOOL_FIELD(all);
        WRITE_NODE_FIELD(larg);
@@ -1371,6 +1370,16 @@ _outDefElem(StringInfo str, DefElem *node)
        WRITE_NODE_FIELD(arg);
 }
 
+static void
+_outLockingClause(StringInfo str, LockingClause *node)
+{
+       WRITE_NODE_TYPE("LOCKINGCLAUSE");
+
+       WRITE_NODE_FIELD(lockedRels);
+       WRITE_BOOL_FIELD(forUpdate);
+       WRITE_BOOL_FIELD(nowait);
+}
+
 static void
 _outColumnDef(StringInfo str, ColumnDef *node)
 {
@@ -1462,6 +1471,7 @@ _outQuery(StringInfo str, Query *node)
        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);
@@ -2079,6 +2089,9 @@ _outNode(StringInfo str, void *obj)
                        case T_DefElem:
                                _outDefElem(str, obj);
                                break;
+                       case T_LockingClause:
+                               _outLockingClause(str, obj);
+                               break;
 
                        default:
 
index 2e9c842051a39e4c92ca38295abeef86d1f19b62..ff49ee21f2e49cb7f199e5aef05e2a03d4383e87 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.180 2005/06/28 05:08:57 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.181 2005/08/01 20:31:08 tgl Exp $
  *
  * NOTES
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -146,6 +146,7 @@ _readQuery(void)
        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);
index ba758528c7646641466c17508daf6343c7b20588..9624a4ad1350ee1a87fb9c9b26b34c103f18e6e0 100644 (file)
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.29 2005/06/05 22:32:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.30 2005/08/01 20:31:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -295,18 +295,26 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
                         * already adjusted the marker values, so just list_concat the
                         * list.)
                         *
-                        * Executor can't handle multiple FOR UPDATE/SHARE flags, so
-                        * complain if they are valid but different
+                        * Executor can't handle multiple FOR UPDATE/SHARE/NOWAIT flags,
+                        * so complain if they are valid but different
                         */
-                       if (parse->rowMarks && subquery->rowMarks &&
-                               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->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
index a9211cfe93151dc5c05d7b42ba65a59ab424ec27..099bb7148316ea16768af77706912e9e8e640b18 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.323 2005/07/28 22:27:00 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.324 2005/08/01 20:31:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,7 +134,7 @@ static void transformFKConstraints(ParseState *pstate,
                                           bool isAddConstraint);
 static void applyColumnNames(List *dst, List *src);
 static List *getSetColTypes(ParseState *pstate, Node *node);
-static void transformLocking(Query *qry, List *lockedRels, bool forUpdate);
+static void transformLockingClause(Query *qry, LockingClause *lc);
 static void transformConstraintAttrs(List *constraintList);
 static void transformColumnType(ParseState *pstate, ColumnDef *column);
 static void release_pstate_resources(ParseState *pstate);
@@ -1812,8 +1812,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
        qry->commandType = CMD_SELECT;
 
-       /* make FOR UPDATE/FOR SHARE list available to addRangeTableEntry */
-       pstate->p_lockedRels = stmt->lockedRels;
+       /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
+       pstate->p_locking_clause = stmt->lockingClause;
 
        /* process the FROM clause */
        transformFromClause(pstate, stmt->fromClause);
@@ -1872,8 +1872,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
                parseCheckAggregates(pstate, qry);
 
-       if (stmt->lockedRels != NIL)
-               transformLocking(qry, stmt->lockedRels, stmt->forUpdate);
+       if (stmt->lockingClause)
+               transformLockingClause(qry, stmt->lockingClause);
 
        return qry;
 }
@@ -1901,8 +1901,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        List       *sortClause;
        Node       *limitOffset;
        Node       *limitCount;
-       List       *lockedRels;
-       bool            forUpdate;
+       LockingClause *lockingClause;
        Node       *node;
        ListCell   *left_tlist,
                           *dtlist;
@@ -1940,16 +1939,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        sortClause = stmt->sortClause;
        limitOffset = stmt->limitOffset;
        limitCount = stmt->limitCount;
-       lockedRels = stmt->lockedRels;
-       forUpdate = stmt->forUpdate;
+       lockingClause = stmt->lockingClause;
 
        stmt->sortClause = NIL;
        stmt->limitOffset = NULL;
        stmt->limitCount = NULL;
-       stmt->lockedRels = NIL;
+       stmt->lockingClause = NULL;
 
        /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
-       if (lockedRels)
+       if (lockingClause)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
@@ -2089,8 +2087,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
                parseCheckAggregates(pstate, qry);
 
-       if (lockedRels != NIL)
-               transformLocking(qry, lockedRels, forUpdate);
+       if (lockingClause)
+               transformLockingClause(qry, lockingClause);
 
        return qry;
 }
@@ -2114,7 +2112,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                                (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
        /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
-       if (stmt->lockedRels)
+       if (stmt->lockingClause)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
@@ -2134,7 +2132,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
        {
                Assert(stmt->larg != NULL && stmt->rarg != NULL);
                if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
-                       stmt->lockedRels)
+                       stmt->lockingClause)
                        isLeaf = true;
                else
                        isLeaf = false;
@@ -2760,24 +2758,40 @@ CheckSelectLocking(Query *qry, bool forUpdate)
  * in rewriteHandler.c.
  */
 static void
-transformLocking(Query *qry, List *lockedRels, bool forUpdate)
+transformLockingClause(Query *qry, LockingClause *lc)
 {
+       List       *lockedRels = lc->lockedRels;
        List       *rowMarks;
        ListCell   *l;
        ListCell   *rt;
        Index           i;
+       LockingClause *allrels;
 
-       if (qry->rowMarks && forUpdate != qry->forUpdate)
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
-       qry->forUpdate = forUpdate;
+       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);
+
+       /* 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;
 
-       CheckSelectLocking(qry, forUpdate);
-       
        rowMarks = qry->rowMarks;
 
-       if (linitial(lockedRels) == NULL)
+       if (lockedRels == NIL)
        {
                /* all regular tables used in query */
                i = 0;
@@ -2799,8 +2813,7 @@ transformLocking(Query *qry, List *lockedRels, bool forUpdate)
                                         * FOR UPDATE/SHARE of subquery is propagated to all
                                         * of subquery's rels
                                         */
-                                       transformLocking(rte->subquery, list_make1(NULL),
-                                                                        forUpdate);
+                                       transformLockingClause(rte->subquery, allrels);
                                        break;
                                default:
                                        /* ignore JOIN, SPECIAL, FUNCTION RTEs */
@@ -2836,8 +2849,7 @@ transformLocking(Query *qry, List *lockedRels, bool forUpdate)
                                                         * FOR UPDATE/SHARE of subquery is propagated to
                                                         * all of subquery's rels
                                                         */
-                                                       transformLocking(rte->subquery, list_make1(NULL),
-                                                                                        forUpdate);
+                                                       transformLockingClause(rte->subquery, allrels);
                                                        break;
                                                case RTE_JOIN:
                                                        ereport(ERROR,
index 8e79355cdd5fbb7d56d0f1dfdc05570113745e1b..768fb4ada7511d4cad7755d0ffcddd3b2c9013d0 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.506 2005/08/01 04:03:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.507 2005/08/01 20:31:09 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -87,7 +87,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, List *lockingClause,
+                                                               List *sortClause, Node *lockingClause,
                                                                Node *limitOffset, Node *limitCount);
 static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
 static Node *doNegate(Node *n);
@@ -132,7 +132,7 @@ static void doNegateFloat(Value *v);
 
 %type <node>   stmt schema_stmt
                AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt
-               AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt 
+               AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
                AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
                AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
                ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
@@ -165,7 +165,7 @@ static void doNegateFloat(Value *v);
 
 %type <dbehavior>      opt_drop_behavior
 
-%type <list>   createdb_opt_list alterdb_opt_list copy_opt_list 
+%type <list>   createdb_opt_list alterdb_opt_list copy_opt_list
                                transaction_mode_list
 %type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item
                                transaction_mode_item
@@ -240,8 +240,8 @@ static void doNegateFloat(Value *v);
 %type <oncommit> OnCommitOption
 %type <withoids> OptWithOids WithOidsAs
 
-%type <list>   for_locking_clause opt_for_locking_clause
-                               update_list
+%type <node>   for_locking_clause opt_for_locking_clause
+%type <list>   locked_rels_list
 %type <boolean>        opt_all
 
 %type <node>   join_outer join_qual
@@ -4555,7 +4555,7 @@ opt_equal:        '='                                                                             {}
  *****************************************************************************/
 
 AlterDatabaseStmt:
-                       ALTER DATABASE database_name opt_with alterdb_opt_list
+                       ALTER DATABASE database_name opt_with alterdb_opt_list
                                 {
                                        AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt);
                                        n->dbname = $3;
@@ -5070,7 +5070,7 @@ lock_type:        ACCESS SHARE                                    { $$ = AccessShareLock; }
                        | ACCESS EXCLUSIVE                              { $$ = AccessExclusiveLock; }
                ;
 
-opt_nowait:    NOWAIT                  { $$ = TRUE; }
+opt_nowait:    NOWAIT                                                  { $$ = TRUE; }
                        | /*EMPTY*/                                             { $$ = FALSE; }
                ;
 
@@ -5191,7 +5191,7 @@ select_no_parens:
                        simple_select                                           { $$ = $1; }
                        | select_clause sort_clause
                                {
-                                       insertSelectOptions((SelectStmt *) $1, $2, NIL,
+                                       insertSelectOptions((SelectStmt *) $1, $2, NULL,
                                                                                NULL, NULL);
                                        $$ = $1;
                                }
@@ -5424,14 +5424,6 @@ select_offset_value:
                        a_expr                                                                  { $$ = $1; }
                ;
 
-/*
- *     jimmy bell-style recursive queries aren't supported in the
- *     current system.
- *
- *     ...however, recursive addattr and rename supported.  make special
- *     cases for these.
- */
-
 group_clause:
                        GROUP_P BY expr_list                                    { $$ = $3; }
                        | /*EMPTY*/                                                             { $$ = NIL; }
@@ -5443,8 +5435,22 @@ having_clause:
                ;
 
 for_locking_clause:
-                       FOR UPDATE update_list                                  { $$ = lcons(makeString("for_update"), $3); }
-                       | FOR SHARE update_list                                 { $$ = lcons(makeString("for_share"), $3); }
+                       FOR UPDATE locked_rels_list opt_nowait
+                               {
+                                       LockingClause *n = makeNode(LockingClause);
+                                       n->lockedRels = $3;
+                                       n->forUpdate = TRUE;
+                                       n->nowait = $4;
+                                       $$ = (Node *) n;
+                               }
+                       | FOR SHARE locked_rels_list opt_nowait
+                               {
+                                       LockingClause *n = makeNode(LockingClause);
+                                       n->lockedRels = $3;
+                                       n->forUpdate = FALSE;
+                                       n->nowait = $4;
+                                       $$ = (Node *) n;
+                               }
                        | FOR READ ONLY                                                 { $$ = NULL; }
                ;
 
@@ -5453,9 +5459,9 @@ opt_for_locking_clause:
                        | /* EMPTY */                                                   { $$ = NULL; }
                ;
 
-update_list:
+locked_rels_list:
                        OF name_list                                                    { $$ = $2; }
-                       | /* EMPTY */                                                   { $$ = list_make1(NULL); }
+                       | /* EMPTY */                                                   { $$ = NIL; }
                ;
 
 /*****************************************************************************
@@ -8691,7 +8697,7 @@ findLeftmostSelect(SelectStmt *node)
  */
 static void
 insertSelectOptions(SelectStmt *stmt,
-                                       List *sortClause, List *lockingClause,
+                                       List *sortClause, Node *lockingClause,
                                        Node *limitOffset, Node *limitCount)
 {
        /*
@@ -8708,25 +8714,11 @@ insertSelectOptions(SelectStmt *stmt,
        }
        if (lockingClause)
        {
-               Value      *type;
-
-               if (stmt->lockedRels)
+               if (stmt->lockingClause)
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                         errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed")));
-
-               Assert(list_length(lockingClause) > 1);
-               /* 1st is Value node containing "for_update" or "for_share" */
-               type = (Value *) linitial(lockingClause);
-               Assert(IsA(type, String));
-               if (strcmp(strVal(type), "for_update") == 0)
-                       stmt->forUpdate = true;
-               else if (strcmp(strVal(type), "for_share") == 0)
-                       stmt->forUpdate = false;
-               else
-                       elog(ERROR, "invalid first node in locking clause");
-
-               stmt->lockedRels = list_delete_first(lockingClause);
+               stmt->lockingClause = (LockingClause *) lockingClause;
        }
        if (limitOffset)
        {
index 51d200736b08ef46a909804e8daacb35b836ab37..85984cb1d9e0cdab3626a7cb04a5217d68c78641 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.112 2005/06/28 05:08:58 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.113 2005/08/01 20:31:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -903,9 +903,9 @@ isLockedRel(ParseState *pstate, char *refname)
        /* Outer loop to check parent query levels as well as this one */
        while (pstate != NULL)
        {
-               if (pstate->p_lockedRels != NIL)
+               if (pstate->p_locking_clause)
                {
-                       if (linitial(pstate->p_lockedRels) == NULL)
+                       if (pstate->p_locking_clause->lockedRels == NIL)
                        {
                                /* all tables used in query */
                                return true;
@@ -915,7 +915,7 @@ isLockedRel(ParseState *pstate, char *refname)
                                /* just the named tables */
                                ListCell   *l;
 
-                               foreach(l, pstate->p_lockedRels)
+                               foreach(l, pstate->p_locking_clause->lockedRels)
                                {
                                        char       *rname = strVal(lfirst(l));
 
index d8fba16b55541f1e482bbf189a1f6b57aeaffa9e..008c1fe6a5412d377ec433eee6734b6a4e131b5c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.75 2005/05/29 18:24:13 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.76 2005/08/01 20:31:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -423,7 +423,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
                stmt->sortClause != NIL ||
                stmt->limitOffset != NULL ||
                stmt->limitCount != NULL ||
-               stmt->lockedRels != NIL ||
+               stmt->lockingClause != NULL ||
                stmt->op != SETOP_NONE)
                goto fail;
        if (list_length(stmt->targetList) != 1)
index 428bf7ba44f8d3bdeefa7c4e4b2f94c82c69238a..1c58ccd7ca3cdf33afcffb11f6bda6526e2023bb 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.156 2005/07/28 22:27:02 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.157 2005/08/01 20:31:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,7 +52,8 @@ static TargetEntry *process_matched_tle(TargetEntry *src_tle,
                                        TargetEntry *prior_tle,
                                        const char *attrName);
 static Node *get_assignment_input(Node *node);
-static void markQueryForLocking(Query *qry, bool forUpdate, bool skipOldNew);
+static void markQueryForLocking(Query *qry, bool forUpdate, bool noWait,
+                                                               bool skipOldNew);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
                   int varno, Query *parsetree);
 static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
@@ -962,7 +963,8 @@ ApplyRetrieveRule(Query *parsetree,
                /*
                 * Set up the view's referenced tables as if FOR UPDATE/SHARE.
                 */
-               markQueryForLocking(rule_action, parsetree->forUpdate, true);
+               markQueryForLocking(rule_action, parsetree->forUpdate,
+                                                       parsetree->rowNoWait, true);
        }
 
        return parsetree;
@@ -977,16 +979,24 @@ ApplyRetrieveRule(Query *parsetree,
  * NB: this must agree with the parser's transformLocking() routine.
  */
 static void
-markQueryForLocking(Query *qry, bool forUpdate, bool skipOldNew)
+markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew)
 {
        Index           rti = 0;
        ListCell   *l;
 
-       if (qry->rowMarks && forUpdate != qry->forUpdate)
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
+       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)
        {
@@ -1007,7 +1017,7 @@ markQueryForLocking(Query *qry, bool forUpdate, bool skipOldNew)
                else if (rte->rtekind == RTE_SUBQUERY)
                {
                        /* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
-                       markQueryForLocking(rte->subquery, forUpdate, false);
+                       markQueryForLocking(rte->subquery, forUpdate, noWait, false);
                }
        }
 }
index 91afd4ed9a5ee5767b30588eb7ccbe299d83cd21..7a4ef9f7554d0371b79b4fe6d16925310fe0ab6e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.77 2005/06/17 22:32:45 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.78 2005/08/01 20:31:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -368,6 +368,27 @@ LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
                                           lockmode, false, false);
 }
 
+/*
+ *             ConditionalLockTuple
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE iff the lock was acquired.
+ */
+bool
+ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
+{
+       LOCKTAG         tag;
+
+       SET_LOCKTAG_TUPLE(tag,
+                                         relation->rd_lockInfo.lockRelId.dbId,
+                                         relation->rd_lockInfo.lockRelId.relId,
+                                         ItemPointerGetBlockNumber(tid),
+                                         ItemPointerGetOffsetNumber(tid));
+
+       return (LockAcquire(LockTableId, &tag, relation->rd_istemp,
+                                               lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
+}
+
 /*
  *             UnlockTuple
  */
@@ -463,6 +484,44 @@ XactLockTableWait(TransactionId xid)
                TransactionIdAbort(xid);
 }
 
+/*
+ *             ConditionalXactLockTableWait
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE if the lock was acquired.
+ */
+bool
+ConditionalXactLockTableWait(TransactionId xid)
+{
+       LOCKTAG         tag;
+
+       for (;;)
+       {
+               Assert(TransactionIdIsValid(xid));
+               Assert(!TransactionIdEquals(xid, GetTopTransactionId()));
+
+               SET_LOCKTAG_TRANSACTION(tag, xid);
+
+               if (LockAcquire(LockTableId, &tag, false,
+                                               ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
+                       return false;
+
+               LockRelease(LockTableId, &tag, ShareLock, false);
+
+               if (!TransactionIdIsInProgress(xid))
+                       break;
+               xid = SubTransGetParent(xid);
+       }
+
+       /*
+        * Transaction was committed/aborted/crashed - we have to update
+        * pg_clog if transaction is still marked as running.
+        */
+       if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
+               TransactionIdAbort(xid);
+
+       return true;
+}
 
 /*
  *             LockDatabaseObject
index e2e133b3cb2c5e79ac141983250f25ea536b0f4d..bf9ebfefaa383f7604163e36af1d4987114f611a 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.204 2005/07/15 18:39:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.205 2005/08/01 20:31:12 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -201,7 +201,7 @@ static void get_agg_expr(Aggref *aggref, deparse_context *context);
 static void get_const_expr(Const *constval, deparse_context *context);
 static void get_sublink_expr(SubLink *sublink, deparse_context *context);
 static void get_from_clause(Query *query, const char *prefix,
-                            deparse_context *context);
+                                                       deparse_context *context);
 static void get_from_clause_item(Node *jtnode, Query *query,
                                         deparse_context *context);
 static void get_from_clause_alias(Alias *alias, int varno,
@@ -1961,6 +1961,8 @@ get_select_query_def(Query *query, deparse_context *context,
                                                         quote_identifier(rte->eref->aliasname));
                        sep = ", ";
                }
+               if (query->rowNoWait)
+                       appendStringInfo(buf, " NOWAIT");
        }
 }
 
@@ -2401,8 +2403,8 @@ get_delete_query_def(Query *query, deparse_context *context)
                                         only_marker(rte),
                                         generate_relation_name(rte->relid));
 
-    /* Add the USING clause if given */
-    get_from_clause(query, " USING ", context);
+       /* Add the USING clause if given */
+       get_from_clause(query, " USING ", context);
 
        /* Add a WHERE clause if given */
        if (query->jointree->quals != NULL)
index dde6fe8ecd89e23e362d1cca54c278196038543f..9c040b0dfd4f11db6cabf3a974cce30dfd548261 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.102 2005/06/20 18:37:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.103 2005/08/01 20:31:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -163,7 +163,8 @@ extern HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer c
 extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple tup,
                ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait);
 extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tup,
-                                Buffer *userbuf, CommandId cid, LockTupleMode mode);
+                                Buffer *userbuf, CommandId cid,
+                                LockTupleMode mode, bool nowait);
 
 extern Oid     simple_heap_insert(Relation relation, HeapTuple tup);
 extern void simple_heap_delete(Relation relation, ItemPointer tid);
index 2199b05f2c5f177b9f414d289afb4f83c3740610..8a4d2101e75e5accb25db66343dec1e7aa2ab98d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/multixact.h,v 1.3 2005/06/08 15:50:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/multixact.h,v 1.4 2005/08/01 20:31:13 tgl Exp $
  */
 #ifndef MULTIXACT_H
 #define MULTIXACT_H
@@ -42,6 +42,7 @@ extern MultiXactId MultiXactIdCreate(TransactionId xid1, TransactionId xid2);
 extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid);
 extern bool MultiXactIdIsRunning(MultiXactId multi);
 extern void MultiXactIdWait(MultiXactId multi);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi);
 extern void MultiXactIdSetOldestMember(void);
 
 extern void AtEOXact_MultiXact(void);
index c7584968a364d58fd01fd12af6f77151f7de5332..2ce578b3f4164e0dd44f3f5dcfc70f80b65f39c0 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.294 2005/07/31 17:19:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.295 2005/08/01 20:31:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200507301
+#define CATALOG_VERSION_NO     200508011
 
 #endif
index 46d27a56f537ce15e1f9626a987f013f76700df7..73648f93ade1b2771cf28c095115ac79bdf81ab5 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.136 2005/06/26 22:05:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.137 2005/08/01 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -319,8 +319,9 @@ typedef struct EState
 
        uint32          es_processed;   /* # of tuples processed */
        Oid                     es_lastoid;             /* last oid processed (by INSERT) */
-       List       *es_rowMark;         /* not good place, but there is no other */
-       bool            es_forUpdate;   /* was it FOR UPDATE or FOR SHARE */
+       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 */
index 24480f1281a16e41c6014d1850118703aee350e8..bc980757121a6a5e9cc69a3b2f08351b89649fc7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.174 2005/08/01 04:03:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.175 2005/08/01 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -315,6 +315,7 @@ typedef enum NodeTag
        T_CompositeTypeStmt,
        T_InhRelation,
        T_FunctionParameter,
+       T_LockingClause,
 
        /*
         * TAGS FOR RANDOM OTHER STUFF
index fc88dec5351c1632f6d8c88602efe922aa168ada..90a282e16321d9ef6b9680984895fcd988084001 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.288 2005/08/01 04:03:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.289 2005/08/01 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -95,6 +95,7 @@ typedef struct Query
 
        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) */
 
@@ -415,6 +416,20 @@ typedef struct DefElem
        Node       *arg;                        /* a (Value *) or a (TypeName *) */
 } DefElem;
 
+/*
+ * LockingClause - raw representation of FOR UPDATE/SHARE options
+ *
+ * Note: lockedRels == NIL means "all relations in query".  Otherwise it
+ * is a list of String nodes giving relation eref names.
+ */
+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 */
+} LockingClause;
+
 
 /****************************************************************************
  *     Nodes for a Query tree
@@ -686,8 +701,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 */
-       List       *lockedRels;         /* FOR UPDATE or FOR SHARE relations */
-       bool            forUpdate;              /* true = FOR UPDATE, false = FOR SHARE */
+       LockingClause *lockingClause;   /* FOR UPDATE/FOR SHARE */
 
        /*
         * These fields are used only in upper-level SelectStmts.
@@ -699,6 +713,7 @@ typedef struct SelectStmt
        /* Eventually add fields for CORRESPONDING spec here */
 } SelectStmt;
 
+
 /* ----------------------
  *             Set Operation node for post-analysis query trees
  *
index 291282e09202cee5d97f3496af680ec217337cbe..c864f5714f82244e2953e379f9d69b75e78fbb58 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.44 2005/06/05 00:38:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.45 2005/08/01 20:31:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,9 +61,9 @@ 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 */
-       List       *p_lockedRels;       /* FOR UPDATE/SHARE, if any (see gram.y) */
-       Node       *p_value_substitute;         /* what to replace VALUE with, if
-                                                                                * any */
+       LockingClause *p_locking_clause;        /* 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 3a83c26c7df7bd4efe12b2fe52f4b57be76eddf7..72504ee2ab58bbf49b675bc00d4df11c1cd749b1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.50 2005/06/17 22:32:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.51 2005/08/01 20:31:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,12 +64,15 @@ extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
 
 /* Lock a tuple (see heap_lock_tuple before assuming you understand this) */
 extern void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
+extern bool ConditionalLockTuple(Relation relation, ItemPointer tid,
+                                                                LOCKMODE lockmode);
 extern void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
 
 /* Lock an XID (used to wait for a transaction to finish) */
 extern void XactLockTableInsert(TransactionId xid);
 extern void XactLockTableDelete(TransactionId xid);
 extern void XactLockTableWait(TransactionId xid);
+extern bool ConditionalXactLockTableWait(TransactionId xid);
 
 /* Lock a general object (other than a relation) of the current database */
 extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,