@@ -1718,7 +1718,8 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
1718
1718
* broken.
1719
1719
*/
1720
1720
if (TransactionIdIsValid (prev_xmax ) &&
1721
- !HeapTupleUpdateXmaxMatchesXmin (prev_xmax , heapTuple -> t_data ))
1721
+ !TransactionIdEquals (prev_xmax ,
1722
+ HeapTupleHeaderGetXmin (heapTuple -> t_data )))
1722
1723
break ;
1723
1724
1724
1725
/*
@@ -1887,7 +1888,7 @@ heap_get_latest_tid(Relation relation,
1887
1888
* tuple. Check for XMIN match.
1888
1889
*/
1889
1890
if (TransactionIdIsValid (priorXmax ) &&
1890
- ! HeapTupleUpdateXmaxMatchesXmin (priorXmax , tp .t_data ))
1891
+ ! TransactionIdEquals (priorXmax , HeapTupleHeaderGetXmin ( tp .t_data ) ))
1891
1892
{
1892
1893
UnlockReleaseBuffer (buffer );
1893
1894
break ;
@@ -1919,39 +1920,6 @@ heap_get_latest_tid(Relation relation,
1919
1920
} /* end of loop */
1920
1921
}
1921
1922
1922
- /*
1923
- * HeapTupleUpdateXmaxMatchesXmin - verify update chain xmax/xmin lineage
1924
- *
1925
- * Given the new version of a tuple after some update, verify whether the
1926
- * given Xmax (corresponding to the previous version) matches the tuple's
1927
- * Xmin, taking into account that the Xmin might have been frozen after the
1928
- * update.
1929
- */
1930
- bool
1931
- HeapTupleUpdateXmaxMatchesXmin (TransactionId xmax , HeapTupleHeader htup )
1932
- {
1933
- TransactionId xmin = HeapTupleHeaderGetXmin (htup );
1934
-
1935
- /*
1936
- * If the xmax of the old tuple is identical to the xmin of the new one,
1937
- * it's a match.
1938
- */
1939
- if (TransactionIdEquals (xmax , xmin ))
1940
- return true;
1941
-
1942
- /*
1943
- * When a tuple is frozen, the original Xmin is lost, but we know it's a
1944
- * committed transaction. So unless the Xmax is InvalidXid, we don't know
1945
- * for certain that there is a match, but there may be one; and we must
1946
- * return true so that a HOT chain that is half-frozen can be walked
1947
- * correctly.
1948
- */
1949
- if (TransactionIdEquals (xmin , FrozenTransactionId ) &&
1950
- TransactionIdIsValid (xmax ))
1951
- return true;
1952
-
1953
- return false;
1954
- }
1955
1923
1956
1924
/*
1957
1925
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -5077,7 +5045,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
5077
5045
* the end of the chain, we're done, so return success.
5078
5046
*/
5079
5047
if (TransactionIdIsValid (priorXmax ) &&
5080
- !HeapTupleUpdateXmaxMatchesXmin (priorXmax , mytup .t_data ))
5048
+ !TransactionIdEquals (HeapTupleHeaderGetXmin (mytup .t_data ),
5049
+ priorXmax ))
5081
5050
{
5082
5051
UnlockReleaseBuffer (buf );
5083
5052
return HeapTupleMayBeUpdated ;
@@ -5520,26 +5489,14 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5520
5489
Assert (TransactionIdIsValid (xid ));
5521
5490
5522
5491
/*
5523
- * The updating transaction cannot possibly be still running, but
5524
- * verify whether it has committed, and request to set the
5525
- * COMMITTED flag if so. (We normally don't see any tuples in
5526
- * this state, because they are removed by page pruning before we
5527
- * try to freeze the page; but this can happen if the updating
5528
- * transaction commits after the page is pruned but before
5529
- * HeapTupleSatisfiesVacuum).
5492
+ * If the xid is older than the cutoff, it has to have aborted,
5493
+ * otherwise the tuple would have gotten pruned away.
5530
5494
*/
5531
5495
if (TransactionIdPrecedes (xid , cutoff_xid ))
5532
5496
{
5533
- if (TransactionIdDidCommit (xid ))
5534
- {
5535
- xid = FrozenTransactionId ;
5536
- * flags = FRM_MARK_COMMITTED | FRM_RETURN_IS_XID ;
5537
- }
5538
- else
5539
- {
5540
- * flags |= FRM_INVALIDATE_XMAX ;
5541
- xid = InvalidTransactionId ; /* not strictly necessary */
5542
- }
5497
+ Assert (!TransactionIdDidCommit (xid ));
5498
+ * flags |= FRM_INVALIDATE_XMAX ;
5499
+ xid = InvalidTransactionId ; /* not strictly necessary */
5543
5500
}
5544
5501
else
5545
5502
{
@@ -5610,51 +5567,65 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5610
5567
5611
5568
/*
5612
5569
* It's an update; should we keep it? If the transaction is known
5613
- * aborted or crashed then it's okay to ignore it, otherwise not.
5570
+ * aborted then it's okay to ignore it, otherwise not. However,
5571
+ * if the Xid is older than the cutoff_xid, we must remove it.
5572
+ * Note that such an old updater cannot possibly be committed,
5573
+ * because HeapTupleSatisfiesVacuum would have returned
5574
+ * HEAPTUPLE_DEAD and we would not be trying to freeze the tuple.
5575
+ *
5576
+ * Note the TransactionIdDidAbort() test is just an optimization
5577
+ * and not strictly necessary for correctness.
5614
5578
*
5615
5579
* As with all tuple visibility routines, it's critical to test
5616
- * TransactionIdIsInProgress before TransactionIdDidCommit ,
5580
+ * TransactionIdIsInProgress before the transam.c routines ,
5617
5581
* because of race conditions explained in detail in tqual.c.
5618
- *
5619
- * We normally don't see committed updating transactions earlier
5620
- * than the cutoff xid, because they are removed by page pruning
5621
- * before we try to freeze the page; but it can happen if the
5622
- * updating transaction commits after the page is pruned but
5623
- * before HeapTupleSatisfiesVacuum.
5624
5582
*/
5625
5583
if (TransactionIdIsCurrentTransactionId (xid ) ||
5626
5584
TransactionIdIsInProgress (xid ))
5627
5585
{
5628
5586
Assert (!TransactionIdIsValid (update_xid ));
5629
5587
update_xid = xid ;
5630
5588
}
5631
- else if (TransactionIdDidCommit (xid ))
5589
+ else if (! TransactionIdDidAbort (xid ))
5632
5590
{
5633
5591
/*
5634
- * The transaction committed, so we can tell caller to set
5635
- * HEAP_XMAX_COMMITTED. (We can only do this because we know
5636
- * the transaction is not running.)
5592
+ * Test whether to tell caller to set HEAP_XMAX_COMMITTED
5593
+ * while we have the Xid still in cache. Note this can only
5594
+ * be done if the transaction is known not running.
5637
5595
*/
5596
+ if (TransactionIdDidCommit (xid ))
5597
+ update_committed = true;
5638
5598
Assert (!TransactionIdIsValid (update_xid ));
5639
- update_committed = true;
5640
5599
update_xid = xid ;
5641
5600
}
5642
5601
5643
- /*
5644
- * Not in progress, not committed -- must be aborted or crashed;
5645
- * we can ignore it.
5646
- */
5647
-
5648
5602
/*
5649
5603
* If we determined that it's an Xid corresponding to an update
5650
5604
* that must be retained, additionally add it to the list of
5651
- * members of the new Multi , in case we end up using that. (We
5605
+ * members of the new Multis , in case we end up using that. (We
5652
5606
* might still decide to use only an update Xid and not a multi,
5653
5607
* but it's easier to maintain the list as we walk the old members
5654
5608
* list.)
5609
+ *
5610
+ * It is possible to end up with a very old updater Xid that
5611
+ * crashed and thus did not mark itself as aborted in pg_clog.
5612
+ * That would manifest as a pre-cutoff Xid. Make sure to ignore
5613
+ * it.
5655
5614
*/
5656
5615
if (TransactionIdIsValid (update_xid ))
5657
- newmembers [nnewmembers ++ ] = members [i ];
5616
+ {
5617
+ if (!TransactionIdPrecedes (update_xid , cutoff_xid ))
5618
+ {
5619
+ newmembers [nnewmembers ++ ] = members [i ];
5620
+ }
5621
+ else
5622
+ {
5623
+ /* cannot have committed: would be HEAPTUPLE_DEAD */
5624
+ Assert (!TransactionIdDidCommit (update_xid ));
5625
+ update_xid = InvalidTransactionId ;
5626
+ update_committed = false;
5627
+ }
5628
+ }
5658
5629
}
5659
5630
else
5660
5631
{
@@ -5721,10 +5692,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5721
5692
*
5722
5693
* It is assumed that the caller has checked the tuple with
5723
5694
* HeapTupleSatisfiesVacuum() and determined that it is not HEAPTUPLE_DEAD
5724
- * (else we should be removing the tuple, not freezing it). However, note
5725
- * that we don't remove HOT tuples even if they are dead, and it'd be incorrect
5726
- * to freeze them (because that would make them visible), so we mark them as
5727
- * update-committed, and needing further freezing later on.
5695
+ * (else we should be removing the tuple, not freezing it).
5728
5696
*
5729
5697
* NB: cutoff_xid *must* be <= the current global xmin, to ensure that any
5730
5698
* XID older than it could neither be running nor seen as running by any
@@ -5835,18 +5803,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
5835
5803
else if (TransactionIdIsNormal (xid ) &&
5836
5804
TransactionIdPrecedes (xid , cutoff_xid ))
5837
5805
{
5838
- /*
5839
- * Must freeze regular XIDs older than the cutoff. We must not freeze
5840
- * a HOT-updated tuple, though; doing so would bring it back to life.
5841
- */
5842
- if (HeapTupleHeaderIsHotUpdated (tuple ))
5843
- {
5844
- frz -> t_infomask |= HEAP_XMAX_COMMITTED ;
5845
- changed = true;
5846
- /* must not freeze */
5847
- }
5848
- else
5849
- freeze_xmax = true;
5806
+ freeze_xmax = true;
5850
5807
}
5851
5808
5852
5809
if (freeze_xmax )
0 commit comments