Evade extra table_tuple_fetch_row_version() in ExecUpdate()/ExecDelete()

When we lock tuple using table_tuple_lock() then we at the same time fetch
the locked tuple to the slot.  In this case we can skip extra
table_tuple_fetch_row_version() thank to we've already fetched the 'old' tuple
and nobody can change it concurrently since it's locked.

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:
Alexander Korotkov 2023-03-23 00:12:00 +03:00
parent c75a623304
commit 764da7710b
1 changed files with 35 additions and 13 deletions

View File

@ -1559,6 +1559,22 @@ ldelete:
{ {
case TM_Ok: case TM_Ok:
Assert(context->tmfd.traversed); Assert(context->tmfd.traversed);
/*
* Save locked tuple for further processing of
* RETURNING clause.
*/
if (processReturning &&
resultRelInfo->ri_projectReturning &&
!resultRelInfo->ri_FdwRoutine)
{
TupleTableSlot *returningSlot;
returningSlot = ExecGetReturningSlot(estate, resultRelInfo);
ExecCopySlot(returningSlot, inputslot);
ExecMaterializeSlot(returningSlot);
}
epqslot = EvalPlanQual(context->epqstate, epqslot = EvalPlanQual(context->epqstate,
resultRelationDesc, resultRelationDesc,
resultRelInfo->ri_RangeTableIndex, resultRelInfo->ri_RangeTableIndex,
@ -1673,12 +1689,17 @@ ldelete:
} }
else else
{ {
/*
* Tuple can be already fetched to the returning slot in case
* we've previously locked it. Fetch the tuple only if the slot
* is empty.
*/
slot = ExecGetReturningSlot(estate, resultRelInfo); slot = ExecGetReturningSlot(estate, resultRelInfo);
if (oldtuple != NULL) if (oldtuple != NULL)
{ {
ExecForceStoreHeapTuple(oldtuple, slot, false); ExecForceStoreHeapTuple(oldtuple, slot, false);
} }
else else if (TupIsNull(slot))
{ {
if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid, if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
SnapshotAny, slot)) SnapshotAny, slot))
@ -2393,6 +2414,19 @@ redo_act:
case TM_Ok: case TM_Ok:
Assert(context->tmfd.traversed); Assert(context->tmfd.traversed);
/* Make sure ri_oldTupleSlot is initialized. */
if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
ExecInitUpdateProjection(context->mtstate,
resultRelInfo);
/*
* Save the locked tuple for further calculation
* of the new tuple.
*/
oldSlot = resultRelInfo->ri_oldTupleSlot;
ExecCopySlot(oldSlot, inputslot);
ExecMaterializeSlot(oldSlot);
epqslot = EvalPlanQual(context->epqstate, epqslot = EvalPlanQual(context->epqstate,
resultRelationDesc, resultRelationDesc,
resultRelInfo->ri_RangeTableIndex, resultRelInfo->ri_RangeTableIndex,
@ -2401,18 +2435,6 @@ redo_act:
/* Tuple not passing quals anymore, exiting... */ /* Tuple not passing quals anymore, exiting... */
return NULL; return NULL;
/* Make sure ri_oldTupleSlot is initialized. */
if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
ExecInitUpdateProjection(context->mtstate,
resultRelInfo);
/* Fetch the most recent version of old tuple. */
oldSlot = resultRelInfo->ri_oldTupleSlot;
if (!table_tuple_fetch_row_version(resultRelationDesc,
tupleid,
SnapshotAny,
oldSlot))
elog(ERROR, "failed to fetch tuple being updated");
slot = ExecGetUpdateNewTuple(resultRelInfo, slot = ExecGetUpdateNewTuple(resultRelInfo,
epqslot, oldSlot); epqslot, oldSlot);
goto redo_act; goto redo_act;