From 7d0114a57244f82f61e0db43a5a58a524a666f52 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Thu, 18 Sep 2025 15:22:08 +0800
Subject: [PATCH v2 2/2] Avoid memory alloc and free in TidScan

When TidScan needs to rebuild TidList, most of time the rebuilt
TidList may have the same length as the existing TidList. So that
we can reuse memory of existing TidList, this way we avoid freeing
memory and re-allocate memory.

Author: Chao Li <lic@highgo.go>
---
 src/backend/executor/nodeTidscan.c | 53 +++++++++++++++++-------------
 src/include/nodes/execnodes.h      |  1 +
 2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index b40a31e0d2f..7e9b8f9743b 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -157,8 +157,17 @@ TidListEval(TidScanState *tidstate)
 	 * ScalarArrayOpExprs, we may have to enlarge the array.
 	 */
 	numAllocTids = list_length(tidstate->tss_tidexprs);
-	tidList = (ItemPointerData *)
-		palloc(numAllocTids * sizeof(ItemPointerData));
+	if (numAllocTids > tidstate->tss_MaxNumTids)
+	{
+		if (tidstate->tss_TidList != NULL)
+			tidstate->tss_TidList = (ItemPointerData *)
+				repalloc(tidstate->tss_TidList, numAllocTids * sizeof(ItemPointerData));
+		else
+			tidstate->tss_TidList = (ItemPointerData *)
+				palloc(numAllocTids * sizeof(ItemPointerData));
+		tidstate->tss_MaxNumTids = numAllocTids;
+	}
+	tidList = tidstate->tss_TidList;
 	numTids = 0;
 
 	foreach(l, tidstate->tss_tidexprs)
@@ -186,12 +195,13 @@ TidListEval(TidScanState *tidstate)
 			if (!table_tuple_tid_valid(scan, itemptr))
 				continue;
 
-			if (numTids >= numAllocTids)
+			if (numTids >= tidstate->tss_MaxNumTids)
 			{
-				numAllocTids *= 2;
-				tidList = (ItemPointerData *)
-					repalloc(tidList,
-							 numAllocTids * sizeof(ItemPointerData));
+				tidstate->tss_MaxNumTids <<= 1;
+				tidstate->tss_TidList = (ItemPointerData *)
+					repalloc(tidstate->tss_TidList,
+							 tidstate->tss_MaxNumTids * sizeof(ItemPointerData));
+				tidList = tidstate->tss_TidList;
 			}
 			tidList[numTids++] = *itemptr;
 		}
@@ -211,12 +221,12 @@ TidListEval(TidScanState *tidstate)
 				continue;
 			itemarray = DatumGetArrayTypeP(arraydatum);
 			deconstruct_array_builtin(itemarray, TIDOID, &ipdatums, &ipnulls, &ndatums);
-			if (numTids + ndatums > numAllocTids)
+			if (numTids + ndatums >= tidstate->tss_MaxNumTids)
 			{
-				numAllocTids = numTids + ndatums;
-				tidList = (ItemPointerData *)
-					repalloc(tidList,
-							 numAllocTids * sizeof(ItemPointerData));
+				tidstate->tss_MaxNumTids = numTids + ndatums;
+				tidstate->tss_TidList = (ItemPointerData *)
+					repalloc(tidstate->tss_TidList, tidstate->tss_MaxNumTids * sizeof(ItemPointerData));
+				tidList = tidstate->tss_TidList;
 			}
 			for (i = 0; i < ndatums; i++)
 			{
@@ -242,12 +252,12 @@ TidListEval(TidScanState *tidstate)
 							  RelationGetRelid(tidstate->ss.ss_currentRelation),
 							  &cursor_tid))
 			{
-				if (numTids >= numAllocTids)
+				if (numTids >= tidstate->tss_MaxNumTids)
 				{
-					numAllocTids *= 2;
+					tidstate->tss_MaxNumTids <<= 1;
 					tidList = (ItemPointerData *)
 						repalloc(tidList,
-								 numAllocTids * sizeof(ItemPointerData));
+								 tidstate->tss_MaxNumTids * sizeof(ItemPointerData));
 				}
 				tidList[numTids++] = cursor_tid;
 			}
@@ -271,7 +281,6 @@ TidListEval(TidScanState *tidstate)
 						  itemptr_comparator);
 	}
 
-	tidstate->tss_TidList = tidList;
 	tidstate->tss_NumTids = numTids;
 	tidstate->tss_TidPtr = -1;
 }
@@ -333,7 +342,7 @@ TidNext(TidScanState *node)
 	/*
 	 * First time through, compute the list of TIDs to be visited
 	 */
-	if (node->tss_TidList == NULL)
+	if (node->tss_NumTids < 0)
 		TidListEval(node);
 
 	scan = node->ss.ss_currentScanDesc;
@@ -408,7 +417,7 @@ TidRecheck(TidScanState *node, TupleTableSlot *slot)
 	if (node->tss_isCurrentOf)
 		return true;
 
-	if (node->tss_TidList == NULL)
+	if (node->tss_NumTids == 0)
 		TidListEval(node);
 
 	/*
@@ -464,10 +473,8 @@ ExecReScanTidScan(TidScanState *node)
 	if (bms_overlap(node->ss.ps.chgParam,
 					 ((TidScan *) node->ss.ps.plan)->tidparamids))
 	{
-		if (node->tss_TidList)
-			pfree(node->tss_TidList);
-		node->tss_TidList = NULL;
-		node->tss_NumTids = 0;
+		/* indicate that the TidList must be recalculated */
+		node->tss_NumTids = -1;
 	}
 
 	node->tss_TidPtr = -1;
@@ -529,7 +536,7 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	 * mark tid list as not computed yet
 	 */
 	tidstate->tss_TidList = NULL;
-	tidstate->tss_NumTids = 0;
+	tidstate->tss_NumTids = -1;
 	tidstate->tss_TidPtr = -1;
 
 	/*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a5abe176aef..db3f62cf3fe 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1913,6 +1913,7 @@ typedef struct TidScanState
 	List	   *tss_tidexprs;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
+	int			tss_MaxNumTids;	/* allocated size of TidList */
 	int			tss_TidPtr;
 	ItemPointerData *tss_TidList;
 } TidScanState;
-- 
2.39.5 (Apple Git-154)

