diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 54cba255719..3bcd8da1daf 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.154 2003/08/04 02:39:57 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.155 2003/09/15 23:33:38 tgl Exp $ * * * INTERFACE ROUTINES @@ -1206,10 +1206,15 @@ simple_heap_insert(Relation relation, HeapTuple tup) * * NB: do not call this directly unless you are prepared to deal with * concurrent-update conditions. Use simple_heap_delete instead. + * + * Normal, successful return value is HeapTupleMayBeUpdated, which + * actually means we did delete it. Failure return codes are + * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated + * (the last only possible if wait == false). */ int heap_delete(Relation relation, ItemPointer tid, - ItemPointer ctid, CommandId cid) + ItemPointer ctid, CommandId cid, bool wait) { ItemId lp; HeapTupleData tp; @@ -1243,7 +1248,7 @@ l1: ReleaseBuffer(buffer); elog(ERROR, "attempted to delete invisible tuple"); } - else if (result == HeapTupleBeingUpdated) + else if (result == HeapTupleBeingUpdated && wait) { TransactionId xwait = HeapTupleHeaderGetXmax(tp.t_data); @@ -1275,7 +1280,9 @@ l1: } if (result != HeapTupleMayBeUpdated) { - Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated); + Assert(result == HeapTupleSelfUpdated || + result == HeapTupleUpdated || + result == HeapTupleBeingUpdated); *ctid = tp.t_data->t_ctid; LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); @@ -1369,7 +1376,10 @@ simple_heap_delete(Relation relation, ItemPointer tid) ItemPointerData ctid; int result; - result = heap_delete(relation, tid, &ctid, GetCurrentCommandId()); + result = heap_delete(relation, tid, + &ctid, + GetCurrentCommandId(), + true /* wait for commit */); switch (result) { case HeapTupleSelfUpdated: @@ -1396,10 +1406,15 @@ simple_heap_delete(Relation relation, ItemPointer tid) * * NB: do not call this directly unless you are prepared to deal with * concurrent-update conditions. Use simple_heap_update instead. + * + * Normal, successful return value is HeapTupleMayBeUpdated, which + * actually means we *did* update it. Failure return codes are + * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated + * (the last only possible if wait == false). */ int heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, - ItemPointer ctid, CommandId cid) + ItemPointer ctid, CommandId cid, bool wait) { ItemId lp; HeapTupleData oldtup; @@ -1443,7 +1458,7 @@ l2: ReleaseBuffer(buffer); elog(ERROR, "attempted to update invisible tuple"); } - else if (result == HeapTupleBeingUpdated) + else if (result == HeapTupleBeingUpdated && wait) { TransactionId xwait = HeapTupleHeaderGetXmax(oldtup.t_data); @@ -1475,7 +1490,9 @@ l2: } if (result != HeapTupleMayBeUpdated) { - Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated); + Assert(result == HeapTupleSelfUpdated || + result == HeapTupleUpdated || + result == HeapTupleBeingUpdated); *ctid = oldtup.t_data->t_ctid; LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); @@ -1699,7 +1716,10 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup) ItemPointerData ctid; int result; - result = heap_update(relation, otid, tup, &ctid, GetCurrentCommandId()); + result = heap_update(relation, otid, tup, + &ctid, + GetCurrentCommandId(), + true /* wait for commit */); switch (result) { case HeapTupleSelfUpdated: diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 8ceac36cf4a..95c83c2372e 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.99 2003/09/15 00:26:31 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.100 2003/09/15 23:33:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -515,13 +515,58 @@ AtCommit_Notify(void) } else if (listener->notification == 0) { + ItemPointerData ctid; + int result; + rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl); - simple_heap_update(lRel, &lTuple->t_self, rTuple); + /* + * We cannot use simple_heap_update here because the tuple + * could have been modified by an uncommitted transaction; + * specifically, since UNLISTEN releases exclusive lock on + * the table before commit, the other guy could already have + * tried to unlisten. There are no other cases where we + * should be able to see an uncommitted update or delete. + * Therefore, our response to a HeapTupleBeingUpdated result + * is just to ignore it. We do *not* wait for the other + * guy to commit --- that would risk deadlock, and we don't + * want to block while holding the table lock anyway for + * performance reasons. We also ignore HeapTupleUpdated, + * which could occur if the other guy commits between our + * heap_getnext and heap_update calls. + */ + result = heap_update(lRel, &lTuple->t_self, rTuple, + &ctid, + GetCurrentCommandId(), + false /* no wait for commit */); + switch (result) + { + case HeapTupleSelfUpdated: + /* Tuple was already updated in current command? */ + elog(ERROR, "tuple already updated by self"); + break; + + case HeapTupleMayBeUpdated: + /* done successfully */ #ifdef NOT_USED /* currently there are no indexes */ - CatalogUpdateIndexes(lRel, rTuple); + CatalogUpdateIndexes(lRel, rTuple); #endif + break; + + case HeapTupleBeingUpdated: + /* ignore uncommitted tuples */ + break; + + case HeapTupleUpdated: + /* ignore just-committed tuples */ + break; + + default: + elog(ERROR, "unrecognized heap_update status: %u", + result); + break; + } } } } @@ -803,7 +848,13 @@ ProcessIncomingNotify(void) relname, (int) sourcePID); NotifyMyFrontEnd(relname, sourcePID); - /* Rewrite the tuple with 0 in notification column */ + /* + * Rewrite the tuple with 0 in notification column. + * + * simple_heap_update is safe here because no one else would + * have tried to UNLISTEN us, so there can be no uncommitted + * changes. + */ rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl); simple_heap_update(lRel, &lTuple->t_self, rTuple); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index a75edb370a2..1f78b1265b2 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.216 2003/08/08 21:41:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.217 2003/09/15 23:33:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1406,7 +1406,8 @@ ExecDelete(TupleTableSlot *slot, ldelete:; result = heap_delete(resultRelationDesc, tupleid, &ctid, - estate->es_snapshot->curcid); + estate->es_snapshot->curcid, + true /* wait for commit */); switch (result) { case HeapTupleSelfUpdated: @@ -1540,7 +1541,8 @@ lreplace:; */ result = heap_update(resultRelationDesc, tupleid, tuple, &ctid, - estate->es_snapshot->curcid); + estate->es_snapshot->curcid, + true /* wait for commit */); switch (result) { case HeapTupleSelfUpdated: diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 34f1241ed5a..05801af9e16 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: heapam.h,v 1.83 2003/08/04 02:40:10 momjian Exp $ + * $Id: heapam.h,v 1.84 2003/09/15 23:33:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -155,9 +155,9 @@ extern void setLastTid(const ItemPointer tid); extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid); extern int heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, - CommandId cid); + CommandId cid, bool wait); extern int heap_update(Relation relation, ItemPointer otid, HeapTuple tup, - ItemPointer ctid, CommandId cid); + ItemPointer ctid, CommandId cid, bool wait); extern int heap_mark4update(Relation relation, HeapTuple tup, Buffer *userbuf, CommandId cid);