mirror of
https://github.com/postgres/postgres.git
synced 2025-11-18 02:02:55 +03:00
Fix concurrent update issue with MERGE.
When executing a MERGE UPDATE action, if there is more than one concurrent update of the target row, the lock-and-retry code would sometimes incorrectly identify the latest version of the target tuple, leading to incorrect results. This was caused by using the ctid field from the TM_FailureData returned by table_tuple_lock() in a case where the result was TM_Ok, which is unsafe because the TM_FailureData struct is not guaranteed to be fully populated in that case. Instead, it should use the tupleid passed to (and updated by) table_tuple_lock(). To reduce the chances of similar errors in the future, improve the commentary for table_tuple_lock() and TM_FailureData to make it clearer that table_tuple_lock() updates the tid passed to it, and most fields of TM_FailureData should not be relied on in non-failure cases. An exception to this is the "traversed" field, which is set in both success and failure cases. Reported-by: Dmitry <dsy.075@yandex.ru> Author: Yugo Nagata <nagata@sraoss.co.jp> Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com> Reviewed-by: Chao Li <li.evan.chao@gmail.com> Discussion: https://postgr.es/m/1570d30e-2b95-4239-b9c3-f7bf2f2f8556@yandex.ru Backpatch-through: 15
This commit is contained in:
@@ -3402,7 +3402,7 @@ lmerge_matched:
|
||||
* the tuple moved, and setting our current
|
||||
* resultRelInfo to that.
|
||||
*/
|
||||
if (ItemPointerIndicatesMovedPartitions(&context->tmfd.ctid))
|
||||
if (ItemPointerIndicatesMovedPartitions(tupleid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
|
||||
errmsg("tuple to be merged was already moved to another partition due to concurrent update")));
|
||||
@@ -3450,12 +3450,13 @@ lmerge_matched:
|
||||
if (ItemPointerIsValid(&lockedtid))
|
||||
UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
|
||||
InplaceUpdateTupleLock);
|
||||
LockTuple(resultRelInfo->ri_RelationDesc, &context->tmfd.ctid,
|
||||
LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
|
||||
InplaceUpdateTupleLock);
|
||||
lockedtid = context->tmfd.ctid;
|
||||
lockedtid = *tupleid;
|
||||
}
|
||||
|
||||
if (!table_tuple_fetch_row_version(resultRelationDesc,
|
||||
&context->tmfd.ctid,
|
||||
tupleid,
|
||||
SnapshotAny,
|
||||
resultRelInfo->ri_oldTupleSlot))
|
||||
elog(ERROR, "failed to fetch the target tuple");
|
||||
|
||||
Reference in New Issue
Block a user