]> granicus.if.org Git - postgresql/commitdiff
Compare Xmin to previous Xmax when locking an update chain
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 27 Nov 2013 20:49:12 +0000 (17:49 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 28 Nov 2013 14:54:25 +0000 (11:54 -0300)
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

index 8e1a3bff1920e1333a4a4106202b63050971a3d8..493df9f6ccdb104e69950a68ae55080ce7c1751e 100644 (file)
@@ -4797,6 +4797,7 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
                                old_infomask;
        TransactionId xmax,
                                new_xmax;
+       TransactionId priorXmax = InvalidTransactionId;
 
        ItemPointerCopy(tid, &tupid);
 
@@ -4822,6 +4823,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);
 
@@ -4922,6 +4935,7 @@ l4:
                }
 
                /* tail recursion */
+               priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
                ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
                UnlockReleaseBuffer(buf);
        }