@@ -1718,7 +1718,8 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
17181718 * broken.
17191719 */
17201720 if (TransactionIdIsValid (prev_xmax ) &&
1721- !HeapTupleUpdateXmaxMatchesXmin (prev_xmax , heapTuple -> t_data ))
1721+ !TransactionIdEquals (prev_xmax ,
1722+ HeapTupleHeaderGetXmin (heapTuple -> t_data )))
17221723 break ;
17231724
17241725 /*
@@ -1887,7 +1888,7 @@ heap_get_latest_tid(Relation relation,
18871888 * tuple. Check for XMIN match.
18881889 */
18891890 if (TransactionIdIsValid (priorXmax ) &&
1890- ! HeapTupleUpdateXmaxMatchesXmin (priorXmax , tp .t_data ))
1891+ ! TransactionIdEquals (priorXmax , HeapTupleHeaderGetXmin ( tp .t_data ) ))
18911892 {
18921893 UnlockReleaseBuffer (buffer );
18931894 break ;
@@ -1919,39 +1920,6 @@ heap_get_latest_tid(Relation relation,
19191920 } /* end of loop */
19201921}
19211922
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- }
19551923
19561924/*
19571925 * UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -5077,7 +5045,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
50775045 * the end of the chain, we're done, so return success.
50785046 */
50795047 if (TransactionIdIsValid (priorXmax ) &&
5080- !HeapTupleUpdateXmaxMatchesXmin (priorXmax , mytup .t_data ))
5048+ !TransactionIdEquals (HeapTupleHeaderGetXmin (mytup .t_data ),
5049+ priorXmax ))
50815050 {
50825051 UnlockReleaseBuffer (buf );
50835052 return HeapTupleMayBeUpdated ;
@@ -5520,26 +5489,14 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
55205489 Assert (TransactionIdIsValid (xid ));
55215490
55225491 /*
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.
55305494 */
55315495 if (TransactionIdPrecedes (xid , cutoff_xid ))
55325496 {
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 */
55435500 }
55445501 else
55455502 {
@@ -5610,51 +5567,65 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
56105567
56115568 /*
56125569 * 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.
56145578 *
56155579 * As with all tuple visibility routines, it's critical to test
5616- * TransactionIdIsInProgress before TransactionIdDidCommit ,
5580+ * TransactionIdIsInProgress before the transam.c routines ,
56175581 * 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.
56245582 */
56255583 if (TransactionIdIsCurrentTransactionId (xid ) ||
56265584 TransactionIdIsInProgress (xid ))
56275585 {
56285586 Assert (!TransactionIdIsValid (update_xid ));
56295587 update_xid = xid ;
56305588 }
5631- else if (TransactionIdDidCommit (xid ))
5589+ else if (! TransactionIdDidAbort (xid ))
56325590 {
56335591 /*
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.
56375595 */
5596+ if (TransactionIdDidCommit (xid ))
5597+ update_committed = true;
56385598 Assert (!TransactionIdIsValid (update_xid ));
5639- update_committed = true;
56405599 update_xid = xid ;
56415600 }
56425601
5643- /*
5644- * Not in progress, not committed -- must be aborted or crashed;
5645- * we can ignore it.
5646- */
5647-
56485602 /*
56495603 * If we determined that it's an Xid corresponding to an update
56505604 * 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
56525606 * might still decide to use only an update Xid and not a multi,
56535607 * but it's easier to maintain the list as we walk the old members
56545608 * 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.
56555614 */
56565615 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+ }
56585629 }
56595630 else
56605631 {
@@ -5721,10 +5692,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
57215692 *
57225693 * It is assumed that the caller has checked the tuple with
57235694 * 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).
57285696 *
57295697 * NB: cutoff_xid *must* be <= the current global xmin, to ensure that any
57305698 * 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,
58355803 else if (TransactionIdIsNormal (xid ) &&
58365804 TransactionIdPrecedes (xid , cutoff_xid ))
58375805 {
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;
58505807 }
58515808
58525809 if (freeze_xmax )
0 commit comments