From a67697f00b6ea05984544211c8a2d9cfa2fa1cb5 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 2 Mar 2026 03:55:56 +1300
Subject: [PATCH v2 07/19] Use stack buffer in some executor code.

Mechanical changes to the allocation of memory used for:
* string copies
* datums, nulls, Oids, keys and similar values linked to the
  number of attributes

These are all non-escaping values that are not used in recursive
processes.
---
 src/backend/executor/execPartition.c |  7 +++++--
 src/backend/executor/execTuples.c    | 11 +++++++----
 src/backend/executor/nodeAgg.c       | 13 +++++++++----
 src/backend/executor/nodeHashjoin.c  | 14 ++++++++------
 src/backend/executor/nodeMemoize.c   |  7 +++++--
 src/backend/executor/nodeWindowAgg.c |  7 +++++--
 src/backend/executor/spi.c           | 11 +++++++----
 7 files changed, 46 insertions(+), 24 deletions(-)

diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index bab294f5e91..6cc6d6ea510 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -33,6 +33,7 @@
 #include "utils/partcache.h"
 #include "utils/rls.h"
 #include "utils/ruleutils.h"
+#include "utils/stack_buffer.h"
 
 
 /*-----------------------
@@ -2518,6 +2519,8 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate,
 	int			newidx;
 	bool		fix_subplan_map = false;
 
+	DECLARE_STACK_BUFFER();
+
 	Assert(prunestate->do_exec_prune);
 	Assert(parent_plan != NULL);
 	estate = parent_plan->state;
@@ -2535,7 +2538,7 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate,
 		 * indexes to new ones.  For convenience of initialization, we use
 		 * 1-based indexes in this array and leave pruned items as 0.
 		 */
-		new_subplan_indexes = palloc0_array(int, n_total_subplans);
+		new_subplan_indexes = stack_buffer_alloc0_array(int, n_total_subplans);
 		newidx = 1;
 		i = -1;
 		while ((i = bms_next_member(initially_valid_subplans, i)) >= 0)
@@ -2645,7 +2648,7 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate,
 		bms_free(prunestate->other_subplans);
 		prunestate->other_subplans = new_other_subplans;
 
-		pfree(new_subplan_indexes);
+		stack_buffer_free(new_subplan_indexes);
 	}
 }
 
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index b768eae9e53..d105123e3e1 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -68,6 +68,7 @@
 #include "utils/builtins.h"
 #include "utils/expandeddatum.h"
 #include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
 #include "utils/typcache.h"
 
 static TupleDesc ExecTypeFromTLInternal(List *targetList,
@@ -2330,8 +2331,10 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
 	int			i;
 	HeapTuple	tuple;
 
-	dvalues = (Datum *) palloc(natts * sizeof(Datum));
-	nulls = (bool *) palloc(natts * sizeof(bool));
+	DECLARE_STACK_BUFFER();
+
+	dvalues = stack_buffer_alloc_array(Datum, natts);
+	nulls = stack_buffer_alloc_array(bool, natts);
 
 	/*
 	 * Call the "in" function for each non-dropped attribute, even for nulls,
@@ -2368,8 +2371,8 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
 	 * Release locally palloc'd space.  XXX would probably be good to pfree
 	 * values of pass-by-reference datums, as well.
 	 */
-	pfree(dvalues);
-	pfree(nulls);
+	stack_buffer_free(dvalues);
+	stack_buffer_free(nulls);
 
 	return tuple;
 }
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 7d487a165fa..ffc198f526d 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -273,6 +273,7 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/memutils_memorychunk.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 #include "utils/tuplesort.h"
 
@@ -4347,10 +4348,12 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 	{
 		Oid		   *ops;
 
+		DECLARE_STACK_BUFFER();
+
 		Assert(numArguments > 0);
 		Assert(list_length(aggref->aggdistinct) == numDistinctCols);
 
-		ops = palloc(numDistinctCols * sizeof(Oid));
+		ops = stack_buffer_alloc_array(Oid, numDistinctCols);
 
 		i = 0;
 		foreach(lc, aggref->aggdistinct)
@@ -4367,7 +4370,7 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 									   ops,
 									   pertrans->sortCollations,
 									   &aggstate->ss.ps);
-		pfree(ops);
+		stack_buffer_free(ops);
 	}
 
 	pertrans->sortstates = palloc0_array(Tuplesortstate *, numGroupingSets);
@@ -4382,11 +4385,13 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
 	char	   *strInitVal;
 	Datum		initVal;
 
+	DECLARE_STACK_BUFFER();
+
 	getTypeInputInfo(transtype, &typinput, &typioparam);
-	strInitVal = TextDatumGetCString(textInitVal);
+	strInitVal = stack_buffer_text_datum_to_cstring(textInitVal);
 	initVal = OidInputFunctionCall(typinput, strInitVal,
 								   typioparam, -1);
-	pfree(strInitVal);
+	stack_buffer_free(strInitVal);
 	return initVal;
 }
 
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 5aa8a09b265..fd7a47d8b85 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -171,6 +171,7 @@
 #include "miscadmin.h"
 #include "utils/lsyscache.h"
 #include "utils/sharedtuplestore.h"
+#include "utils/stack_buffer.h"
 #include "utils/wait_event.h"
 
 
@@ -826,6 +827,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 		ListCell   *lc;
 		int			nkeys;
 
+		DECLARE_STACK_BUFFER();
 
 		hjstate->hj_HashTupleSlot = slot;
 
@@ -840,9 +842,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 		 */
 		nkeys = list_length(node->hashoperators);
 
-		outer_hashfuncid = palloc_array(Oid, nkeys);
-		inner_hashfuncid = palloc_array(Oid, nkeys);
-		hash_strict = palloc_array(bool, nkeys);
+		outer_hashfuncid = stack_buffer_alloc_array(Oid, nkeys);
+		inner_hashfuncid = stack_buffer_alloc_array(Oid, nkeys);
+		hash_strict = stack_buffer_alloc_array(bool, nkeys);
 
 		/*
 		 * Determine the hash function for each side of the join for the given
@@ -905,9 +907,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 		}
 
 		/* no need to keep these */
-		pfree(outer_hashfuncid);
-		pfree(inner_hashfuncid);
-		pfree(hash_strict);
+		stack_buffer_free(outer_hashfuncid);
+		stack_buffer_free(inner_hashfuncid);
+		stack_buffer_free(hash_strict);
 	}
 
 	/*
diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c
index edf52efd4c3..09a5e674dd7 100644
--- a/src/backend/executor/nodeMemoize.c
+++ b/src/backend/executor/nodeMemoize.c
@@ -74,6 +74,7 @@
 #include "miscadmin.h"
 #include "utils/datum.h"
 #include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
 
 /* States of the ExecMemoize state machine */
 #define MEMO_CACHE_LOOKUP			1	/* Attempt to perform a cache lookup */
@@ -958,6 +959,8 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
 	int			nkeys;
 	Oid		   *eqfuncoids;
 
+	DECLARE_STACK_BUFFER();
+
 	/* check for unsupported flags */
 	Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
 
@@ -1006,7 +1009,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
 											 * data */
 	mstate->hashfunctions = (FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
 
-	eqfuncoids = palloc(nkeys * sizeof(Oid));
+	eqfuncoids = stack_buffer_alloc_array(Oid, nkeys);
 
 	for (i = 0; i < nkeys; i++)
 	{
@@ -1033,7 +1036,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
 												   node->param_exprs,
 												   (PlanState *) mstate);
 
-	pfree(eqfuncoids);
+	stack_buffer_free(eqfuncoids);
 	mstate->mem_used = 0;
 
 	/* Limit the total memory consumed by the cache to this */
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d9b64b0f465..ed3744f5493 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -52,6 +52,7 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/regproc.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 #include "windowapi.h"
 
@@ -3163,11 +3164,13 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
 	char	   *strInitVal;
 	Datum		initVal;
 
+	DECLARE_STACK_BUFFER();
+
 	getTypeInputInfo(transtype, &typinput, &typioparam);
-	strInitVal = TextDatumGetCString(textInitVal);
+	strInitVal = stack_buffer_text_datum_to_cstring(textInitVal);
 	initVal = OidInputFunctionCall(typinput, strInitVal,
 								   typioparam, -1);
-	pfree(strInitVal);
+	stack_buffer_free(strInitVal);
 	return initVal;
 }
 
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 3019a3b2b97..dbbb710f877 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -31,6 +31,7 @@
 #include "utils/memutils.h"
 #include "utils/rel.h"
 #include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
@@ -1113,6 +1114,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 	bool	   *n;
 	int			i;
 
+	DECLARE_STACK_BUFFER();
+
 	if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
 	{
 		SPI_result = SPI_ERROR_ARGUMENT;
@@ -1130,8 +1133,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 	SPI_result = 0;
 
 	numberOfAttributes = rel->rd_att->natts;
-	v = palloc_array(Datum, numberOfAttributes);
-	n = palloc_array(bool, numberOfAttributes);
+	v = stack_buffer_alloc_array(Datum, numberOfAttributes);
+	n = stack_buffer_alloc_array(bool, numberOfAttributes);
 
 	/* fetch old values and nulls */
 	heap_deform_tuple(tuple, rel->rd_att, v, n);
@@ -1163,8 +1166,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 		SPI_result = SPI_ERROR_NOATTRIBUTE;
 	}
 
-	pfree(v);
-	pfree(n);
+	stack_buffer_free(v);
+	stack_buffer_free(n);
 
 	MemoryContextSwitchTo(oldcxt);
 
-- 
2.53.0

