0001-Improve-lazy-tuple-slot-v1.patch

application/octet-stream

Filename: 0001-Improve-lazy-tuple-slot-v1.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 v1-0001
Subject: Improve lazy tuple slot
File+
src/backend/access/heap/heapam_handler.c 2 2
src/backend/executor/nodeModifyTable.c 2 2
src/include/executor/tuptable.h 41 16
From 185a6c8ee89aecb49b61b35da3c3c5562a558bd8 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Mon, 27 Mar 2023 15:05:25 +0300
Subject: [PATCH 1/2] Improve lazy tuple slot

This commit implements two improvements to lazy tuple slot implementation.
 * Replace macros with inline function to prevent multiple evaluation of the
   argument.
 * Introdue a new lazy tuple slot constructor on the base of existing normal
   slot, not a callback.

Reported-by: Andres Freund
Discussion: https://postgr.es/m/20230323003003.plgaxjqahjgkuxrk%40awork3.anarazel.de
---
 src/backend/access/heap/heapam_handler.c |  4 +-
 src/backend/executor/nodeModifyTable.c   |  4 +-
 src/include/executor/tuptable.h          | 57 +++++++++++++++++-------
 3 files changed, 45 insertions(+), 20 deletions(-)

diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 9e690074e94..2bddf562c2f 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -330,7 +330,7 @@ heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid,
 
 		Assert(wait);
 
-		evalSlot = LAZY_TTS_EVAL(lockedSlot);
+		evalSlot = LazyTupleTableSlotEval(lockedSlot);
 		result = heapam_tuple_lock_internal(relation, tid, snapshot,
 											evalSlot, cid, LockTupleExclusive,
 											LockWaitBlock,
@@ -403,7 +403,7 @@ heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
 
 		Assert(wait);
 
-		evalSlot = LAZY_TTS_EVAL(lockedSlot);
+		evalSlot = LazyTupleTableSlotEval(lockedSlot);
 		result = heapam_tuple_lock_internal(relation, otid, snapshot,
 											evalSlot, cid, *lockmode,
 											LockWaitBlock,
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index e3503756818..2f6c97a8404 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1365,7 +1365,7 @@ ExecDeleteAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
 
 	if (lockUpdated)
 	{
-		MAKE_LAZY_TTS(&lazyEPQSlot, GetEPQSlot, &slotArg);
+		MakeLazyTupleTableSlotWithCallback(&lazyEPQSlot, GetEPQSlot, &slotArg);
 		lazyEPQSlotPtr = &lazyEPQSlot;
 	}
 	else
@@ -2101,7 +2101,7 @@ lreplace:
 	 */
 	if (lockUpdated)
 	{
-		MAKE_LAZY_TTS(&lazyEPQSlot, GetEPQSlot, &slotArg);
+		MakeLazyTupleTableSlotWithCallback(&lazyEPQSlot, GetEPQSlot, &slotArg);
 		lazyEPQSlotPtr = &lazyEPQSlot;
 	}
 	else
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index c61734a15d4..30729f7a076 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -307,9 +307,11 @@ typedef struct MinimalTupleTableSlot
  * likely will reain undemanded.  Preallocating such slot would be a waste of
  * resources in the  majority of cases.  Lazy slot is aimed to resolve this
  * problem.  It is basically a promise to allocate the slot once it's needed.
- * Once callee needs the slot, it could get it using LAZY_TTS_EVAL(lazySlot)
- * macro.
+ * Once callee needs the slot, it could get it using
+ * LazyTupleTableSlotEval(lazySlot) function.
  */
+typedef TupleTableSlot *(*LazyTupleTableSlotCallback) (void *arg);
+
 typedef struct
 {
 	TupleTableSlot *slot;		/* cached slot or NULL if not yet allocated */
@@ -318,25 +320,48 @@ typedef struct
 } LazyTupleTableSlot;
 
 /*
- * A constructor for the lazy slot.
+ * Initialize LazyTupleTableSlot with callback and argument.
+ */
+static inline void
+MakeLazyTupleTableSlotWithCallback(LazyTupleTableSlot *lazySlot,
+								   LazyTupleTableSlotCallback callback,
+								   void *arg)
+{
+	lazySlot->slot = NULL;
+	lazySlot->getSlot = callback;
+	lazySlot->getSlotArg = arg;
+}
+
+/*
+ * Initialize LazyTupleTableSlot with existing slot.
  */
-#define MAKE_LAZY_TTS(lazySlot, callback, arg) \
-	do { \
-		(lazySlot)->slot = NULL; \
-		(lazySlot)->getSlot = callback; \
-		(lazySlot)->getSlotArg = arg; \
-	} while (false)
+static inline void
+MakeLazyTupleTableSlotWithSlot(LazyTupleTableSlot *lazySlot,
+							   TupleTableSlot *slot)
+{
+	lazySlot->slot = slot;
+	lazySlot->getSlot = NULL;
+	lazySlot->getSlotArg = NULL;
+}
 
 /*
- * Macro for lazy slot evaluation.  NULL lazy slot evaluates to NULL slot.
+ * Function for lazy slot evaluation.  NULL lazy slot evaluates to NULL slot.
  * Cached version is used if present.  Use the callback otherwise.
  */
-#define LAZY_TTS_EVAL(lazySlot) \
-	((lazySlot) ? \
-		((lazySlot)->slot ? \
-			(lazySlot)->slot : \
-			((lazySlot)->slot = (lazySlot)->getSlot((lazySlot)->getSlotArg))) : \
-		NULL)
+static inline TupleTableSlot *
+LazyTupleTableSlotEval(LazyTupleTableSlot *lazySlot)
+{
+	if (!lazySlot)
+		return NULL;
+
+	if (!lazySlot->slot)
+	{
+		Assert(lazySlot->getSlot);
+		lazySlot->slot = lazySlot->getSlot(lazySlot->getSlotArg);
+	}
+	Assert(lazySlot->slot);
+	return lazySlot->slot;
+}
 
 /* in executor/execTuples.c */
 extern TupleTableSlot *MakeTupleTableSlot(TupleDesc tupleDesc,
-- 
2.37.1 (Apple Git-137.1)