diff options
Diffstat (limited to 'src/backend/access/heap/heapam.c')
-rw-r--r-- | src/backend/access/heap/heapam.c | 435 |
1 files changed, 194 insertions, 241 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 3c8a5da0bc8..65536c72148 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -86,7 +86,7 @@ static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask, LockTupleMode mode, bool is_update, TransactionId *result_xmax, uint16 *result_infomask, uint16 *result_infomask2); -static HTSU_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple, +static TM_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, TransactionId xid, LockTupleMode mode); static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask, @@ -1389,7 +1389,6 @@ heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, - bool keep_buf, Relation stats_relation) { ItemPointer tid = &(tuple->t_self); @@ -1419,13 +1418,8 @@ heap_fetch(Relation relation, if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); - if (keep_buf) - *userbuf = buffer; - else - { - ReleaseBuffer(buffer); - *userbuf = InvalidBuffer; - } + ReleaseBuffer(buffer); + *userbuf = InvalidBuffer; tuple->t_data = NULL; return false; } @@ -1441,13 +1435,8 @@ heap_fetch(Relation relation, if (!ItemIdIsNormal(lp)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); - if (keep_buf) - *userbuf = buffer; - else - { - ReleaseBuffer(buffer); - *userbuf = InvalidBuffer; - } + ReleaseBuffer(buffer); + *userbuf = InvalidBuffer; tuple->t_data = NULL; return false; } @@ -1486,14 +1475,9 @@ heap_fetch(Relation relation, return true; } - /* Tuple failed time qual, but maybe caller wants to see it anyway. */ - if (keep_buf) - *userbuf = buffer; - else - { - ReleaseBuffer(buffer); - *userbuf = InvalidBuffer; - } + /* Tuple failed time qual */ + ReleaseBuffer(buffer); + *userbuf = InvalidBuffer; return false; } @@ -1886,40 +1870,12 @@ ReleaseBulkInsertStatePin(BulkInsertState bistate) * The new tuple is stamped with current transaction ID and the specified * command ID. * - * If the HEAP_INSERT_SKIP_WAL option is specified, the new tuple is not - * logged in WAL, even for a non-temp relation. Safe usage of this behavior - * requires that we arrange that all new tuples go into new pages not - * containing any tuples from other transactions, and that the relation gets - * fsync'd before commit. (See also heap_sync() comments) - * - * The HEAP_INSERT_SKIP_FSM option is passed directly to - * RelationGetBufferForTuple, which see for more info. - * - * HEAP_INSERT_FROZEN should only be specified for inserts into - * relfilenodes created during the current subtransaction and when - * there are no prior snapshots or pre-existing portals open. - * This causes rows to be frozen, which is an MVCC violation and - * requires explicit options chosen by user. - * - * HEAP_INSERT_SPECULATIVE is used on so-called "speculative insertions", - * which can be backed out afterwards without aborting the whole transaction. - * Other sessions can wait for the speculative insertion to be confirmed, - * turning it into a regular tuple, or aborted, as if it never existed. - * Speculatively inserted tuples behave as "value locks" of short duration, - * used to implement INSERT .. ON CONFLICT. - * - * HEAP_INSERT_NO_LOGICAL force-disables the emitting of logical decoding - * information for the tuple. This should solely be used during table rewrites - * where RelationIsLogicallyLogged(relation) is not yet accurate for the new - * relation. - * - * Note that most of these options will be applied when inserting into the - * heap's TOAST table, too, if the tuple requires any out-of-line data. Only - * HEAP_INSERT_SPECULATIVE is explicitly ignored, as the toast data does not - * partake in speculative insertion. + * See table_insert for comments about most of the input flags, except that + * this routine directly takes a tuple rather than a slot. * - * The BulkInsertState object (if any; bistate can be NULL for default - * behavior) is also just passed through to RelationGetBufferForTuple. + * There's corresponding HEAP_INSERT_ options to all the TABLE_INSERT_ + * options, and there additionally is HEAP_INSERT_SPECULATIVE which is used to + * implement table_insert_speculative(). * * On return the header fields of *tup are updated to match the stored tuple; * in particular tup->t_self receives the actual TID where the tuple was @@ -2489,36 +2445,20 @@ xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask) /* * heap_delete - delete a tuple * - * NB: do not call this directly unless you are prepared to deal with - * concurrent-update conditions. Use simple_heap_delete instead. + * See table_delete() for an explanation of the parameters, except that this + * routine directly takes a tuple rather than a slot. * - * relation - table to be modified (caller must hold suitable lock) - * tid - TID of tuple to be deleted - * cid - delete command ID (used for visibility test, and stored into - * cmax if successful) - * crosscheck - if not InvalidSnapshot, also check tuple against this - * wait - true if should wait for any conflicting update to commit/abort - * hufd - output parameter, filled in failure cases (see below) - * changingPart - true iff the tuple is being moved to another partition - * table due to an update of the partition key. Otherwise, false. - * - * Normal, successful return value is HeapTupleMayBeUpdated, which - * actually means we did delete it. Failure return codes are - * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated - * (the last only possible if wait == false). - * - * In the failure cases, the routine fills *hufd with the tuple's t_ctid, - * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax - * (the last only for HeapTupleSelfUpdated, since we - * cannot obtain cmax from a combocid generated by another transaction). - * See comments for struct HeapUpdateFailureData for additional info. + * 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 combocid + * generated by another transaction). */ -HTSU_Result +TM_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, - HeapUpdateFailureData *hufd, bool changingPart) + TM_FailureData *tmfd, bool changingPart) { - HTSU_Result result; + TM_Result result; TransactionId xid = GetCurrentTransactionId(); ItemId lp; HeapTupleData tp; @@ -2586,14 +2526,14 @@ heap_delete(Relation relation, ItemPointer tid, l1: result = HeapTupleSatisfiesUpdate(&tp, cid, buffer); - if (result == HeapTupleInvisible) + if (result == TM_Invisible) { UnlockReleaseBuffer(buffer); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("attempted to delete invisible tuple"))); } - else if (result == HeapTupleBeingUpdated && wait) + else if (result == TM_BeingModified && wait) { TransactionId xwait; uint16 infomask; @@ -2687,30 +2627,36 @@ l1: if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) || HEAP_XMAX_IS_LOCKED_ONLY(tp.t_data->t_infomask) || HeapTupleHeaderIsOnlyLocked(tp.t_data)) - result = HeapTupleMayBeUpdated; + result = TM_Ok; + else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid) || + HeapTupleHeaderIndicatesMovedPartitions(tp.t_data)) + result = TM_Updated; else - result = HeapTupleUpdated; + result = TM_Deleted; } - if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated) + if (crosscheck != InvalidSnapshot && result == TM_Ok) { /* Perform additional check for transaction-snapshot mode RI updates */ if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer)) - result = HeapTupleUpdated; + result = TM_Updated; } - if (result != HeapTupleMayBeUpdated) + if (result != TM_Ok) { - Assert(result == HeapTupleSelfUpdated || - result == HeapTupleUpdated || - result == HeapTupleBeingUpdated); + Assert(result == TM_SelfModified || + result == TM_Updated || + result == TM_Deleted || + result == TM_BeingModified); Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID)); - hufd->ctid = tp.t_data->t_ctid; - hufd->xmax = HeapTupleHeaderGetUpdateXid(tp.t_data); - if (result == HeapTupleSelfUpdated) - hufd->cmax = HeapTupleHeaderGetCmax(tp.t_data); + 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 - hufd->cmax = InvalidCommandId; + tmfd->cmax = InvalidCommandId; UnlockReleaseBuffer(buffer); if (have_tuple_lock) UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive); @@ -2896,7 +2842,7 @@ l1: if (old_key_tuple != NULL && old_key_copied) heap_freetuple(old_key_tuple); - return HeapTupleMayBeUpdated; + return TM_Ok; } /* @@ -2910,28 +2856,32 @@ l1: void simple_heap_delete(Relation relation, ItemPointer tid) { - HTSU_Result result; - HeapUpdateFailureData hufd; + TM_Result result; + TM_FailureData tmfd; result = heap_delete(relation, tid, GetCurrentCommandId(true), InvalidSnapshot, true /* wait for commit */ , - &hufd, false /* changingPart */ ); + &tmfd, false /* changingPart */ ); switch (result) { - case HeapTupleSelfUpdated: + case TM_SelfModified: /* Tuple was already updated in current command? */ elog(ERROR, "tuple already updated by self"); break; - case HeapTupleMayBeUpdated: + case TM_Ok: /* done successfully */ break; - case HeapTupleUpdated: + case TM_Updated: elog(ERROR, "tuple concurrently updated"); break; + case TM_Deleted: + elog(ERROR, "tuple concurrently deleted"); + break; + default: elog(ERROR, "unrecognized heap_delete status: %u", result); break; @@ -2941,42 +2891,20 @@ simple_heap_delete(Relation relation, ItemPointer tid) /* * heap_update - replace a tuple * - * NB: do not call this directly unless you are prepared to deal with - * concurrent-update conditions. Use simple_heap_update instead. - * - * relation - table to be modified (caller must hold suitable lock) - * otid - TID of old tuple to be replaced - * newtup - newly constructed tuple data to store - * cid - update command ID (used for visibility test, and stored into - * cmax/cmin if successful) - * crosscheck - if not InvalidSnapshot, also check old tuple against this - * wait - true if should wait for any conflicting update to commit/abort - * hufd - output parameter, filled in failure cases (see below) - * lockmode - output parameter, filled with lock mode acquired on tuple - * - * Normal, successful return value is HeapTupleMayBeUpdated, which - * actually means we *did* update it. Failure return codes are - * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated - * (the last only possible if wait == false). + * See table_update() for an explanation of the parameters, except that this + * routine directly takes a tuple rather than a slot. * - * On success, the header fields of *newtup are updated to match the new - * stored tuple; in particular, newtup->t_self is set to the TID where the - * new tuple was inserted, and its HEAP_ONLY_TUPLE flag is set iff a HOT - * update was done. However, any TOAST changes in the new tuple's - * data are not reflected into *newtup. - * - * In the failure cases, the routine fills *hufd with the tuple's t_ctid, - * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax - * (the last only for HeapTupleSelfUpdated, since we - * cannot obtain cmax from a combocid generated by another transaction). - * See comments for struct HeapUpdateFailureData for additional info. + * 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 combocid + * generated by another transaction). */ -HTSU_Result +TM_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, - HeapUpdateFailureData *hufd, LockTupleMode *lockmode) + TM_FailureData *tmfd, LockTupleMode *lockmode) { - HTSU_Result result; + TM_Result result; TransactionId xid = GetCurrentTransactionId(); Bitmapset *hot_attrs; Bitmapset *key_attrs; @@ -3150,16 +3078,16 @@ l2: result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer); /* see below about the "no wait" case */ - Assert(result != HeapTupleBeingUpdated || wait); + Assert(result != TM_BeingModified || wait); - if (result == HeapTupleInvisible) + if (result == TM_Invisible) { UnlockReleaseBuffer(buffer); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("attempted to update invisible tuple"))); } - else if (result == HeapTupleBeingUpdated && wait) + else if (result == TM_BeingModified && wait) { TransactionId xwait; uint16 infomask; @@ -3250,7 +3178,7 @@ l2: * MultiXact. In that case, we need to check whether it committed * or aborted. If it aborted we are safe to update it again; * otherwise there is an update conflict, and we have to return - * HeapTupleUpdated below. + * TableTuple{Deleted, Updated} below. * * In the LockTupleExclusive case, we still need to preserve the * surviving members: those would include the tuple locks we had @@ -3322,28 +3250,40 @@ l2: can_continue = true; } - result = can_continue ? HeapTupleMayBeUpdated : HeapTupleUpdated; + if (can_continue) + result = TM_Ok; + else if (!ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid) || + HeapTupleHeaderIndicatesMovedPartitions(oldtup.t_data)) + result = TM_Updated; + else + result = TM_Deleted; } - if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated) + if (crosscheck != InvalidSnapshot && result == TM_Ok) { /* Perform additional check for transaction-snapshot mode RI updates */ if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer)) - result = HeapTupleUpdated; + { + result = TM_Updated; + Assert(!ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid)); + } } - if (result != HeapTupleMayBeUpdated) + if (result != TM_Ok) { - Assert(result == HeapTupleSelfUpdated || - result == HeapTupleUpdated || - result == HeapTupleBeingUpdated); + Assert(result == TM_SelfModified || + result == TM_Updated || + result == TM_Deleted || + result == TM_BeingModified); Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID)); - hufd->ctid = oldtup.t_data->t_ctid; - hufd->xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data); - if (result == HeapTupleSelfUpdated) - hufd->cmax = HeapTupleHeaderGetCmax(oldtup.t_data); + Assert(result != TM_Updated || + !ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid)); + tmfd->ctid = oldtup.t_data->t_ctid; + tmfd->xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data); + if (result == TM_SelfModified) + tmfd->cmax = HeapTupleHeaderGetCmax(oldtup.t_data); else - hufd->cmax = InvalidCommandId; + tmfd->cmax = InvalidCommandId; UnlockReleaseBuffer(buffer); if (have_tuple_lock) UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode); @@ -3828,7 +3768,7 @@ l2: bms_free(modified_attrs); bms_free(interesting_attrs); - return HeapTupleMayBeUpdated; + return TM_Ok; } /* @@ -3948,29 +3888,33 @@ HeapDetermineModifiedColumns(Relation relation, Bitmapset *interesting_cols, void simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup) { - HTSU_Result result; - HeapUpdateFailureData hufd; + TM_Result result; + TM_FailureData tmfd; LockTupleMode lockmode; result = heap_update(relation, otid, tup, GetCurrentCommandId(true), InvalidSnapshot, true /* wait for commit */ , - &hufd, &lockmode); + &tmfd, &lockmode); switch (result) { - case HeapTupleSelfUpdated: + case TM_SelfModified: /* Tuple was already updated in current command? */ elog(ERROR, "tuple already updated by self"); break; - case HeapTupleMayBeUpdated: + case TM_Ok: /* done successfully */ break; - case HeapTupleUpdated: + case TM_Updated: elog(ERROR, "tuple concurrently updated"); break; + case TM_Deleted: + elog(ERROR, "tuple concurrently deleted"); + break; + default: elog(ERROR, "unrecognized heap_update status: %u", result); break; @@ -4005,7 +3949,7 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update) * * Input parameters: * relation: relation containing tuple (caller must hold suitable lock) - * tuple->t_self: TID of tuple to lock (rest of struct need not be valid) + * tid: TID of tuple to lock * cid: current command ID (used for visibility test, and stored into * tuple's cmax if lock is successful) * mode: indicates if shared or exclusive tuple lock is desired @@ -4016,31 +3960,26 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update) * Output parameters: * *tuple: all fields filled in * *buffer: set to buffer holding tuple (pinned but not locked at exit) - * *hufd: filled in failure cases (see below) + * *tmfd: filled in failure cases (see below) * - * Function result may be: - * HeapTupleMayBeUpdated: lock was successfully acquired - * HeapTupleInvisible: lock failed because tuple was never visible to us - * HeapTupleSelfUpdated: lock failed because tuple updated by self - * HeapTupleUpdated: lock failed because tuple updated by other xact - * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip + * Function results are the same as the ones for table_lock_tuple(). * - * In the failure cases other than HeapTupleInvisible, the routine fills - * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact, - * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated, + * In the failure cases other than TM_Invisible, 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 combocid generated by another * transaction). - * See comments for struct HeapUpdateFailureData for additional info. + * See comments for struct TM_FailureData for additional info. * * See README.tuplock for a thorough explanation of this mechanism. */ -HTSU_Result +TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, - Buffer *buffer, HeapUpdateFailureData *hufd) + Buffer *buffer, TM_FailureData *tmfd) { - HTSU_Result result; + TM_Result result; ItemPointer tid = &(tuple->t_self); ItemId lp; Page page; @@ -4080,7 +4019,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, l3: result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer); - if (result == HeapTupleInvisible) + if (result == TM_Invisible) { /* * This is possible, but only when locking a tuple for ON CONFLICT @@ -4088,10 +4027,12 @@ l3: * order to give that case the opportunity to throw a more specific * error. */ - result = HeapTupleInvisible; + result = TM_Invisible; goto out_locked; } - else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated) + else if (result == TM_BeingModified || + result == TM_Updated || + result == TM_Deleted) { TransactionId xwait; uint16 infomask; @@ -4147,7 +4088,7 @@ l3: if (TUPLOCK_from_mxstatus(members[i].status) >= mode) { pfree(members); - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_unlocked; } } @@ -4163,20 +4104,20 @@ l3: Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) || HEAP_XMAX_IS_SHR_LOCKED(infomask) || HEAP_XMAX_IS_EXCL_LOCKED(infomask)); - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_unlocked; case LockTupleShare: if (HEAP_XMAX_IS_SHR_LOCKED(infomask) || HEAP_XMAX_IS_EXCL_LOCKED(infomask)) { - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_unlocked; } break; case LockTupleNoKeyExclusive: if (HEAP_XMAX_IS_EXCL_LOCKED(infomask)) { - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_unlocked; } break; @@ -4184,7 +4125,7 @@ l3: if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) && infomask2 & HEAP_KEYS_UPDATED) { - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_unlocked; } break; @@ -4233,12 +4174,12 @@ l3: */ if (follow_updates && updated) { - HTSU_Result res; + TM_Result res; res = heap_lock_updated_tuple(relation, tuple, &t_ctid, GetCurrentTransactionId(), mode); - if (res != HeapTupleMayBeUpdated) + if (res != TM_Ok) { result = res; /* recovery code expects to have buffer lock held */ @@ -4363,15 +4304,15 @@ l3: /* * Time to sleep on the other transaction/multixact, if necessary. * - * If the other transaction is an update that's already committed, - * then sleeping cannot possibly do any good: if we're required to - * sleep, get out to raise an error instead. + * If the other transaction is an update/delete that's already + * committed, then sleeping cannot possibly do any good: if we're + * required to sleep, get out to raise an error instead. * * By here, we either have already acquired the buffer exclusive lock, * or we must wait for the locking transaction or multixact; so below * we ensure that we grab buffer lock after the sleep. */ - if (require_sleep && result == HeapTupleUpdated) + if (require_sleep && (result == TM_Updated || result == TM_Deleted)) { LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); goto failed; @@ -4394,7 +4335,7 @@ l3: * This can only happen if wait_policy is Skip and the lock * couldn't be obtained. */ - result = HeapTupleWouldBlock; + result = TM_WouldBlock; /* recovery code expects to have buffer lock held */ LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); goto failed; @@ -4420,7 +4361,7 @@ l3: status, infomask, relation, NULL)) { - result = HeapTupleWouldBlock; + result = TM_WouldBlock; /* recovery code expects to have buffer lock held */ LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); goto failed; @@ -4460,7 +4401,7 @@ l3: case LockWaitSkip: if (!ConditionalXactLockTableWait(xwait)) { - result = HeapTupleWouldBlock; + result = TM_WouldBlock; /* recovery code expects to have buffer lock held */ LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); goto failed; @@ -4479,12 +4420,12 @@ l3: /* if there are updates, follow the update chain */ if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask)) { - HTSU_Result res; + TM_Result res; res = heap_lock_updated_tuple(relation, tuple, &t_ctid, GetCurrentTransactionId(), mode); - if (res != HeapTupleMayBeUpdated) + if (res != TM_Ok) { result = res; /* recovery code expects to have buffer lock held */ @@ -4530,23 +4471,28 @@ l3: (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) || HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) || HeapTupleHeaderIsOnlyLocked(tuple->t_data)) - result = HeapTupleMayBeUpdated; + result = TM_Ok; + else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid) || + HeapTupleHeaderIndicatesMovedPartitions(tuple->t_data)) + result = TM_Updated; else - result = HeapTupleUpdated; + result = TM_Deleted; } failed: - if (result != HeapTupleMayBeUpdated) + if (result != TM_Ok) { - Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated || - result == HeapTupleWouldBlock); + Assert(result == TM_SelfModified || result == TM_Updated || + result == TM_Deleted || result == TM_WouldBlock); Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID)); - hufd->ctid = tuple->t_data->t_ctid; - hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data); - if (result == HeapTupleSelfUpdated) - hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data); + Assert(result != TM_Updated || + !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid)); + tmfd->ctid = tuple->t_data->t_ctid; + tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data); + if (result == TM_SelfModified) + tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data); else - hufd->cmax = InvalidCommandId; + tmfd->cmax = InvalidCommandId; goto out_locked; } @@ -4664,7 +4610,7 @@ failed: END_CRIT_SECTION(); - result = HeapTupleMayBeUpdated; + result = TM_Ok; out_locked: LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); @@ -5021,19 +4967,19 @@ l5: * Given a hypothetical multixact status held by the transaction identified * with the given xid, does the current transaction need to wait, fail, or can * it continue if it wanted to acquire a lock of the given mode? "needwait" - * is set to true if waiting is necessary; if it can continue, then - * HeapTupleMayBeUpdated is returned. If the lock is already held by the - * current transaction, return HeapTupleSelfUpdated. In case of a conflict - * with another transaction, a different HeapTupleSatisfiesUpdate return code - * is returned. + * is set to true if waiting is necessary; if it can continue, then TM_Ok is + * returned. If the lock is already held by the current transaction, return + * TM_SelfModified. In case of a conflict with another transaction, a + * different HeapTupleSatisfiesUpdate return code is returned. * * The held status is said to be hypothetical because it might correspond to a * lock held by a single Xid, i.e. not a real MultiXactId; we express it this * way for simplicity of API. */ -static HTSU_Result +static TM_Result test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid, - LockTupleMode mode, bool *needwait) + LockTupleMode mode, HeapTuple tup, + bool *needwait) { MultiXactStatus wantedstatus; @@ -5052,7 +4998,7 @@ test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid, * very rare but can happen if multiple transactions are trying to * lock an ancient version of the same tuple. */ - return HeapTupleSelfUpdated; + return TM_SelfModified; } else if (TransactionIdIsInProgress(xid)) { @@ -5072,10 +5018,10 @@ test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid, * If we set needwait above, then this value doesn't matter; * otherwise, this value signals to caller that it's okay to proceed. */ - return HeapTupleMayBeUpdated; + return TM_Ok; } else if (TransactionIdDidAbort(xid)) - return HeapTupleMayBeUpdated; + return TM_Ok; else if (TransactionIdDidCommit(xid)) { /* @@ -5094,18 +5040,24 @@ test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid, * always be checked. */ if (!ISUPDATE_from_mxstatus(status)) - return HeapTupleMayBeUpdated; + return TM_Ok; if (DoLockModesConflict(LOCKMODE_from_mxstatus(status), LOCKMODE_from_mxstatus(wantedstatus))) + { /* bummer */ - return HeapTupleUpdated; + if (!ItemPointerEquals(&tup->t_self, &tup->t_data->t_ctid) || + HeapTupleHeaderIndicatesMovedPartitions(tup->t_data)) + return TM_Updated; + else + return TM_Deleted; + } - return HeapTupleMayBeUpdated; + return TM_Ok; } /* Not in progress, not aborted, not committed -- must have crashed */ - return HeapTupleMayBeUpdated; + return TM_Ok; } @@ -5116,11 +5068,11 @@ test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid, * xid with the given mode; if this tuple is updated, recurse to lock the new * version as well. */ -static HTSU_Result +static TM_Result heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid, LockTupleMode mode) { - HTSU_Result result; + TM_Result result; ItemPointerData tupid; HeapTupleData mytup; Buffer buf; @@ -5145,7 +5097,7 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid, block = ItemPointerGetBlockNumber(&tupid); ItemPointerCopy(&tupid, &(mytup.t_self)); - if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL)) + if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, NULL)) { /* * if we fail to find the updated version of the tuple, it's @@ -5154,7 +5106,7 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid, * chain, and there's no further tuple to lock: return success to * caller. */ - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_unlocked; } @@ -5203,7 +5155,7 @@ l4: !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data), priorXmax)) { - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_locked; } @@ -5214,7 +5166,7 @@ l4: */ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data))) { - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_locked; } @@ -5257,7 +5209,9 @@ l4: { result = test_lockmode_for_conflict(members[i].status, members[i].xid, - mode, &needwait); + mode, + &mytup, + &needwait); /* * If the tuple was already locked by ourselves in a @@ -5269,7 +5223,7 @@ l4: * this tuple and continue locking the next version in the * update chain. */ - if (result == HeapTupleSelfUpdated) + if (result == TM_SelfModified) { pfree(members); goto next; @@ -5284,7 +5238,7 @@ l4: pfree(members); goto l4; } - if (result != HeapTupleMayBeUpdated) + if (result != TM_Ok) { pfree(members); goto out_locked; @@ -5334,7 +5288,7 @@ l4: } result = test_lockmode_for_conflict(status, rawxmax, mode, - &needwait); + &mytup, &needwait); /* * If the tuple was already locked by ourselves in a previous @@ -5345,7 +5299,7 @@ l4: * either. We just need to skip this tuple and continue * locking the next version in the update chain. */ - if (result == HeapTupleSelfUpdated) + if (result == TM_SelfModified) goto next; if (needwait) @@ -5355,7 +5309,7 @@ l4: XLTW_LockUpdated); goto l4; } - if (result != HeapTupleMayBeUpdated) + if (result != TM_Ok) { goto out_locked; } @@ -5415,7 +5369,7 @@ next: ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) || HeapTupleHeaderIsOnlyLocked(mytup.t_data)) { - result = HeapTupleMayBeUpdated; + result = TM_Ok; goto out_locked; } @@ -5425,7 +5379,7 @@ next: UnlockReleaseBuffer(buf); } - result = HeapTupleMayBeUpdated; + result = TM_Ok; out_locked: UnlockReleaseBuffer(buf); @@ -5459,7 +5413,7 @@ out_unlocked: * transaction cannot be using repeatable read or serializable isolation * levels, because that would lead to a serializability failure. */ -static HTSU_Result +static TM_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, TransactionId xid, LockTupleMode mode) { @@ -5485,7 +5439,7 @@ heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, } /* nothing to lock */ - return HeapTupleMayBeUpdated; + return TM_Ok; } /* @@ -5505,7 +5459,7 @@ heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, * An explicit confirmation WAL record also makes logical decoding simpler. */ void -heap_finish_speculative(Relation relation, HeapTuple tuple) +heap_finish_speculative(Relation relation, ItemPointer tid) { Buffer buffer; Page page; @@ -5513,11 +5467,11 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) ItemId lp = NULL; HeapTupleHeader htup; - buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self))); + buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); page = (Page) BufferGetPage(buffer); - offnum = ItemPointerGetOffsetNumber(&(tuple->t_self)); + offnum = ItemPointerGetOffsetNumber(tid); if (PageGetMaxOffsetNumber(page) >= offnum) lp = PageGetItemId(page, offnum); @@ -5533,7 +5487,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) /* NO EREPORT(ERROR) from here till changes are logged */ START_CRIT_SECTION(); - Assert(HeapTupleHeaderIsSpeculative(tuple->t_data)); + Assert(HeapTupleHeaderIsSpeculative(htup)); MarkBufferDirty(buffer); @@ -5541,7 +5495,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) * Replace the speculative insertion token with a real t_ctid, pointing to * itself like it does on regular tuples. */ - htup->t_ctid = tuple->t_self; + htup->t_ctid = *tid; /* XLOG stuff */ if (RelationNeedsWAL(relation)) @@ -5549,7 +5503,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) xl_heap_confirm xlrec; XLogRecPtr recptr; - xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self); + xlrec.offnum = ItemPointerGetOffsetNumber(tid); XLogBeginInsert(); @@ -5596,10 +5550,9 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) * confirmation records. */ void -heap_abort_speculative(Relation relation, HeapTuple tuple) +heap_abort_speculative(Relation relation, ItemPointer tid) { TransactionId xid = GetCurrentTransactionId(); - ItemPointer tid = &(tuple->t_self); ItemId lp; HeapTupleData tp; Page page; |