mirror of
https://github.com/postgres/postgres.git
synced 2025-10-19 15:49:24 +03:00
Allow locking updated tuples in tuple_update() and tuple_delete()
Currently, in read committed transaction isolation mode (default), we have the following sequence of actions when tuple_update()/tuple_delete() finds the tuple updated by concurrent transaction. 1. Attempt to update/delete tuple with tuple_update()/tuple_delete(), which returns TM_Updated. 2. Lock tuple with tuple_lock(). 3. Re-evaluate plan qual (recheck if we still need to update/delete and calculate the new tuple for update). 4. Second attempt to update/delete tuple with tuple_update()/tuple_delete(). This attempt should be successful, since the tuple was previously locked. This patch eliminates step 2 by taking the lock during first tuple_update()/tuple_delete() call. Heap table access method saves some efforts by checking the updated tuple once instead of twice. Future undo-based table access methods, which will start from the latest row version, can immediately place a lock there. The code in nodeModifyTable.c is simplified by removing the nested switch/case. Discussion: https://postgr.es/m/CAPpHfdua-YFw3XTprfutzGp28xXLigFtzNbuFY8yPhqeq6X5kg%40mail.gmail.com Reviewed-by: Aleksander Alekseev, Pavel Borisov, Vignesh C, Mason Sharp Reviewed-by: Andres Freund, Chris Travers
This commit is contained in:
@@ -530,7 +530,8 @@ typedef struct TableAmRoutine
|
||||
Snapshot crosscheck,
|
||||
bool wait,
|
||||
TM_FailureData *tmfd,
|
||||
bool changingPart);
|
||||
bool changingPart,
|
||||
LazyTupleTableSlot *lockedSlot);
|
||||
|
||||
/* see table_tuple_update() for reference about parameters */
|
||||
TM_Result (*tuple_update) (Relation rel,
|
||||
@@ -542,7 +543,8 @@ typedef struct TableAmRoutine
|
||||
bool wait,
|
||||
TM_FailureData *tmfd,
|
||||
LockTupleMode *lockmode,
|
||||
TU_UpdateIndexes *update_indexes);
|
||||
TU_UpdateIndexes *update_indexes,
|
||||
LazyTupleTableSlot *lockedSlot);
|
||||
|
||||
/* see table_tuple_lock() for reference about parameters */
|
||||
TM_Result (*tuple_lock) (Relation rel,
|
||||
@@ -1457,7 +1459,7 @@ table_multi_insert(Relation rel, TupleTableSlot **slots, int nslots,
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a tuple.
|
||||
* Delete a tuple (or lock last tuple version if lockedSlot is given).
|
||||
*
|
||||
* NB: do not call this directly unless prepared to deal with
|
||||
* concurrent-update conditions. Use simple_table_tuple_delete instead.
|
||||
@@ -1473,6 +1475,8 @@ table_multi_insert(Relation rel, TupleTableSlot **slots, int nslots,
|
||||
* tmfd - 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.
|
||||
* lockedSlot - lazy slot to save the locked tuple if should lock the last
|
||||
* row version during the concurrent update. NULL if not needed.
|
||||
*
|
||||
* Normal, successful return value is TM_Ok, which means we did actually
|
||||
* delete it. Failure return codes are TM_SelfModified, TM_Updated, and
|
||||
@@ -1485,15 +1489,17 @@ table_multi_insert(Relation rel, TupleTableSlot **slots, int nslots,
|
||||
static inline TM_Result
|
||||
table_tuple_delete(Relation rel, ItemPointer tid, CommandId cid,
|
||||
Snapshot snapshot, Snapshot crosscheck, bool wait,
|
||||
TM_FailureData *tmfd, bool changingPart)
|
||||
TM_FailureData *tmfd, bool changingPart,
|
||||
LazyTupleTableSlot *lockedSlot)
|
||||
{
|
||||
return rel->rd_tableam->tuple_delete(rel, tid, cid,
|
||||
snapshot, crosscheck,
|
||||
wait, tmfd, changingPart);
|
||||
wait, tmfd, changingPart,
|
||||
lockedSlot);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a tuple.
|
||||
* Update a tuple (or lock last tuple version if lockedSlot is given).
|
||||
*
|
||||
* NB: do not call this directly unless you are prepared to deal with
|
||||
* concurrent-update conditions. Use simple_table_tuple_update instead.
|
||||
@@ -1511,7 +1517,9 @@ table_tuple_delete(Relation rel, ItemPointer tid, CommandId cid,
|
||||
* lockmode - filled with lock mode acquired on tuple
|
||||
* update_indexes - in success cases this is set to true if new index entries
|
||||
* are required for this tuple
|
||||
*
|
||||
* lockedSlot - lazy slot to save the locked tuple if should lock the last
|
||||
* row version during the concurrent update. NULL if not needed.
|
||||
|
||||
* Normal, successful return value is TM_Ok, which means we did actually
|
||||
* update it. Failure return codes are TM_SelfModified, TM_Updated, and
|
||||
* TM_BeingModified (the last only possible if wait == false).
|
||||
@@ -1530,12 +1538,14 @@ static inline TM_Result
|
||||
table_tuple_update(Relation rel, ItemPointer otid, TupleTableSlot *slot,
|
||||
CommandId cid, Snapshot snapshot, Snapshot crosscheck,
|
||||
bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode,
|
||||
TU_UpdateIndexes *update_indexes)
|
||||
TU_UpdateIndexes *update_indexes,
|
||||
LazyTupleTableSlot *lockedSlot)
|
||||
{
|
||||
return rel->rd_tableam->tuple_update(rel, otid, slot,
|
||||
cid, snapshot, crosscheck,
|
||||
wait, tmfd,
|
||||
lockmode, update_indexes);
|
||||
lockmode, update_indexes,
|
||||
lockedSlot);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user