0001-Evade-extra-table_tuple_fetch_row_version-in-Exe-v15.patch

application/octet-stream

Filename: 0001-Evade-extra-table_tuple_fetch_row_version-in-Exe-v15.patch
Type: application/octet-stream
Part: 0
Message: Re: POC: Lock updated tuples in tuple_update() and tuple_delete()

Patch

Format: format-patch
Series: patch v15-0001
Subject: Evade extra table_tuple_fetch_row_version() in ExecUpdate()/ExecDelete()
File+
src/backend/executor/nodeModifyTable.c 35 13
From a8b4e8a7b27815e013ea07b8cc9ac68541a9ac07 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Tue, 21 Mar 2023 00:34:15 +0300
Subject: [PATCH 1/2] 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
---
 src/backend/executor/nodeModifyTable.c | 48 +++++++++++++++++++-------
 1 file changed, 35 insertions(+), 13 deletions(-)

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 3a673895082..93ebfdbb0d8 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1559,6 +1559,22 @@ 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,
@@ -1673,12 +1689,17 @@ 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))
@@ -2393,6 +2414,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,
@@ -2401,18 +2435,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)