Implementing SELECT FOR UPDATE [NOWAIT] - Mailing list pgsql-patches

From Hans-Juergen Schoenig
Subject Implementing SELECT FOR UPDATE [NOWAIT]
Date
Msg-id 41CFE499.7040703@cybertec.at
Whole thread Raw
Responses Re: Implementing SELECT FOR UPDATE [NOWAIT]  (Bruce Momjian <pgman@candle.pha.pa.us>)
Re: Implementing SELECT FOR UPDATE [NOWAIT]  (Bruce Momjian <pgman@candle.pha.pa.us>)
List pgsql-patches
Folks,

We have implemented SELECT FOR UPDATE NOWAIT for PostgreSQL.
The patch attached to this email contains all the required code
including ECPG updates and some documentation.
It would be nice if this patch would be included in PostgreSQL 8.1

    Best regards,

        Ewald Geschwinde & Hans-Juergen Schoenig

--
Cybertec Geschwinde u Schoenig GmbH.
Schoengrabern 134, A-2020 Hollabrunn, Austria
Tel: +43/660/816 40 77
www.cybertec.at, www.postgresql.at
diff -r -c postgresql-8.0.0rc2.orig/doc/src/sgml/ref/select.sgml postgresql-8.0.0rc2/doc/src/sgml/ref/select.sgml
*** postgresql-8.0.0rc2.orig/doc/src/sgml/ref/select.sgml    Sat Nov 27 22:27:07 2004
--- postgresql-8.0.0rc2/doc/src/sgml/ref/select.sgml    Mon Dec 27 10:57:05 2004
***************
*** 30,36 ****
      [ 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 [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] ]

  where <replaceable class="parameter">from_item</replaceable> can be one of:

--- 30,36 ----
      [ 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 [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [NOWAIT] ]

  where <replaceable class="parameter">from_item</replaceable> can be one of:

***************
*** 772,778 ****
     <para>
      The <literal>FOR UPDATE</literal> clause has this form:
  <synopsis>
! FOR UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
  </synopsis>
     </para>

--- 772,778 ----
     <para>
      The <literal>FOR UPDATE</literal> clause has this form:
  <synopsis>
! FOR UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [NOWAIT]
  </synopsis>
     </para>

***************
*** 789,796 ****
      has already locked a selected row or rows, <command>SELECT FOR
      UPDATE</command> will wait for the other transaction to complete,
      and will then lock and return the updated row (or no row, if the
!     row was deleted).  For further discussion see <xref
!     linkend="mvcc">.
     </para>

     <para>
--- 789,802 ----
      has already locked a selected row or rows, <command>SELECT FOR
      UPDATE</command> will wait for the other transaction to complete,
      and will then lock and return the updated row (or no row, if the
!     row was deleted).  If the current transaction is not supposed to
!     wait on other transactions to commit the NOWAIT option can be
!     used.  If <command>SELECT FOR UPDATE NOWAIT</command> finds out
!     that somebody else is holding a lock an error will be thrown.
!     This will only happen in case of row level locks - if somebody
!     holds a table lock <command>SELECT FOR UPDATE NOWAIT</command>
!     will still wait for concurrent transactions.  For further
!     discussion see <xref linkend="mvcc">.
     </para>

     <para>
diff -r -c postgresql-8.0.0rc2.orig/doc/src/sgml/sql.sgml postgresql-8.0.0rc2/doc/src/sgml/sql.sgml
*** postgresql-8.0.0rc2.orig/doc/src/sgml/sql.sgml    Mon Nov 15 07:32:14 2004
--- postgresql-8.0.0rc2/doc/src/sgml/sql.sgml    Mon Dec 27 10:57:05 2004
***************
*** 866,872 ****
      [ 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 [ OF <replaceable class="PARAMETER">class_name</replaceable> [, ...] ] ]
       </synopsis>
      </para>

--- 866,872 ----
      [ 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 [ OF <replaceable class="PARAMETER">class_name</replaceable> [, ...] ] [NOWAIT] ]
       </synopsis>
      </para>

diff -r -c postgresql-8.0.0rc2.orig/src/backend/access/heap/heapam.c
postgresql-8.0.0rc2/src/backend/access/heap/heapam.c
*** postgresql-8.0.0rc2.orig/src/backend/access/heap/heapam.c    Sun Nov 14 03:04:12 2004
--- postgresql-8.0.0rc2/src/backend/access/heap/heapam.c    Mon Dec 27 10:56:52 2004
***************
*** 16,21 ****
--- 16,22 ----
   *        relation_openrv - open any relation specified by a RangeVar
   *        relation_openr    - open a system relation by name
   *        relation_close    - close any relation
+  *        conditional_relation_open - open with option not to wait
   *        heap_open        - open a heap relation by relation OID
   *        heap_openrv        - open a heap relation specified by a RangeVar
   *        heap_openr        - open a system heap relation by name
***************
*** 1828,1834 ****
   */
  int
  heap_mark4update(Relation relation, HeapTuple tuple, Buffer *buffer,
!                  CommandId cid)
  {
      TransactionId xid = GetCurrentTransactionId();
      ItemPointer tid = &(tuple->t_self);
--- 1829,1835 ----
   */
  int
  heap_mark4update(Relation relation, HeapTuple tuple, Buffer *buffer,
!                  CommandId cid, bool nowait)
  {
      TransactionId xid = GetCurrentTransactionId();
      ItemPointer tid = &(tuple->t_self);
***************
*** 1858,1866 ****
      {
          TransactionId xwait = HeapTupleHeaderGetXmax(tuple->t_data);

-         /* sleep until concurrent transaction ends */
          LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
!         XactLockTableWait(xwait);

          LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
          if (!TransactionIdDidCommit(xwait))
--- 1859,1881 ----
      {
          TransactionId xwait = HeapTupleHeaderGetXmax(tuple->t_data);

          LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
!
!         if (nowait)
!         {
!             /* rather error than sleep until concurrent transaction ends */
!             if (!ConditionalXactLockTableWait(xwait))
!             {
!                 ReleaseBuffer(*buffer);
!                 ereport(ERROR,
!                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
!                      errmsg("data in table \"%s\" was modify by concurrent trasaction.",
!                          RelationGetRelationName(relation))));
!             }
!         }
!         else
!             /* sleep until concurrent transaction ends */
!             XactLockTableWait(xwait);

          LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
          if (!TransactionIdDidCommit(xwait))
diff -r -c postgresql-8.0.0rc2.orig/src/backend/commands/trigger.c postgresql-8.0.0rc2/src/backend/commands/trigger.c
*** postgresql-8.0.0rc2.orig/src/backend/commands/trigger.c    Tue Dec  7 00:57:17 2004
--- postgresql-8.0.0rc2/src/backend/commands/trigger.c    Mon Dec 27 10:56:52 2004
***************
*** 1574,1580 ****
          *newSlot = NULL;
          tuple.t_self = *tid;
  ltrmark:;
!         test = heap_mark4update(relation, &tuple, &buffer, cid);
          switch (test)
          {
              case HeapTupleSelfUpdated:
--- 1574,1580 ----
          *newSlot = NULL;
          tuple.t_self = *tid;
  ltrmark:;
!         test = heap_mark4update(relation, &tuple, &buffer, cid, estate->es_nowait);
          switch (test)
          {
              case HeapTupleSelfUpdated:
diff -r -c postgresql-8.0.0rc2.orig/src/backend/executor/execMain.c postgresql-8.0.0rc2/src/backend/executor/execMain.c
*** postgresql-8.0.0rc2.orig/src/backend/executor/execMain.c    Thu Oct  7 20:38:49 2004
--- postgresql-8.0.0rc2/src/backend/executor/execMain.c    Mon Dec 27 10:56:52 2004
***************
*** 558,563 ****
--- 558,564 ----
      /*
       * Have to lock relations selected for update
       */
+     estate->es_nowait = parseTree->nowait;
      estate->es_rowMark = NIL;
      if (parseTree->rowMarks != NIL)
      {
***************
*** 1133,1139 ****

                      tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
                      test = heap_mark4update(erm->relation, &tuple, &buffer,
!                                             estate->es_snapshot->curcid);
                      ReleaseBuffer(buffer);
                      switch (test)
                      {
--- 1134,1140 ----

                      tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
                      test = heap_mark4update(erm->relation, &tuple, &buffer,
!                             estate->es_snapshot->curcid, estate->es_nowait);
                      ReleaseBuffer(buffer);
                      switch (test)
                      {
***************
*** 2082,2087 ****
--- 2083,2089 ----
      epqstate->es_instrument = estate->es_instrument;
      epqstate->es_select_into = estate->es_select_into;
      epqstate->es_into_oids = estate->es_into_oids;
+     epqstate->es_nowait = estate->es_nowait;
      epqstate->es_topPlan = estate->es_topPlan;

      /*
diff -r -c postgresql-8.0.0rc2.orig/src/backend/executor/execUtils.c
postgresql-8.0.0rc2/src/backend/executor/execUtils.c
*** postgresql-8.0.0rc2.orig/src/backend/executor/execUtils.c    Fri Oct  1 01:21:23 2004
--- postgresql-8.0.0rc2/src/backend/executor/execUtils.c    Mon Dec 27 10:56:52 2004
***************
*** 203,209 ****
      estate->es_instrument = false;
      estate->es_select_into = false;
      estate->es_into_oids = false;
!
      estate->es_exprcontexts = NIL;

      estate->es_per_tuple_exprcontext = NULL;
--- 203,210 ----
      estate->es_instrument = false;
      estate->es_select_into = false;
      estate->es_into_oids = false;
!     estate->es_nowait = false;
!
      estate->es_exprcontexts = NIL;

      estate->es_per_tuple_exprcontext = NULL;
diff -r -c postgresql-8.0.0rc2.orig/src/backend/nodes/copyfuncs.c postgresql-8.0.0rc2/src/backend/nodes/copyfuncs.c
*** postgresql-8.0.0rc2.orig/src/backend/nodes/copyfuncs.c    Sun Dec 12 00:26:33 2004
--- postgresql-8.0.0rc2/src/backend/nodes/copyfuncs.c    Mon Dec 27 10:56:52 2004
***************
*** 1426,1431 ****
--- 1426,1442 ----
      return newnode;
  }

+ static ForUpdate *
+ _copyForUpdate(ForUpdate *from)
+ {
+     ForUpdate *newnode = makeNode(ForUpdate);
+
+     COPY_NODE_FIELD(update_list);
+     COPY_SCALAR_FIELD(nowait);
+
+     return newnode;
+ }
+
  static RangeSubselect *
  _copyRangeSubselect(RangeSubselect *from)
  {
***************
*** 1537,1542 ****
--- 1548,1554 ----
      COPY_NODE_FIELD(groupClause);
      COPY_NODE_FIELD(havingQual);
      COPY_NODE_FIELD(distinctClause);
+     COPY_SCALAR_FIELD(nowait);
      COPY_NODE_FIELD(sortClause);
      COPY_NODE_FIELD(limitOffset);
      COPY_NODE_FIELD(limitCount);
***************
*** 1611,1617 ****
      COPY_NODE_FIELD(sortClause);
      COPY_NODE_FIELD(limitOffset);
      COPY_NODE_FIELD(limitCount);
!     COPY_NODE_FIELD(forUpdate);
      COPY_SCALAR_FIELD(op);
      COPY_SCALAR_FIELD(all);
      COPY_NODE_FIELD(larg);
--- 1623,1629 ----
      COPY_NODE_FIELD(sortClause);
      COPY_NODE_FIELD(limitOffset);
      COPY_NODE_FIELD(limitCount);
!     COPY_NODE_FIELD(forupdateClause);
      COPY_SCALAR_FIELD(op);
      COPY_SCALAR_FIELD(all);
      COPY_NODE_FIELD(larg);
***************
*** 3065,3070 ****
--- 3077,3085 ----
          case T_SortBy:
              retval = _copySortBy(from);
              break;
+         case T_ForUpdate:
+             retval = _copyForUpdate(from);
+             break;
          case T_RangeSubselect:
              retval = _copyRangeSubselect(from);
              break;
diff -r -c postgresql-8.0.0rc2.orig/src/backend/nodes/equalfuncs.c postgresql-8.0.0rc2/src/backend/nodes/equalfuncs.c
*** postgresql-8.0.0rc2.orig/src/backend/nodes/equalfuncs.c    Sun Dec 12 00:26:33 2004
--- postgresql-8.0.0rc2/src/backend/nodes/equalfuncs.c    Mon Dec 27 10:56:52 2004
***************
*** 656,661 ****
--- 656,662 ----
      COMPARE_NODE_FIELD(groupClause);
      COMPARE_NODE_FIELD(havingQual);
      COMPARE_NODE_FIELD(distinctClause);
+     COMPARE_SCALAR_FIELD(nowait);
      COMPARE_NODE_FIELD(sortClause);
      COMPARE_NODE_FIELD(limitOffset);
      COMPARE_NODE_FIELD(limitCount);
***************
*** 719,725 ****
      COMPARE_NODE_FIELD(sortClause);
      COMPARE_NODE_FIELD(limitOffset);
      COMPARE_NODE_FIELD(limitCount);
!     COMPARE_NODE_FIELD(forUpdate);
      COMPARE_SCALAR_FIELD(op);
      COMPARE_SCALAR_FIELD(all);
      COMPARE_NODE_FIELD(larg);
--- 720,726 ----
      COMPARE_NODE_FIELD(sortClause);
      COMPARE_NODE_FIELD(limitOffset);
      COMPARE_NODE_FIELD(limitCount);
!     COMPARE_NODE_FIELD(forupdateClause);
      COMPARE_SCALAR_FIELD(op);
      COMPARE_SCALAR_FIELD(all);
      COMPARE_NODE_FIELD(larg);
***************
*** 1577,1582 ****
--- 1578,1592 ----
  }

  static bool
+ _equalForUpdate(ForUpdate *a, ForUpdate *b)
+ {
+     COMPARE_SCALAR_FIELD(nowait);
+     COMPARE_NODE_FIELD(update_list);
+
+     return true;
+ }
+
+ static bool
  _equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
  {
      COMPARE_NODE_FIELD(subquery);
***************
*** 2202,2207 ****
--- 2212,2220 ----
          case T_SortBy:
              retval = _equalSortBy(a, b);
              break;
+         case T_ForUpdate:
+             retval = _equalForUpdate(a, b);
+             break;
          case T_RangeSubselect:
              retval = _equalRangeSubselect(a, b);
              break;
diff -r -c postgresql-8.0.0rc2.orig/src/backend/nodes/outfuncs.c postgresql-8.0.0rc2/src/backend/nodes/outfuncs.c
*** postgresql-8.0.0rc2.orig/src/backend/nodes/outfuncs.c    Sun Dec 12 00:26:33 2004
--- postgresql-8.0.0rc2/src/backend/nodes/outfuncs.c    Mon Dec 27 10:56:52 2004
***************
*** 1202,1208 ****
      WRITE_NODE_FIELD(sortClause);
      WRITE_NODE_FIELD(limitOffset);
      WRITE_NODE_FIELD(limitCount);
!     WRITE_NODE_FIELD(forUpdate);
      WRITE_ENUM_FIELD(op, SetOperation);
      WRITE_BOOL_FIELD(all);
      WRITE_NODE_FIELD(larg);
--- 1202,1208 ----
      WRITE_NODE_FIELD(sortClause);
      WRITE_NODE_FIELD(limitOffset);
      WRITE_NODE_FIELD(limitCount);
!     WRITE_NODE_FIELD(forupdateClause);
      WRITE_ENUM_FIELD(op, SetOperation);
      WRITE_BOOL_FIELD(all);
      WRITE_NODE_FIELD(larg);
***************
*** 1323,1328 ****
--- 1323,1329 ----
      WRITE_NODE_FIELD(groupClause);
      WRITE_NODE_FIELD(havingQual);
      WRITE_NODE_FIELD(distinctClause);
+     WRITE_BOOL_FIELD(nowait);
      WRITE_NODE_FIELD(sortClause);
      WRITE_NODE_FIELD(limitOffset);
      WRITE_NODE_FIELD(limitCount);
diff -r -c postgresql-8.0.0rc2.orig/src/backend/nodes/readfuncs.c postgresql-8.0.0rc2/src/backend/nodes/readfuncs.c
*** postgresql-8.0.0rc2.orig/src/backend/nodes/readfuncs.c    Sun Dec 12 00:26:34 2004
--- postgresql-8.0.0rc2/src/backend/nodes/readfuncs.c    Mon Dec 27 10:56:52 2004
***************
*** 149,154 ****
--- 149,155 ----
      READ_NODE_FIELD(groupClause);
      READ_NODE_FIELD(havingQual);
      READ_NODE_FIELD(distinctClause);
+     READ_BOOL_FIELD(nowait);
      READ_NODE_FIELD(sortClause);
      READ_NODE_FIELD(limitOffset);
      READ_NODE_FIELD(limitCount);
diff -r -c postgresql-8.0.0rc2.orig/src/backend/parser/analyze.c postgresql-8.0.0rc2/src/backend/parser/analyze.c
*** postgresql-8.0.0rc2.orig/src/backend/parser/analyze.c    Wed Nov 17 00:34:26 2004
--- postgresql-8.0.0rc2/src/backend/parser/analyze.c    Mon Dec 27 10:56:52 2004
***************
*** 135,141 ****
                         bool isAddConstraint);
  static void applyColumnNames(List *dst, List *src);
  static List *getSetColTypes(ParseState *pstate, Node *node);
! static void transformForUpdate(Query *qry, List *forUpdate);
  static void transformConstraintAttrs(List *constraintList);
  static void transformColumnType(ParseState *pstate, ColumnDef *column);
  static void release_pstate_resources(ParseState *pstate);
--- 135,141 ----
                         bool isAddConstraint);
  static void applyColumnNames(List *dst, List *src);
  static List *getSetColTypes(ParseState *pstate, Node *node);
! static void transformForUpdate(Query *qry, Node *forupdateClause);
  static void transformConstraintAttrs(List *constraintList);
  static void transformColumnType(ParseState *pstate, ColumnDef *column);
  static void release_pstate_resources(ParseState *pstate);
***************
*** 1804,1810 ****
      qry->commandType = CMD_SELECT;

      /* make FOR UPDATE clause available to addRangeTableEntry */
!     pstate->p_forUpdate = stmt->forUpdate;

      /* process the FROM clause */
      transformFromClause(pstate, stmt->fromClause);
--- 1804,1810 ----
      qry->commandType = CMD_SELECT;

      /* make FOR UPDATE clause available to addRangeTableEntry */
!     pstate->p_forUpdate = stmt->forupdateClause ? ((ForUpdate *)stmt->forupdateClause)->update_list : NIL;

      /* process the FROM clause */
      transformFromClause(pstate, stmt->fromClause);
***************
*** 1864,1871 ****
      if (pstate->p_hasAggs || qry->groupClause)
          parseCheckAggregates(pstate, qry);

!     if (stmt->forUpdate != NIL)
!         transformForUpdate(qry, stmt->forUpdate);

      return qry;
  }
--- 1864,1871 ----
      if (pstate->p_hasAggs || qry->groupClause)
          parseCheckAggregates(pstate, qry);

!     if (stmt->forupdateClause)
!         transformForUpdate(qry, stmt->forupdateClause);

      return qry;
  }
***************
*** 1893,1899 ****
      List       *sortClause;
      Node       *limitOffset;
      Node       *limitCount;
!     List       *forUpdate;
      Node       *node;
      ListCell   *left_tlist,
                 *dtlist;
--- 1893,1899 ----
      List       *sortClause;
      Node       *limitOffset;
      Node       *limitCount;
!     Node       *forUpdate;
      Node       *node;
      ListCell   *left_tlist,
                 *dtlist;
***************
*** 1931,1942 ****
      sortClause = stmt->sortClause;
      limitOffset = stmt->limitOffset;
      limitCount = stmt->limitCount;
!     forUpdate = stmt->forUpdate;

      stmt->sortClause = NIL;
      stmt->limitOffset = NULL;
      stmt->limitCount = NULL;
!     stmt->forUpdate = NIL;

      /* We don't support forUpdate with set ops at the moment. */
      if (forUpdate)
--- 1931,1942 ----
      sortClause = stmt->sortClause;
      limitOffset = stmt->limitOffset;
      limitCount = stmt->limitCount;
!     forUpdate = stmt->forupdateClause;

      stmt->sortClause = NIL;
      stmt->limitOffset = NULL;
      stmt->limitCount = NULL;
!     stmt->forupdateClause = NULL;

      /* We don't support forUpdate with set ops at the moment. */
      if (forUpdate)
***************
*** 2080,2086 ****
      if (pstate->p_hasAggs || qry->groupClause)
          parseCheckAggregates(pstate, qry);

!     if (forUpdate != NIL)
          transformForUpdate(qry, forUpdate);

      return qry;
--- 2080,2086 ----
      if (pstate->p_hasAggs || qry->groupClause)
          parseCheckAggregates(pstate, qry);

!     if (forUpdate)
          transformForUpdate(qry, forUpdate);

      return qry;
***************
*** 2105,2111 ****
                  (errcode(ERRCODE_SYNTAX_ERROR),
                   errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
      /* We don't support forUpdate with set ops at the moment. */
!     if (stmt->forUpdate)
          ereport(ERROR,
                  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                   errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
--- 2105,2111 ----
                  (errcode(ERRCODE_SYNTAX_ERROR),
                   errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
      /* We don't support forUpdate with set ops at the moment. */
!     if (stmt->forupdateClause)
          ereport(ERROR,
                  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                   errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
***************
*** 2125,2131 ****
      {
          Assert(stmt->larg != NULL && stmt->rarg != NULL);
          if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
!             stmt->forUpdate)
              isLeaf = true;
          else
              isLeaf = false;
--- 2125,2131 ----
      {
          Assert(stmt->larg != NULL && stmt->rarg != NULL);
          if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
!             stmt->forupdateClause)
              isLeaf = true;
          else
              isLeaf = false;
***************
*** 2737,2752 ****
   * in rewriteHandler.c.
   */
  static void
! transformForUpdate(Query *qry, List *forUpdate)
  {
      List       *rowMarks = qry->rowMarks;
      ListCell   *l;
      ListCell   *rt;
!     Index        i;

      CheckSelectForUpdate(qry);

!     if (linitial(forUpdate) == NULL)
      {
          /* all regular tables used in query */
          i = 0;
--- 2737,2761 ----
   * in rewriteHandler.c.
   */
  static void
! transformForUpdate(Query *qry, Node *forupdateClause)
  {
      List       *rowMarks = qry->rowMarks;
      ListCell   *l;
      ListCell   *rt;
!     Index       i;
!     ForUpdate  *fu = (ForUpdate *) forupdateClause;
!     ForUpdate  empty;
!     List       *update_list;

      CheckSelectForUpdate(qry);

!     update_list = fu->update_list;
!     qry->nowait = fu->nowait;
!
!     memset(&empty, 0, sizeof(ForUpdate));
!     empty.nowait = FALSE;
!
!     if (linitial(update_list) == NULL)
      {
          /* all regular tables used in query */
          i = 0;
***************
*** 2768,2774 ****
                       * FOR UPDATE of subquery is propagated to subquery's
                       * rels
                       */
!                     transformForUpdate(rte->subquery, list_make1(NULL));
                      break;
                  default:
                      /* ignore JOIN, SPECIAL, FUNCTION RTEs */
--- 2777,2785 ----
                       * FOR UPDATE of subquery is propagated to subquery's
                       * rels
                       */
!                     empty.update_list = list_make1(NULL);
!                     transformForUpdate(rte->subquery, (Node *) &empty);
!                     empty.update_list = NIL;
                      break;
                  default:
                      /* ignore JOIN, SPECIAL, FUNCTION RTEs */
***************
*** 2779,2785 ****
      else
      {
          /* just the named tables */
!         foreach(l, forUpdate)
          {
              char       *relname = strVal(lfirst(l));

--- 2790,2796 ----
      else
      {
          /* just the named tables */
!         foreach(l, update_list)
          {
              char       *relname = strVal(lfirst(l));

***************
*** 2804,2810 ****
                               * FOR UPDATE of subquery is propagated to
                               * subquery's rels
                               */
!                             transformForUpdate(rte->subquery, list_make1(NULL));
                              break;
                          case RTE_JOIN:
                              ereport(ERROR,
--- 2815,2823 ----
                               * FOR UPDATE of subquery is propagated to
                               * subquery's rels
                               */
!                             empty.update_list = list_make1(NULL);
!                             transformForUpdate(rte->subquery, (Node *) &empty);
!                             empty.update_list = NULL;
                              break;
                          case RTE_JOIN:
                              ereport(ERROR,
diff -r -c postgresql-8.0.0rc2.orig/src/backend/parser/gram.y postgresql-8.0.0rc2/src/backend/parser/gram.y
*** postgresql-8.0.0rc2.orig/src/backend/parser/gram.y    Mon Nov  8 05:02:20 2004
--- postgresql-8.0.0rc2/src/backend/parser/gram.y    Mon Dec 27 10:56:52 2004
***************
*** 87,93 ****
  static List *extractArgTypes(List *parameters);
  static SelectStmt *findLeftmostSelect(SelectStmt *node);
  static void insertSelectOptions(SelectStmt *stmt,
!                                 List *sortClause, List *forUpdate,
                                  Node *limitOffset, Node *limitCount);
  static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
  static Node *doNegate(Node *n);
--- 87,93 ----
  static List *extractArgTypes(List *parameters);
  static SelectStmt *findLeftmostSelect(SelectStmt *node);
  static void insertSelectOptions(SelectStmt *stmt,
!                                 List *sortClause, Node *forUpdate,
                                  Node *limitOffset, Node *limitCount);
  static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
  static Node *doNegate(Node *n);
***************
*** 239,245 ****
  %type <oncommit> OnCommitOption
  %type <withoids> OptWithOids WithOidsAs

! %type <list>    for_update_clause opt_for_update_clause update_list
  %type <boolean>    opt_all

  %type <node>    join_outer join_qual
--- 239,247 ----
  %type <oncommit> OnCommitOption
  %type <withoids> OptWithOids WithOidsAs

! %type <node>    for_update_clause opt_for_update_clause
! %type <list>    update_list
!
  %type <boolean>    opt_all

  %type <node>    join_outer join_qual
***************
*** 4801,4807 ****
              simple_select                        { $$ = $1; }
              | select_clause sort_clause
                  {
!                     insertSelectOptions((SelectStmt *) $1, $2, NIL,
                                          NULL, NULL);
                      $$ = $1;
                  }
--- 4803,4809 ----
              simple_select                        { $$ = $1; }
              | select_clause sort_clause
                  {
!                     insertSelectOptions((SelectStmt *) $1, $2, NULL,
                                          NULL, NULL);
                      $$ = $1;
                  }
***************
*** 4862,4867 ****
--- 4864,4870 ----
                      n->whereClause = $6;
                      n->groupClause = $7;
                      n->havingClause = $8;
+                     n->forupdateClause = NULL;
                      $$ = (Node *)n;
                  }
              | select_clause UNION opt_all select_clause
***************
*** 5053,5060 ****
          ;

  for_update_clause:
!             FOR UPDATE update_list                    { $$ = $3; }
!             | FOR READ ONLY                            { $$ = NULL; }
          ;

  opt_for_update_clause:
--- 5056,5069 ----
          ;

  for_update_clause:
!             FOR UPDATE update_list opt_nowait
!                 {
!                     ForUpdate *n = makeNode(ForUpdate);
!                     n->update_list = $3;
!                     n->nowait = $4;
!                     $$ = (Node *) n;
!                 }
!             | FOR READ ONLY                            { $$ = NULL; }
          ;

  opt_for_update_clause:
***************
*** 8284,8290 ****
   */
  static void
  insertSelectOptions(SelectStmt *stmt,
!                     List *sortClause, List *forUpdate,
                      Node *limitOffset, Node *limitCount)
  {
      /*
--- 8293,8299 ----
   */
  static void
  insertSelectOptions(SelectStmt *stmt,
!                     List *sortClause, Node *forUpdate,
                      Node *limitOffset, Node *limitCount)
  {
      /*
***************
*** 8301,8311 ****
      }
      if (forUpdate)
      {
!         if (stmt->forUpdate)
              ereport(ERROR,
                      (errcode(ERRCODE_SYNTAX_ERROR),
                       errmsg("multiple FOR UPDATE clauses not allowed")));
!         stmt->forUpdate = forUpdate;
      }
      if (limitOffset)
      {
--- 8310,8320 ----
      }
      if (forUpdate)
      {
!         if (stmt->forupdateClause)
              ereport(ERROR,
                      (errcode(ERRCODE_SYNTAX_ERROR),
                       errmsg("multiple FOR UPDATE clauses not allowed")));
!         stmt->forupdateClause = forUpdate;
      }
      if (limitOffset)
      {
diff -r -c postgresql-8.0.0rc2.orig/src/backend/parser/parse_type.c postgresql-8.0.0rc2/src/backend/parser/parse_type.c
*** postgresql-8.0.0rc2.orig/src/backend/parser/parse_type.c    Wed Dec 15 21:15:17 2004
--- postgresql-8.0.0rc2/src/backend/parser/parse_type.c    Mon Dec 27 10:56:52 2004
***************
*** 432,438 ****
          stmt->sortClause != NIL ||
          stmt->limitOffset != NULL ||
          stmt->limitCount != NULL ||
!         stmt->forUpdate != NIL ||
          stmt->op != SETOP_NONE)
          goto fail;
      if (list_length(stmt->targetList) != 1)
--- 432,438 ----
          stmt->sortClause != NIL ||
          stmt->limitOffset != NULL ||
          stmt->limitCount != NULL ||
!         stmt->forupdateClause != NULL ||
          stmt->op != SETOP_NONE)
          goto fail;
      if (list_length(stmt->targetList) != 1)
diff -r -c postgresql-8.0.0rc2.orig/src/backend/storage/lmgr/lmgr.c postgresql-8.0.0rc2/src/backend/storage/lmgr/lmgr.c
*** postgresql-8.0.0rc2.orig/src/backend/storage/lmgr/lmgr.c    Thu Sep 16 18:58:33 2004
--- postgresql-8.0.0rc2/src/backend/storage/lmgr/lmgr.c    Mon Dec 27 10:56:52 2004
***************
*** 393,395 ****
--- 393,435 ----
      if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
          TransactionIdAbort(xid);
  }
+
+ /*
+  * 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;
+     TransactionId myxid = GetTopTransactionId();
+
+     for (;;)
+     {
+         Assert(TransactionIdIsValid(xid));
+         Assert(!TransactionIdEquals(xid, myxid));
+
+         MemSet(&tag, 0, sizeof(tag));
+         tag.relId = XactLockTableId;
+         tag.dbId = InvalidOid;
+         tag.objId.xid = xid;
+
+         if (!LockAcquire(LockTableId, &tag, myxid, ShareLock, true))
+             return FALSE;
+         LockRelease(LockTableId, &tag, myxid, ShareLock);
+
+         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;
+ }
+
diff -r -c postgresql-8.0.0rc2.orig/src/bin/psql/sql_help.h postgresql-8.0.0rc2/src/bin/psql/sql_help.h
*** postgresql-8.0.0rc2.orig/src/bin/psql/sql_help.h    Tue Dec 21 05:22:44 2004
--- postgresql-8.0.0rc2/src/bin/psql/sql_help.h    Mon Dec 27 10:57:05 2004
***************
*** 383,393 ****

      { "SELECT",
        N_("retrieve rows from a table or view"),
!       N_("SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]\n    * | expression [ AS output_name ] [, ...]\n
[FROM from_item [, ...] ]\n    [ WHERE condition ]\n    [ GROUP BY expression [, ...] ]\n    [ HAVING condition [, ...]
]\n   [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]\n    [ ORDER BY expression [ ASC | DESC | USING operator ] [,
...]]\n    [ LIMIT { count | ALL } ]\n    [ OFFSET start ]\n    [ FOR UPDATE [ OF table_name [, ...] ] ]\n\nwhere
from_itemcan be one of:\n\n    [ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]\n    ( select )
[AS ] alias [ ( column_alias [, ...] ) ]\n    function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [,
...]| column_definition [, ...] ) ]\n    function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )\n
from_item[ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]") }, 

      { "SELECT INTO",
        N_("define a new table from the results of a query"),
!       N_("SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]\n    * | expression [ AS output_name ] [, ...]\n
INTO[ TEMPORARY | TEMP ] [ TABLE ] new_table\n    [ FROM from_item [, ...] ]\n    [ WHERE condition ]\n    [ GROUP BY
expression[, ...] ]\n    [ HAVING condition [, ...] ]\n    [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]\n    [
ORDERBY expression [ ASC | DESC | USING operator ] [, ...] ]\n    [ LIMIT { count | ALL } ]\n    [ OFFSET start ]\n
[FOR UPDATE [ OF tablename [, ...] ] ]") }, 

      { "SET",
        N_("change a run-time parameter"),
--- 383,393 ----

      { "SELECT",
        N_("retrieve rows from a table or view"),
!       N_("SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]\n    * | expression [ AS output_name ] [, ...]\n
[FROM from_item [, ...] ]\n    [ WHERE condition ]\n    [ GROUP BY expression [, ...] ]\n    [ HAVING condition [, ...]
]\n   [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]\n    [ ORDER BY expression [ ASC | DESC | USING operator ] [,
...]]\n    [ LIMIT { count | ALL } ]\n    [ OFFSET start ]\n    [ FOR UPDATE [ OF table_name [, ...] ] [NOWAIT]
]\n\nwherefrom_item can be one of:\n\n    [ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]\n
(select ) [ AS ] alias [ ( column_alias [, ...] ) ]\n    function_name ( [ argument [, ...] ] ) [ AS ] alias [ (
column_alias[, ...] | column_definition [, ...] ) ]\n    function_name ( [ argument [, ...] ] ) AS ( column_definition
[,...] )\n    from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]") }, 

      { "SELECT INTO",
        N_("define a new table from the results of a query"),
!       N_("SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]\n    * | expression [ AS output_name ] [, ...]\n
INTO[ TEMPORARY | TEMP ] [ TABLE ] new_table\n    [ FROM from_item [, ...] ]\n    [ WHERE condition ]\n    [ GROUP BY
expression[, ...] ]\n    [ HAVING condition [, ...] ]\n    [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]\n    [
ORDERBY expression [ ASC | DESC | USING operator ] [, ...] ]\n    [ LIMIT { count | ALL } ]\n    [ OFFSET start ]\n
[FOR UPDATE [ OF tablename [, ...] ] [NOWAIT] ]") }, 

      { "SET",
        N_("change a run-time parameter"),
diff -r -c postgresql-8.0.0rc2.orig/src/include/access/heapam.h postgresql-8.0.0rc2/src/include/access/heapam.h
*** postgresql-8.0.0rc2.orig/src/include/access/heapam.h    Sun Aug 29 07:06:55 2004
--- postgresql-8.0.0rc2/src/include/access/heapam.h    Mon Dec 27 10:57:05 2004
***************
*** 164,170 ****
  extern int heap_update(Relation relation, ItemPointer otid, HeapTuple tup,
          ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait);
  extern int heap_mark4update(Relation relation, HeapTuple tup,
!                  Buffer *userbuf, CommandId cid);

  extern Oid    simple_heap_insert(Relation relation, HeapTuple tup);
  extern void simple_heap_delete(Relation relation, ItemPointer tid);
--- 164,170 ----
  extern int heap_update(Relation relation, ItemPointer otid, HeapTuple tup,
          ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait);
  extern int heap_mark4update(Relation relation, HeapTuple tup,
!                  Buffer *userbuf, CommandId cid, bool nowait);

  extern Oid    simple_heap_insert(Relation relation, HeapTuple tup);
  extern void simple_heap_delete(Relation relation, ItemPointer tid);
diff -r -c postgresql-8.0.0rc2.orig/src/include/nodes/execnodes.h postgresql-8.0.0rc2/src/include/nodes/execnodes.h
*** postgresql-8.0.0rc2.orig/src/include/nodes/execnodes.h    Sun Dec 12 00:26:49 2004
--- postgresql-8.0.0rc2/src/include/nodes/execnodes.h    Mon Dec 27 10:56:52 2004
***************
*** 308,313 ****
--- 308,314 ----
      bool        es_instrument;    /* true requests runtime instrumentation */
      bool        es_select_into; /* true if doing SELECT INTO */
      bool        es_into_oids;    /* true to generate OIDs in SELECT INTO */
+     bool        es_nowait;    /* SELECT FOR UPDATE NOWAIT */

      List       *es_exprcontexts;    /* List of ExprContexts within EState */

diff -r -c postgresql-8.0.0rc2.orig/src/include/nodes/nodes.h postgresql-8.0.0rc2/src/include/nodes/nodes.h
*** postgresql-8.0.0rc2.orig/src/include/nodes/nodes.h    Sun Dec 12 00:26:49 2004
--- postgresql-8.0.0rc2/src/include/nodes/nodes.h    Mon Dec 27 10:56:52 2004
***************
*** 302,307 ****
--- 302,308 ----
      T_CompositeTypeStmt,
      T_InhRelation,
      T_FunctionParameter,
+     T_ForUpdate,

      /*
       * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
diff -r -c postgresql-8.0.0rc2.orig/src/include/nodes/parsenodes.h postgresql-8.0.0rc2/src/include/nodes/parsenodes.h
*** postgresql-8.0.0rc2.orig/src/include/nodes/parsenodes.h    Fri Nov  5 20:16:38 2004
--- postgresql-8.0.0rc2/src/include/nodes/parsenodes.h    Mon Dec 27 10:56:52 2004
***************
*** 103,108 ****
--- 103,110 ----

      List       *sortClause;        /* a list of SortClause's */

+     bool       nowait;        /* are we in NOWAIT mode? */
+
      Node       *limitOffset;    /* # of result tuples to skip */
      Node       *limitCount;        /* # of result tuples to return */

***************
*** 422,427 ****
--- 424,439 ----
      Node       *arg;            /* a (Value *) or a (TypeName *) */
  } DefElem;

+ /*
+  * ForUpdate -
+  *       used in raw parsetrees output only
+  */
+ typedef struct ForUpdate
+ {
+     NodeTag        type;
+     List        *update_list;    /* list of tables */
+     bool        nowait;        /* NOWAIT option */
+ } ForUpdate;

  /****************************************************************************
   *    Nodes for a Query tree
***************
*** 686,692 ****
      List       *sortClause;        /* sort clause (a list of SortBy's) */
      Node       *limitOffset;    /* # of result tuples to skip */
      Node       *limitCount;        /* # of result tuples to return */
!     List       *forUpdate;        /* FOR UPDATE clause */

      /*
       * These fields are used only in upper-level SelectStmts.
--- 698,704 ----
      List       *sortClause;        /* sort clause (a list of SortBy's) */
      Node       *limitOffset;    /* # of result tuples to skip */
      Node       *limitCount;        /* # of result tuples to return */
!     Node       *forupdateClause;    /* FOR UPDATE clause (ForUpdate node) */

      /*
       * These fields are used only in upper-level SelectStmts.
diff -r -c postgresql-8.0.0rc2.orig/src/include/storage/lmgr.h postgresql-8.0.0rc2/src/include/storage/lmgr.h
*** postgresql-8.0.0rc2.orig/src/include/storage/lmgr.h    Thu Sep 16 18:58:42 2004
--- postgresql-8.0.0rc2/src/include/storage/lmgr.h    Mon Dec 27 10:56:52 2004
***************
*** 60,64 ****
--- 60,65 ----
  extern void XactLockTableInsert(TransactionId xid);
  extern void XactLockTableDelete(TransactionId xid);
  extern void XactLockTableWait(TransactionId xid);
+ extern bool ConditionalXactLockTableWait(TransactionId xid);

  #endif   /* LMGR_H */

pgsql-patches by date:

Previous
From: David Brown
Date:
Subject: Re: Allow pooled connections to list all prepared queries
Next
From: David Brown
Date:
Subject: Re: Allow pooled connections to list all prepared queries