From cff9a17e02e98f4bdfadfd251174605588c46ea2 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Wed, 11 Jan 2023 15:00:49 +0300 Subject: [PATCH 1/2] Evade extra table_tuple_fetch_row_version() in ExecUpdate()/ExecDelete() We can skip table_tuple_fetch_row_version() in the case we already fetched the tuple during locking it. Reported-by: Bug: Discussion: Author: Reviewed-by: Tested-by: Backpatch-through: --- src/backend/executor/nodeModifyTable.c | 46 ++++++++++++++++++-------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 687a5422eab..87a4e553b9e 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1561,6 +1561,21 @@ ldelete: { case TM_Ok: 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, resultRelationDesc, resultRelInfo->ri_RangeTableIndex, @@ -1675,12 +1690,16 @@ ldelete: } 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); if (oldtuple != NULL) { ExecForceStoreHeapTuple(oldtuple, slot, false); } - else + else if (TupIsNull(slot)) { if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid, SnapshotAny, slot)) @@ -2377,6 +2396,19 @@ redo_act: case TM_Ok: 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, resultRelationDesc, resultRelInfo->ri_RangeTableIndex, @@ -2385,18 +2417,6 @@ redo_act: /* Tuple not passing quals anymore, exiting... */ 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, epqslot, oldSlot); goto redo_act; -- 2.37.1 (Apple Git-137.1)