本文将从源码层讲解postgres元组的删除流程,实现的本质设置xmax和相关标识位字段信息,逻辑上对外显示删除不可见。
关键数据结构
执行流程
/*
* heap_delete - delete a tuple
*
* See table_tuple_delete() for an explanation of the parameters, except that
* this routine directly takes a tuple rather than a slot.
*
* In the failure cases, the routine fills *tmfd with the tuple's t_ctid,
* t_xmax (resolving a possible MultiXact, if necessary), and t_cmax (the last
* only for TM_SelfModified, since we cannot obtain cmax from a combo CID
* generated by another transaction).
*/
TM_Result
heap_delete(Relation relation, ItemPointer tid,
CommandId cid, Snapshot crosscheck, bool wait,
TM_FailureData *tmfd, bool changingPart)
{
TM_Result result;
TransactionId xid = GetCurrentTransactionId(); // 写操作事务均需要获取事务号
ItemId lp;
HeapTupleData tp;
Page page;
BlockNumber block;
Buffer buffer;
Buffer vmbuffer = InvalidBuffer;
TransactionId new_xmax;
uint16 new_infomask,
new_infomask2;
bool have_tuple_lock = false;
bool iscombo;
bool all_visible_cleared = false;
HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
bool old_key_copied = false;
Assert(ItemPointerIsValid(tid)); // 删除元组的 tid上层函数传入
/*
* Forbid this during a parallel operation, lest it allocate a combo CID.
* Other workers might need that combo CID for visibility checks, and we
* have no provision for broadcasting it to them.
*/
// 删除一条元组禁止开并行模式
if (IsInParallelMode())
ereport(ERROR,
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot delete tuples during a parallel operation")));
// 根据元组的 tid确定块号,并根据块号和 relation 描述符将数据块加载时共享缓冲区中buffer,获取页地址
block = ItemPointerGetBlockNumber(tid);
buffer = ReadBuffer(relation, block);
page = BufferGetPage(buffer);
/*
* Before locking the buffer, pin the visibility map page if it appears to
* be necessary. Since we haven't got the lock yet, someone else might be
* in the middle of changing this, so we'll need to recheck after we have
* the lock.
*/
// 由于删除元组,势必会修改VM对应标识位,如果数据页含有 allvisible标记,则需要将数据页对应的VM页加载至
// vmbuffer(pin住)
if (PageIsAllVisible(page))
visibilitymap_pin(relation, block, &vmbuffer);
// 对 buffer施加排他锁
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
/*
* If we didn't pin the visibility map page and the page has become all
* visible while we were busy locking the buffer, we'll have to unlock and
* re-lock, to avoid holding the buffer lock across an I/O. That's a bit
* unfortunate, but hopefully shouldn't happen often.
*/
// 在获取上述缓冲块排他锁期间,可能有其他进程将对应的VM 数据页标记信息更新为 allvisible,那么此时
// 需要释放buffer 排他锁,pin住 vmbuffer,后在此获取buffer排他锁。,其目的是为了防止在持有buffer
// 排他锁行执行 io (加载vm page 至 vmbuffer)
if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
{
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
visibilitymap_pin(relation, block, &vmbuffer);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
}
// 获取偏移量为tid的元组相关信息
lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
Assert(ItemIdIsNormal(lp));
tp.t_tableOid = RelationGetRelid(relation);
tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
tp.t_len = ItemIdGetLength(lp);
tp.t_self = *tid;
l1:
// 可见性判断:根据其结果判断元组是否满足被更新/删除
result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
// 元组不可见,报错
if (result == TM_Invisible)
{
UnlockReleaseBuffer(buffer);
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("attempted to delete invisible tuple")));
}
// 该元组正在被更新,且等待
else if (result == TM_BeingModified && wait)
{
TransactionId xwait;
uint16 infomask;
/* must copy state data before unlocking buffer */
// 获取 元组的xmax和 infomask,此时并不知道 xmax是单纯的事务号还是 MultiXactId
xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
infomask = tp.t_data->t_infomask;
/*
* Sleep until concurrent transaction ends -- except when there's a
* single locker and it's our own transaction. Note we don't care
* which lock mode the locker has, because we need the strongest one.
*
* Before sleeping, we need to acquire tuple lock to establish our
* priority for the tuple (see heap_lock_tuple). LockTuple will
* release us when we are next-in-line for the tuple.
*
* If we are forced to "start over" below, we keep the tuple lock;
* this arranges that we stay at the head of the line while rechecking
* tuple state.
*/
// 如果是 MultiXactId
if (infomask & HEAP_XMAX_IS_MULTI)
{
bool current_is_member = false;
// 判断 MultiXactId中保存的锁模式是否与 LockTupleExclusive冲突,冲突则
if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
LockTupleExclusive, ¤t_is_member))
{
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
/*
* Acquire the lock, if necessary (but skip it when we're
* requesting a lock and already have one; avoids deadlock).
*/
// 如果当前事务不属于MultiXactId成员,则需获取元组级常规锁,反之无需获取,
// 其目的是避免死锁
if (!current_is_member)
heap_acquire_tuplock(relation, &(tp.t_self), LockTupleExclusive,
LockWaitBlock, &have_tuple_lock);
/* wait for multixact */
// 等待冲突的事务完成
MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask,
relation, &(tp.t_self), XLTW_Delete,
NULL);
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.
*/
// 如果元组的 infomask被其他事务更新,则需重新进行上述操作
if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
xwait))
goto l1;
}
/*
* You might think the multixact is necessarily done here, but not
* so: it could have surviving members, namely our own xact or
* other subxacts of this backend. It is legal for us to delete
* the tuple in either case, however (the latter case is
* essentially a situation of upgrading our former shared lock to
* exclusive). We don't bother changing the on-disk hint bits
* since we are about to overwrite the xmax altogether.
*/
}
// xwait 是单一事务号,且不是当前事务号
else if (!TransactionIdIsCurrentTransactionId(xwait))
{
/*
* Wait for regular transaction to end; but first, acquire tuple
* lock.
*/
// 获取元组级常规锁,等待其他事务的更新操作
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
heap_acquire_tuplock(relation, &(tp.t_self), LockTupleExclusive,
LockWaitBlock, &have_tuple_lock);
XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
/*
* xwait is done, but 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.
*/
// 如果其他事务更新元组,则需回到 l1程序点进行相关操作
if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
xwait))
goto l1;
/* Otherwise check if it committed or aborted */
// 更新元组的 HintBit标识位信息
UpdateXmaxHintBits(tp.t_data, buffer, xwait);
}
/*
* We may overwrite if previous xmax aborted, or if it committed but
* only locked the tuple without updating it.
*/
// 如果 xmax意外终止或者提交但是被他人锁住(未更新)
if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
HEAP_XMAX_IS_LOCKED_ONLY(tp.t_data->t_infomask) ||
HeapTupleHeaderIsOnlyLocked(tp.t_data))
result = TM_Ok;
else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
result = TM_Updated;
else
result = TM_Deleted;
}
if (crosscheck != InvalidSnapshot && result == TM_Ok)
{
/* Perform additional check for transaction-snapshot mode RI updates */
if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
result = TM_Updated;
}
if (result != TM_Ok)
{
Assert(result == TM_SelfModified ||
result == TM_Updated ||
result == TM_Deleted ||
result == TM_BeingModified);
Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID));
Assert(result != TM_Updated ||
!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
tmfd->ctid = tp.t_data->t_ctid;
tmfd->xmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
if (result == TM_SelfModified)
tmfd->cmax = HeapTupleHeaderGetCmax(tp.t_data);
else
tmfd->cmax = InvalidCommandId;
UnlockReleaseBuffer(buffer);
if (have_tuple_lock)
UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
if (vmbuffer != InvalidBuffer)
ReleaseBuffer(vmbuffer);
return result;
}
/*
* We're about to do the actual delete -- check for conflict first, to
* avoid possibly having to roll back work we've just done.
*
* This is safe without a recheck as long as there is no possibility of
* another process scanning the page between this check and the delete
* being visible to the scan (i.e., an exclusive buffer content lock is
* continuously held from this point until the tuple delete is visible).
*/
// 序列化冲突检查
CheckForSerializableConflictIn(relation, tid, BufferGetBlockNumber(buffer));
/* replace cid with a combo CID if necessary */
HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
/*
* Compute replica identity tuple before entering the critical section so
* we don't PANIC upon a memory allocation failure.
*/
old_key_tuple = ExtractReplicaIdentity(relation, &tp, true, &old_key_copied);
/*
* If this is the first possibly-multixact-able operation in the current
* transaction, set my per-backend OldestMemberMXactId setting. We can be
* certain that the transaction will never become a member of any older
* MultiXactIds than that. (We have to do this even if we end up just
* using our own TransactionId below, since some other backend could
* incorporate our XID into a MultiXact immediately afterwards.)
*/
MultiXactIdSetOldestMember();
// 计算新的xmax + infomask + infomask2
compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(tp.t_data),
tp.t_data->t_infomask, tp.t_data->t_infomask2,
xid, LockTupleExclusive, true,
&new_xmax, &new_infomask, &new_infomask2);
START_CRIT_SECTION();
// 临界区
/*
* If this transaction commits, the tuple will become DEAD sooner or
* later. Set flag that this page is a candidate for pruning once our xid
* falls below the OldestXmin horizon. If the transaction finally aborts,
* the subsequent page pruning will be a no-op and the hint will be
* cleared.
*/
PageSetPrunable(page, xid);
// 首先清除元组对应数据页 和 vm 页的all visible标识位
if (PageIsAllVisible(page))
{
all_visible_cleared = true;
PageClearAllVisible(page);
visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
vmbuffer, VISIBILITYMAP_VALID_BITS);
}
// 更新元组头等相关信息
/* store transaction information of xact deleting the tuple */
tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
tp.t_data->t_infomask |= new_infomask;
tp.t_data->t_infomask2 |= new_infomask2;
HeapTupleHeaderClearHotUpdated(tp.t_data);
HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
/* Make sure there is no forward chain link in t_ctid */
tp.t_data->t_ctid = tp.t_self;
/* Signal that this is actually a move into another partition */
if (changingPart)
HeapTupleHeaderSetMovedPartitions(tp.t_data);
MarkBufferDirty(buffer);
/*
* XLOG stuff
*
* NB: heap_abort_speculative() uses the same xlog record and replay
* routines.
*/
// 写XLOG日志,奔溃恢复
if (RelationNeedsWAL(relation))
{
xl_heap_delete xlrec;
xl_heap_header xlhdr;
XLogRecPtr recptr;
/*
* For logical decode we need combo CIDs to properly decode the
* catalog
*/
if (RelationIsAccessibleInLogicalDecoding(relation))
log_heap_new_cid(relation, &tp);
xlrec.flags = 0;
if (all_visible_cleared)
xlrec.flags |= XLH_DELETE_ALL_VISIBLE_CLEARED;
if (changingPart)
xlrec.flags |= XLH_DELETE_IS_PARTITION_MOVE;
xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
tp.t_data->t_infomask2);
xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
xlrec.xmax = new_xmax;
if (old_key_tuple != NULL)
{
if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
xlrec.flags |= XLH_DELETE_CONTAINS_OLD_TUPLE;
else
xlrec.flags |= XLH_DELETE_CONTAINS_OLD_KEY;
}
XLogBeginInsert();
XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
/*
* Log replica identity of the deleted tuple if there is one
*/
if (old_key_tuple != NULL)
{
xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
XLogRegisterData((char *) &xlhdr, SizeOfHeapHeader);
XLogRegisterData((char *) old_key_tuple->t_data
+ SizeofHeapTupleHeader,
old_key_tuple->t_len
- SizeofHeapTupleHeader);
}
/* filtering by origin on a row level is much more efficient */
XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
PageSetLSN(page, recptr);
}
END_CRIT_SECTION();
// 退出临界区 + 释放锁+内存资源
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
if (vmbuffer != InvalidBuffer)
ReleaseBuffer(vmbuffer);
/*
* If the tuple has toasted out-of-line attributes, we need to delete
* those items too. We have to do this before releasing the buffer
* because we need to look at the contents of the tuple, but it's OK to
* release the content lock on the buffer first.
*/
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_MATVIEW)
{
/* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(&tp));
}
else if (HeapTupleHasExternal(&tp))
heap_toast_delete(relation, &tp, false);
/*
* Mark tuple for invalidation from system caches at next command
* boundary. We have to do this before releasing the buffer because we
* need to look at the contents of the tuple.
*/
// 将删除元组在内存中对应的 cache 标记为无效 ==> 普通表元组不执行,系统表元组会标记
CacheInvalidateHeapTuple(relation, &tp, NULL);
/* Now we can release the buffer */
ReleaseBuffer(buffer);
/*
* Release the lmgr tuple lock, if we had it.
*/
if (have_tuple_lock)
UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
pgstat_count_heap_delete(relation);
if (old_key_tuple != NULL && old_key_copied)
heap_freetuple(old_key_tuple);
return TM_Ok;
}
2 HeapTupleSatisfiesUpdate
该函数主要用于并发更新判断元组的状态,并根据元组的状态继续执行后续的操作。其中元组的状态类型如下:
其执行流程如下:
HeapTupleSatisfiesUpdate元组可见性判断(局部)
源代码:
/*
* HeapTupleSatisfiesUpdate
*
* This function returns a more detailed result code than most of the
* functions in this file, since UPDATE needs to know more than "is it
* visible?". It also allows for user-supplied CommandId rather than
* relying on CurrentCommandId.
*
* The possible return codes are:
*
* TM_Invisible: the tuple didn't exist at all when the scan started, e.g. it
* was created by a later CommandId.
*
* TM_Ok: The tuple is valid and visible, so it may be updated.
*
* TM_SelfModified: The tuple was updated by the current transaction, after
* the current scan started.
*
* TM_Updated: The tuple was updated by a committed transaction (including
* the case where the tuple was moved into a different partition).
*
* TM_Deleted: The tuple was deleted by a committed transaction.
*
* TM_BeingModified: The tuple is being updated by an in-progress transaction
* other than the current transaction. (Note: this includes the case where
* the tuple is share-locked by a MultiXact, even if the MultiXact includes
* the current transaction. Callers that want to distinguish that case must
* test for it themselves.)
*/
TM_Result
HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
Buffer buffer)
{
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
// xmin未提交
if (!HeapTupleHeaderXminCommitted(tuple))
{
// xmin 无效
if (HeapTupleHeaderXminInvalid(tuple))
return TM_Invisible;
. . .
// 插入元组操作当前事务
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
// 此元组命令id > 当前执行命令 id, 在快照获取后执行元祖插入操作,则不可见
if (HeapTupleHeaderGetCmin(tuple) >= curcid)
return TM_Invisible; /* inserted after scan started */
// 在快照获取前插入且xmax 无效,插入成功
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return TM_Ok;
// 元组被锁住
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
{
TransactionId xmax;
// 获取元组 xmax信息
xmax = HeapTupleHeaderGetRawXmax(tuple);
/*
* Careful here: even though this tuple was created by our own
* transaction, it might be locked by other transactions, if
* the original version was key-share locked when we updated
* it.
*/
// xmax为 MultiXact 若 MultiXact在运行,表明正在被修改,否则修改完成,元组可见
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
if (MultiXactIdIsRunning(xmax, true))
return TM_BeingModified;
else
return TM_Ok;
}
/*
* If the locker is gone, then there is nothing of interest
* left in this Xmax; otherwise, report the tuple as
* locked/updated.
*/
// 单一事务号且完成,则可见,反之正在被更新
if (!TransactionIdIsInProgress(xmax))
return TM_Ok;
return TM_BeingModified;
}
// xmax为 MultiXact
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
TransactionId xmax;
xmax = HeapTupleGetUpdateXid(tuple);
// 未被锁
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
/* deleting subtransaction must have aborted */
// xmax 不是当前事务,若xmax在运行,则表明元组正在被更新,反之可见
if (!TransactionIdIsCurrentTransactionId(xmax))
{
if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
false))
return TM_BeingModified;
return TM_Ok;
}
else
{
// 根据快照生成顺序进行判断
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
}
// 如果元组 xmax 不是当前事务,表明元组被abort
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
{
/* deleting subtransaction must have aborted */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
return TM_Ok;
}
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
// xmin 正在运行,元组不可见;若commit,则设置commit标识位,反之设置HEAP_XMIN_INVALID
else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
return TM_Invisible;
else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
HeapTupleHeaderGetRawXmin(tuple));
else
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
InvalidTransactionId);
return TM_Invisible;
}
}
/* by here, the inserting transaction has committed */
// xmain 提交,可见
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
return TM_Ok;
// xmax 提交
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
// 元组被锁住,可见; 未锁情况,若 TID发生变化,则已被更新,若 TID 未变,则已被删除
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return TM_Ok;
if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
return TM_Updated; /* updated by other */
else
return TM_Deleted; /* deleted by other */
}
// xmax 为 MultiXactId
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
TransactionId xmax;
if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
return TM_Ok;
// 被锁住,且正在运行则表明元组正在被更新,不在运行可见
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
{
if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
return TM_BeingModified;
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
return TM_Ok;
}
xmax = HeapTupleGetUpdateXid(tuple);
if (!TransactionIdIsValid(xmax))
{
if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
return TM_BeingModified;
}
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
// xmax为当前事务,根据快获取时机进行可见性判断,见上述相关代码
if (TransactionIdIsCurrentTransactionId(xmax))
{
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
return TM_BeingModified;
// clog日志相关
if (TransactionIdDidCommit(xmax))
{
if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
return TM_Updated;
else
return TM_Deleted;
}
/*
* By here, the update in the Xmax is either aborted or crashed, but
* what about the other members?
*/
if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
{
/*
* There's no member, even just a locker, alive anymore, so we can
* mark the Xmax as invalid.
*/
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
return TM_Ok;
}
else
{
/* There are lockers running */
return TM_BeingModified;
}
}
// xmax为当前事务的判断逻辑
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
{
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return TM_BeingModified;
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
// 元组 xamx正在运行,
if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
return TM_BeingModified;
// xmax 未提交发生奔溃或者回滚
if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
return TM_Ok;
}
/* xmax transaction committed */
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
{
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
return TM_Ok;
}
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
HeapTupleHeaderGetRawXmax(tuple));
if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
return TM_Updated; /* updated by other */
else
return TM_Deleted; /* deleted by other */
}