I'm now exploring code working with heap tuples. The following code in heap_update() catch my eyes.
if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask, *lockmode)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); /* acquire tuple lock, if necessary */ heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode, LockWaitBlock, &have_tuple_lock); /* wait for multixact */ MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask, relation, &oldtup.t_self, XLTW_Update, &remain); checked_lockers = true; locker_remains = remain != 0; LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* * If xwait had just locked the tuple then some other xact * could update this tuple before we get to this point. Check * for xmax change, and start over if so. */ if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) || !TransactionIdEquals(HeapTupleGetRawXmax(&oldtup), xwait)) goto l2; }
Is it safe to rely on same oldtup.t_data pointer after release and re-acquire of buffer content lock? Could the heap tuple be relocated between lock release and re-acquire? I know that we still hold a buffer pin and vacuum would wait for pin release. But other heap operations could still call heap_page_prune() which correspondingly can relocate tuple. Probably, I'm missing something...
Please, forget it. heap_page_prune_opt() do:
/* OK, try to get exclusive buffer lock */ if (!ConditionalLockBufferForCleanup(buffer)) return;
Nobody repairs buffer fragmentation while there is a pin. Everything is right.