diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 3259354d5e0..7e4c8f52ab2 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -1609,8 +1609,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, * transactions. */ if (all_dead && *all_dead && - HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin, - buffer) != HEAPTUPLE_DEAD) + !HeapTupleIsSurelyDead(heapTuple->t_data, RecentGlobalXmin)) *all_dead = false; /* diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index c1c78c389cb..727e0bf91bf 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -1219,6 +1219,46 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, return HEAPTUPLE_DEAD; } +/* + * HeapTupleIsSurelyDead + * + * Determine whether a tuple is surely dead. We sometimes use this + * in lieu of HeapTupleSatisifesVacuum when the tuple has just been + * tested by HeapTupleSatisfiesMVCC and, therefore, any hint bits that + * can be set should already be set. We assume that if no hint bits + * either for xmin or xmax, the transaction is still running. This is + * therefore faster than HeapTupleSatisfiesVacuum, because we don't + * consult CLOG (and also because we don't need to give an exact answer, + * just whether or not the tuple is surely dead). + */ +bool +HeapTupleIsSurelyDead(HeapTupleHeader tuple, TransactionId OldestXmin) +{ + /* + * If the inserting transaction is marked invalid, then it aborted, + * and the tuple is definitely dead. If it's marked neither committed + * nor invalid, then we assume it's still alive (since the presumption + * is that all relevant hint bits were just set moments ago). + */ + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + return (tuple->t_infomask & HEAP_XMIN_INVALID) != 0 ? true : false; + + /* + * If the inserting transaction committed, but any deleting transaction + * aborted, the tuple is still alive. Likewise, if XMAX is a lock rather + * than a delete, the tuple is still alive. + */ + if (tuple->t_infomask & + (HEAP_XMAX_INVALID | HEAP_IS_LOCKED | HEAP_XMAX_IS_MULTI)) + return false; + + /* If deleter isn't known to have committed, assume it's still running. */ + if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) + return false; + + /* Deleter committed, so tuple is dead if the XID is old enough. */ + return TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple), OldestXmin); +} /* * XidInMVCCSnapshot diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h index 0f8a7f8c2d2..3d8a480d810 100644 --- a/src/include/utils/tqual.h +++ b/src/include/utils/tqual.h @@ -83,6 +83,8 @@ extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, Buffer buffer); extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, Buffer buffer); +extern bool HeapTupleIsSurelyDead(HeapTupleHeader tuple, + TransactionId OldestXmin); extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid);