tuple = ExecFetchSlotHeapTuple(slot, false, NULL);
buf = hslot->buffer;
- LockBuffer(buf, BUFFER_LOCK_SHARE);
+ /*
+ * To be able to guarantee that we can set the hint bit, acquire an
+ * exclusive lock on the old buffer. We need the hint bits, set in
+ * heapam_relation_copy_for_cluster() -> HeapTupleSatisfiesVacuum(),
+ * to be set, as otherwise reform_and_rewrite_tuple() ->
+ * rewrite_heap_tuple() will get confused. Specifically,
+ * rewrite_heap_tuple() checks for HEAP_XMAX_INVALID in the old tuple
+ * to determine whether to check the old-to-new mapping hash table.
+ *
+ * It'd be better if we somehow could avoid setting hint bits on the
+ * old page. One reason to use VACUUM FULL are very bloated tables -
+ * rewriting most of the old table during VACUUM FULL doesn't exactly
+ * help...
+ */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
{
HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid)
{
+ /*
+ * The uses from heapam.c rely on being able to perform the hint bit
+ * updates, which can only be guaranteed if we are holding an exclusive
+ * lock on the buffer - which all callers are doing.
+ */
+ Assert(BufferIsLockedByMeInMode(buffer, BUFFER_LOCK_EXCLUSIVE));
+
SetHintBits(tuple, buffer, infomask, xid);
}
/*
* If the tuple has been updated, check the old-to-new mapping hash table.
+ *
+ * Note that this check relies on the HeapTupleSatisfiesVacuum() in
+ * heapam_relation_copy_for_cluster() to have set hint bits.
*/
if (!((old_tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
HeapTupleHeaderIsOnlyLocked(old_tuple->t_data)) &&