From e4828e9ccba731178dd77aed078db7ceb0e1e8d1 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Wed, 27 Nov 2013 17:49:12 -0300 Subject: [PATCH] Compare Xmin to previous Xmax when locking an update chain Not doing so causes us to traverse an update chain that has been broken by concurrent page pruning. All other code that traverses update chains uses this check as one of the cases in which to stop iterating, so replicate it here too. Failure to do so leads to erroneous CLOG, subtrans or multixact lookups. Per discussion following the bug report by J Smith in CADFUPgc5bmtv-yg9znxV-vcfkb+JPRqs7m2OesQXaM_4Z1JpdQ@mail.gmail.com as diagnosed by Andres Freund. --- src/backend/access/heap/heapam.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index f55e612590..39e5d2d26a 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -4814,6 +4814,7 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid, old_infomask; TransactionId xmax, new_xmax; + TransactionId priorXmax = InvalidTransactionId; ItemPointerCopy(tid, &tupid); @@ -4839,6 +4840,18 @@ l4: CHECK_FOR_INTERRUPTS(); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); + /* + * Check the tuple XMIN against prior XMAX, if any. If we reached + * the end of the chain, we're done, so return success. + */ + if (TransactionIdIsValid(priorXmax) && + !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data), + priorXmax)) + { + UnlockReleaseBuffer(buf); + return HeapTupleMayBeUpdated; + } + old_infomask = mytup.t_data->t_infomask; xmax = HeapTupleHeaderGetRawXmax(mytup.t_data); @@ -4939,6 +4952,7 @@ l4: } /* tail recursion */ + priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data); ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid); UnlockReleaseBuffer(buf); } -- 2.40.0