From 89a7bc6e554fce3d35c01eccdd6a1a618e6608d0 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 2 Mar 2026 04:08:31 +1300
Subject: [PATCH v2 08/19] Use stack buffer in some adt code.

---
 src/backend/utils/adt/arrayfuncs.c   |  39 ++++----
 src/backend/utils/adt/datum.c        |   7 +-
 src/backend/utils/adt/jsonfuncs.c    |  52 ++++++++---
 src/backend/utils/adt/like_support.c |  59 +++++++-----
 src/backend/utils/adt/numeric.c      |  15 ++-
 src/backend/utils/adt/rowtypes.c     | 133 ++++++++++++++++-----------
 src/backend/utils/adt/ruleutils.c    |  47 ++++++----
 src/backend/utils/adt/tsvector_op.c  |  10 +-
 8 files changed, 223 insertions(+), 139 deletions(-)

diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 734e5fea45e..c2706be9276 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -35,6 +35,7 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
 #include "utils/typcache.h"
 
 
@@ -1051,6 +1052,8 @@ array_out(PG_FUNCTION_ARGS)
 	array_iter	iter;
 	ArrayMetaState *my_extra;
 
+	DECLARE_STACK_BUFFER();
+
 	/*
 	 * We arrange to look up info about element type, including its output
 	 * conversion proc, only once per series of calls, assuming the element
@@ -1112,8 +1115,8 @@ array_out(PG_FUNCTION_ARGS)
 	 * any overhead such as escaping backslashes), and detect whether each
 	 * item needs double quotes.
 	 */
-	values = (char **) palloc(nitems * sizeof(char *));
-	needquotes = (bool *) palloc(nitems * sizeof(bool));
+	values = stack_buffer_alloc_array(char *, nitems);
+	needquotes = stack_buffer_alloc_array(bool, nitems);
 	overall_length = 0;
 
 	array_iter_setup(&iter, v, typlen, typbyval, typalign);
@@ -1257,8 +1260,8 @@ array_out(PG_FUNCTION_ARGS)
 	/* Assert that we calculated the string length accurately */
 	Assert(overall_length == (p - retval + 1));
 
-	pfree(values);
-	pfree(needquotes);
+	stack_buffer_free(values);
+	stack_buffer_free(needquotes);
 
 	PG_RETURN_CSTRING(retval);
 }
@@ -3232,6 +3235,8 @@ array_map(Datum arrayd,
 	Datum	   *transform_source = exprstate->innermost_caseval;
 	bool	   *transform_source_isnull = exprstate->innermost_casenull;
 
+	DECLARE_STACK_BUFFER();
+
 	inpType = AARR_ELEMTYPE(v);
 	ndim = AARR_NDIM(v);
 	dim = AARR_DIMS(v);
@@ -3278,8 +3283,8 @@ array_map(Datum arrayd,
 	typalignby = typalign_to_alignby(typalign);
 
 	/* Allocate temporary arrays for new values */
-	values = (Datum *) palloc(nitems * sizeof(Datum));
-	nulls = (bool *) palloc(nitems * sizeof(bool));
+	values = stack_buffer_alloc_array(Datum, nitems);
+	nulls = stack_buffer_alloc_array(bool, nitems);
 
 	/* Loop over source data */
 	array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign);
@@ -3340,8 +3345,8 @@ array_map(Datum arrayd,
 	/*
 	 * Note: do not risk trying to pfree the results of the called expression
 	 */
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 
 	return PointerGetDatum(result);
 }
@@ -6424,6 +6429,8 @@ array_replace_internal(ArrayType *array,
 	bool		changed = false;
 	TypeCacheEntry *typentry;
 
+	DECLARE_STACK_BUFFER();
+
 	element_type = ARR_ELEMTYPE(array);
 	ndim = ARR_NDIM(array);
 	dim = ARR_DIMS(array);
@@ -6482,8 +6489,8 @@ array_replace_internal(ArrayType *array,
 							 collation, NULL, NULL);
 
 	/* Allocate temporary arrays for new values */
-	values = (Datum *) palloc(nitems * sizeof(Datum));
-	nulls = (bool *) palloc(nitems * sizeof(bool));
+	values = stack_buffer_alloc_array(Datum, nitems);
+	nulls = stack_buffer_alloc_array(bool, nitems);
 
 	/* Loop over source data */
 	arraydataptr = ARR_DATA_PTR(array);
@@ -6599,16 +6606,16 @@ array_replace_internal(ArrayType *array,
 	 */
 	if (!changed)
 	{
-		pfree(values);
-		pfree(nulls);
+		stack_buffer_free(values);
+		stack_buffer_free(nulls);
 		return array;
 	}
 
 	/* If all elements were removed return an empty array */
 	if (nresult == 0)
 	{
-		pfree(values);
-		pfree(nulls);
+		stack_buffer_free(values);
+		stack_buffer_free(nulls);
 		return construct_empty_array(element_type);
 	}
 
@@ -6643,8 +6650,8 @@ array_replace_internal(ArrayType *array,
 				 typlen, typbyval, typalign,
 				 false);
 
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 
 	return result;
 }
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
index 8832785540f..8b04ba8b9d7 100644
--- a/src/backend/utils/adt/datum.c
+++ b/src/backend/utils/adt/datum.c
@@ -48,6 +48,7 @@
 #include "utils/datum.h"
 #include "utils/expandeddatum.h"
 #include "utils/fmgrprotos.h"
+#include "utils/stack_buffer.h"
 
 
 /*-------------------------------------------------------------------------
@@ -464,6 +465,8 @@ datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
 	ExpandedObjectHeader *eoh = NULL;
 	int			header;
 
+	DECLARE_STACK_BUFFER();
+
 	/* Write header word. */
 	if (isnull)
 		header = -2;
@@ -496,13 +499,13 @@ datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
 			 * EOH_flatten_into expects the target address to be maxaligned,
 			 * so we can't store directly to *start_address.
 			 */
-			tmp = (char *) palloc(header);
+			tmp = (char *) stack_buffer_alloc(header);
 			EOH_flatten_into(eoh, tmp, header);
 			memcpy(*start_address, tmp, header);
 			*start_address += header;
 
 			/* be tidy. */
-			pfree(tmp);
+			stack_buffer_free(tmp);
 		}
 		else
 		{
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index d5b64d7fca5..f0bed51a274 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -37,6 +37,7 @@
 #include "utils/jsonfuncs.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
@@ -1681,7 +1682,11 @@ jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
 {
 	JsonbInState state = {0};
 	JsonbIterator *it;
-	bool	   *path_nulls = palloc0_array(bool, path_len);
+	bool	   *path_nulls;
+
+	DECLARE_STACK_BUFFER();
+
+	path_nulls = stack_buffer_alloc0_array(bool, path_len);
 
 	if (newval->type == jbvArray && newval->val.array.rawScalar)
 		*newval = newval->val.array.elems[0];
@@ -1692,7 +1697,7 @@ jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
 			JB_PATH_CREATE | JB_PATH_FILL_GAPS |
 			JB_PATH_CONSISTENT_POSITION);
 
-	pfree(path_nulls);
+	stack_buffer_free(path_nulls);
 
 	PG_RETURN_JSONB_P(JsonbValueToJsonb(state.result));
 }
@@ -2921,6 +2926,8 @@ populate_array(ArrayIOData *aio,
 	int		   *lbs;
 	int			i;
 
+	DECLARE_STACK_BUFFER();
+
 	ctx.aio = aio;
 	ctx.mcxt = mcxt;
 	ctx.acxt = CurrentMemoryContext;
@@ -2955,7 +2962,7 @@ populate_array(ArrayIOData *aio,
 
 	Assert(ctx.ndims > 0);
 
-	lbs = palloc_array(int, ctx.ndims);
+	lbs = stack_buffer_alloc_array(int, ctx.ndims);
 
 	for (i = 0; i < ctx.ndims; i++)
 		lbs[i] = 1;
@@ -2965,7 +2972,7 @@ populate_array(ArrayIOData *aio,
 
 	pfree(ctx.dims);
 	pfree(ctx.sizes);
-	pfree(lbs);
+	stack_buffer_free(lbs);
 
 	*isnull = false;
 	return result;
@@ -3125,6 +3132,9 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
 	Datum		res;
 	char	   *str = NULL;
 	const char *json = NULL;
+	bool		str_stack = false;
+
+	DECLARE_STACK_BUFFER();
 
 	if (jsv->is_json)
 	{
@@ -3149,9 +3159,8 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
 		else if (len >= 0)
 		{
 			/* create a NUL-terminated version */
-			str = palloc(len + 1);
-			memcpy(str, json, len);
-			str[len] = '\0';
+			str = stack_buffer_strndup(json, len);
+			str_stack = true;
 		}
 		else
 		{
@@ -3164,7 +3173,10 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
 		JsonbValue *jbv = jsv->val.jsonb;
 
 		if (jbv->type == jbvString && omit_quotes)
-			str = pnstrdup(jbv->val.string.val, jbv->val.string.len);
+		{
+			str = stack_buffer_strndup(jbv->val.string.val, jbv->val.string.len);
+			str_stack = true;
+		}
 		else if (typid == JSONBOID)
 		{
 			Jsonb	   *jsonb = JsonbValueToJsonb(jbv); /* directly use jsonb */
@@ -3183,9 +3195,15 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
 			str = JsonbToCString(NULL, &jsonb->root, VARSIZE(jsonb));
 		}
 		else if (jbv->type == jbvString)	/* quotes are stripped */
-			str = pnstrdup(jbv->val.string.val, jbv->val.string.len);
+		{
+			str = stack_buffer_strndup(jbv->val.string.val, jbv->val.string.len);
+			str_stack = true;
+		}
 		else if (jbv->type == jbvBool)
-			str = pstrdup(jbv->val.boolean ? "true" : "false");
+		{
+			str = stack_buffer_strdup(jbv->val.boolean ? "true" : "false");
+			str_stack = true;
+		}
 		else if (jbv->type == jbvNumeric)
 			str = DatumGetCString(DirectFunctionCall1(numeric_out,
 													  PointerGetDatum(jbv->val.numeric)));
@@ -3204,7 +3222,9 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
 	}
 
 	/* free temporary buffer */
-	if (str != json)
+	if (str_stack)
+		stack_buffer_free(str);
+	else if (str != json)
 		pfree(str);
 
 	return res;
@@ -3528,6 +3548,8 @@ populate_record(TupleDesc tupdesc,
 	int			ncolumns = tupdesc->natts;
 	int			i;
 
+	DECLARE_STACK_BUFFER();
+
 	/*
 	 * if the input json is empty, we can only skip the rest if we were passed
 	 * in a non-null record, since otherwise there may be issues with domain
@@ -3552,8 +3574,8 @@ populate_record(TupleDesc tupdesc,
 		record->ncolumns = ncolumns;
 	}
 
-	values = (Datum *) palloc(ncolumns * sizeof(Datum));
-	nulls = (bool *) palloc(ncolumns * sizeof(bool));
+	values = stack_buffer_alloc_array(Datum, ncolumns);
+	nulls = stack_buffer_alloc_array(bool, ncolumns);
 
 	if (defaultval)
 	{
@@ -3618,8 +3640,8 @@ populate_record(TupleDesc tupdesc,
 
 	res = heap_form_tuple(tupdesc, values, nulls);
 
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 
 	return res->t_data;
 }
diff --git a/src/backend/utils/adt/like_support.c b/src/backend/utils/adt/like_support.c
index 01cd6b10730..119ed842bf1 100644
--- a/src/backend/utils/adt/like_support.c
+++ b/src/backend/utils/adt/like_support.c
@@ -52,6 +52,7 @@
 #include "utils/lsyscache.h"
 #include "utils/pg_locale.h"
 #include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
 #include "utils/varlena.h"
 
 
@@ -994,12 +995,14 @@ like_fixed_prefix(Const *patt_const, Const **prefix_const,
 	int			pos,
 				match_pos;
 
+	DECLARE_STACK_BUFFER();
+
 	/* the right-hand const is type text or bytea */
 	Assert(typeid == BYTEAOID || typeid == TEXTOID);
 
 	if (typeid != BYTEAOID)
 	{
-		patt = TextDatumGetCString(patt_const->constvalue);
+		patt = stack_buffer_text_datum_to_cstring(patt_const->constvalue);
 		pattlen = strlen(patt);
 	}
 	else
@@ -1007,12 +1010,12 @@ like_fixed_prefix(Const *patt_const, Const **prefix_const,
 		bytea	   *bstr = DatumGetByteaPP(patt_const->constvalue);
 
 		pattlen = VARSIZE_ANY_EXHDR(bstr);
-		patt = (char *) palloc(pattlen);
+		patt = (char *) stack_buffer_alloc(pattlen);
 		memcpy(patt, VARDATA_ANY(bstr), pattlen);
 		Assert(bstr == DatumGetPointer(patt_const->constvalue));
 	}
 
-	match = palloc(pattlen + 1);
+	match = stack_buffer_alloc(pattlen + 1);
 	match_pos = 0;
 	for (pos = 0; pos < pattlen; pos++)
 	{
@@ -1042,8 +1045,8 @@ like_fixed_prefix(Const *patt_const, Const **prefix_const,
 	if (rest_selec != NULL)
 		*rest_selec = like_selectivity(&patt[pos], pattlen - pos, false);
 
-	pfree(patt);
-	pfree(match);
+	stack_buffer_free(patt);
+	stack_buffer_free(match);
 
 	/* in LIKE, an empty pattern is an exact match! */
 	if (pos == pattlen)
@@ -1075,6 +1078,8 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
 	int			match_mblen;
 	pg_locale_t locale = 0;
 
+	DECLARE_STACK_BUFFER();
+
 	/* the right-hand const is type text or bytea */
 	Assert(typeid == BYTEAOID || typeid == TEXTOID);
 
@@ -1097,10 +1102,10 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
 
 	locale = pg_newlocale_from_collation(collation);
 
-	wpatt = palloc((nbytes + 1) * sizeof(pg_wchar));
+	wpatt = stack_buffer_alloc_array(pg_wchar, nbytes + 1);
 	wpattlen = pg_mb2wchar_with_len(VARDATA_ANY(val), wpatt, nbytes);
 
-	wmatch = palloc((nbytes + 1) * sizeof(pg_wchar));
+	wmatch = stack_buffer_alloc_array(pg_wchar, nbytes + 1);
 	for (wpos = 0; wpos < wpattlen; wpos++)
 	{
 		/* % and _ are wildcard characters in LIKE */
@@ -1128,13 +1133,13 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
 
 	wmatch[wmatch_pos] = '\0';
 
-	match = palloc(pg_database_encoding_max_length() * wmatch_pos + 1);
+	match = stack_buffer_alloc(pg_database_encoding_max_length() * wmatch_pos + 1);
 	match_mblen = pg_wchar2mb_with_len(wmatch, match, wmatch_pos);
 	match[match_mblen] = '\0';
-	pfree(wmatch);
+	stack_buffer_free(wmatch);
 
 	*prefix_const = string_to_const(match, TEXTOID);
-	pfree(match);
+	stack_buffer_free(match);
 
 	if (rest_selec != NULL)
 	{
@@ -1142,14 +1147,14 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
 		char	   *rest;
 		int			rest_mblen;
 
-		rest = palloc(pg_database_encoding_max_length() * wrestlen + 1);
+		rest = stack_buffer_alloc(pg_database_encoding_max_length() * wrestlen + 1);
 		rest_mblen = pg_wchar2mb_with_len(&wpatt[wmatch_pos], rest, wrestlen);
 
 		*rest_selec = like_selectivity(rest, rest_mblen, true);
-		pfree(rest);
+		stack_buffer_free(rest);
 	}
 
-	pfree(wpatt);
+	stack_buffer_free(wpatt);
 
 	/* in LIKE, an empty pattern is an exact match! */
 	if (wpos == wpattlen)
@@ -1169,6 +1174,8 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, Oid collation,
 	char	   *prefix;
 	bool		exact;
 
+	DECLARE_STACK_BUFFER();
+
 	/*
 	 * Should be unnecessary, there are no bytea regex operators defined. As
 	 * such, it should be noted that the rest of this function has *not* been
@@ -1190,12 +1197,12 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, Oid collation,
 
 		if (rest_selec != NULL)
 		{
-			char	   *patt = TextDatumGetCString(patt_const->constvalue);
+			char	   *patt = stack_buffer_text_datum_to_cstring(patt_const->constvalue);
 
 			*rest_selec = regex_selectivity(patt, strlen(patt),
 											case_insensitive,
 											0);
-			pfree(patt);
+			stack_buffer_free(patt);
 		}
 
 		return Pattern_Prefix_None;
@@ -1212,12 +1219,12 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, Oid collation,
 		}
 		else
 		{
-			char	   *patt = TextDatumGetCString(patt_const->constvalue);
+			char	   *patt = stack_buffer_text_datum_to_cstring(patt_const->constvalue);
 
 			*rest_selec = regex_selectivity(patt, strlen(patt),
 											case_insensitive,
 											strlen(prefix));
-			pfree(patt);
+			stack_buffer_free(patt);
 		}
 	}
 
@@ -1619,6 +1626,8 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
 	char	   *cmptxt = NULL;
 	mbcharacter_incrementer charinc;
 
+	DECLARE_STACK_BUFFER();
+
 	/*
 	 * Get a modifiable copy of the prefix string in C-string format, and set
 	 * up the string we will compare to as a Datum.  In C locale this can just
@@ -1630,7 +1639,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
 		bytea	   *bstr = DatumGetByteaPP(str_const->constvalue);
 
 		len = VARSIZE_ANY_EXHDR(bstr);
-		workstr = (char *) palloc(len);
+		workstr = (char *) stack_buffer_alloc(len);
 		memcpy(workstr, VARDATA_ANY(bstr), len);
 		Assert(bstr == DatumGetPointer(str_const->constvalue));
 		cmpstr = str_const->constvalue;
@@ -1641,7 +1650,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
 			workstr = DatumGetCString(DirectFunctionCall1(nameout,
 														  str_const->constvalue));
 		else
-			workstr = TextDatumGetCString(str_const->constvalue);
+			workstr = stack_buffer_text_datum_to_cstring(str_const->constvalue);
 		len = strlen(workstr);
 		if (len == 0 || pg_newlocale_from_collation(collation)->collate_is_c)
 			cmpstr = str_const->constvalue;
@@ -1669,7 +1678,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
 			/* And build the string to compare to */
 			if (datatype == NAMEOID)
 			{
-				cmptxt = palloc(len + 2);
+				cmptxt = stack_buffer_alloc(len + 2);
 				memcpy(cmptxt, workstr, len);
 				cmptxt[len] = suffixchar;
 				cmptxt[len + 1] = '\0';
@@ -1677,7 +1686,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
 			}
 			else
 			{
-				cmptxt = palloc(VARHDRSZ + len + 1);
+				cmptxt = stack_buffer_alloc(VARHDRSZ + len + 1);
 				SET_VARSIZE(cmptxt, VARHDRSZ + len + 1);
 				memcpy(VARDATA(cmptxt), workstr, len);
 				*(VARDATA(cmptxt) + len) = suffixchar;
@@ -1729,8 +1738,8 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
 			{
 				/* Successfully made a string larger than cmpstr */
 				if (cmptxt)
-					pfree(cmptxt);
-				pfree(workstr);
+					stack_buffer_free(cmptxt);
+				stack_buffer_free(workstr);
 				return workstr_const;
 			}
 
@@ -1749,8 +1758,8 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
 
 	/* Failed... */
 	if (cmptxt)
-		pfree(cmptxt);
-	pfree(workstr);
+		stack_buffer_free(cmptxt);
+	stack_buffer_free(workstr);
 
 	return NULL;
 }
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index d25b8ad505d..771ea2ba6b3 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -43,6 +43,7 @@
 #include "utils/numeric.h"
 #include "utils/pg_lsn.h"
 #include "utils/sortsupport.h"
+#include "utils/stack_buffer.h"
 
 /* ----------
  * Uncomment the following to enable compilation of dump_numeric()
@@ -6755,6 +6756,8 @@ set_var_from_str(const char *str, const char *cp,
 	int			offset;
 	NumericDigit *digits;
 
+	DECLARE_STACK_BUFFER();
+
 	/*
 	 * We first parse the string to extract decimal digits and determine the
 	 * correct decimal weight.  Then convert to NBASE representation.
@@ -6781,7 +6784,7 @@ set_var_from_str(const char *str, const char *cp,
 	if (!isdigit((unsigned char) *cp))
 		goto invalid_syntax;
 
-	decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);
+	decdigits = (unsigned char *) stack_buffer_alloc(strlen(cp) + DEC_DIGITS * 2);
 
 	/* leading padding for digit alignment later */
 	memset(decdigits, 0, DEC_DIGITS);
@@ -6915,7 +6918,7 @@ set_var_from_str(const char *str, const char *cp,
 		i += DEC_DIGITS;
 	}
 
-	pfree(decdigits);
+	stack_buffer_free(decdigits);
 
 	/* Strip any leading/trailing zeroes, and normalize weight if zero */
 	strip_var(dest);
@@ -8901,6 +8904,8 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
 	NumericDigit *res_digits;
 	int			i;
 
+	DECLARE_STACK_BUFFER();
+
 	/*
 	 * First of all division by zero check; we must not be handed an
 	 * unnormalized divisor.
@@ -9050,8 +9055,8 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
 	 * zero and not counted in div_ndigitpairs, so that the main loop below
 	 * can safely read and write the (qi+1)'th digit in the approximate case.
 	 */
-	dividend = (int64 *) palloc((div_ndigitpairs + 1) * sizeof(int64) +
-								var2ndigitpairs * sizeof(int32));
+	dividend = (int64 *) stack_buffer_alloc((div_ndigitpairs + 1) * sizeof(int64) +
+											var2ndigitpairs * sizeof(int32));
 	divisor = (int32 *) (dividend + div_ndigitpairs + 1);
 
 	/* load var1 into dividend[0 .. var1ndigitpairs-1], zeroing the rest */
@@ -9389,7 +9394,7 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
 	}
 	Assert(carry == 0);
 
-	pfree(dividend);
+	stack_buffer_free(dividend);
 
 	/*
 	 * Finally, round or truncate the result to the requested precision.
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index e4eb7111ee7..bcceb998da7 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -25,6 +25,7 @@
 #include "utils/builtins.h"
 #include "utils/datum.h"
 #include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
 #include "utils/typcache.h"
 
 
@@ -89,6 +90,8 @@ record_in(PG_FUNCTION_ARGS)
 	bool	   *nulls;
 	StringInfoData buf;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/*
@@ -140,8 +143,8 @@ record_in(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	values = palloc_array(Datum, ncolumns);
-	nulls = palloc_array(bool, ncolumns);
+	values = stack_buffer_alloc_array(Datum, ncolumns);
+	nulls = stack_buffer_alloc_array(bool, ncolumns);
 
 	/*
 	 * Scan the string.  We use "buf" to accumulate the de-quoted data for
@@ -310,8 +313,8 @@ record_in(PG_FUNCTION_ARGS)
 
 	heap_freetuple(tuple);
 	pfree(buf.data);
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 	ReleaseTupleDesc(tupdesc);
 
 	PG_RETURN_HEAPTUPLEHEADER(result);
@@ -341,6 +344,8 @@ record_out(PG_FUNCTION_ARGS)
 	bool	   *nulls;
 	StringInfoData buf;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/* Extract type info from the tuple itself */
@@ -383,8 +388,8 @@ record_out(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	values = palloc_array(Datum, ncolumns);
-	nulls = palloc_array(bool, ncolumns);
+	values = stack_buffer_alloc_array(Datum, ncolumns);
+	nulls = stack_buffer_alloc_array(bool, ncolumns);
 
 	/* Break down the tuple into fields */
 	heap_deform_tuple(&tuple, tupdesc, values, nulls);
@@ -466,8 +471,8 @@ record_out(PG_FUNCTION_ARGS)
 
 	appendStringInfoChar(&buf, ')');
 
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 	ReleaseTupleDesc(tupdesc);
 
 	PG_RETURN_CSTRING(buf.data);
@@ -493,6 +498,8 @@ record_recv(PG_FUNCTION_ARGS)
 	Datum	   *values;
 	bool	   *nulls;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/*
@@ -539,8 +546,8 @@ record_recv(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	values = palloc_array(Datum, ncolumns);
-	nulls = palloc_array(bool, ncolumns);
+	values = stack_buffer_alloc_array(Datum, ncolumns);
+	nulls = stack_buffer_alloc_array(bool, ncolumns);
 
 	/* Fetch number of columns user thinks it has */
 	usercols = pq_getmsgint(buf, 4);
@@ -673,8 +680,8 @@ record_recv(PG_FUNCTION_ARGS)
 	memcpy(result, tuple->t_data, tuple->t_len);
 
 	heap_freetuple(tuple);
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 	ReleaseTupleDesc(tupdesc);
 
 	PG_RETURN_HEAPTUPLEHEADER(result);
@@ -699,6 +706,8 @@ record_send(PG_FUNCTION_ARGS)
 	bool	   *nulls;
 	StringInfoData buf;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/* Extract type info from the tuple itself */
@@ -741,8 +750,8 @@ record_send(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	values = palloc_array(Datum, ncolumns);
-	nulls = palloc_array(bool, ncolumns);
+	values = stack_buffer_alloc_array(Datum, ncolumns);
+	nulls = stack_buffer_alloc_array(bool, ncolumns);
 
 	/* Break down the tuple into fields */
 	heap_deform_tuple(&tuple, tupdesc, values, nulls);
@@ -800,8 +809,8 @@ record_send(PG_FUNCTION_ARGS)
 					 VARSIZE(outputbytes) - VARHDRSZ);
 	}
 
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 	ReleaseTupleDesc(tupdesc);
 
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
@@ -845,6 +854,8 @@ record_cmp(FunctionCallInfo fcinfo)
 	int			i2;
 	int			j;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/* Extract type info from the tuples */
@@ -901,11 +912,11 @@ record_cmp(FunctionCallInfo fcinfo)
 	}
 
 	/* Break down the tuples into fields */
-	values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
-	nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+	values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+	nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
 	heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
-	values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
-	nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+	values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+	nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
 	heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 
 	/*
@@ -1040,10 +1051,10 @@ record_cmp(FunctionCallInfo fcinfo)
 					 errmsg("cannot compare record types with different numbers of columns")));
 	}
 
-	pfree(values1);
-	pfree(nulls1);
-	pfree(values2);
-	pfree(nulls2);
+	stack_buffer_free(values1);
+	stack_buffer_free(nulls1);
+	stack_buffer_free(values2);
+	stack_buffer_free(nulls2);
 	ReleaseTupleDesc(tupdesc1);
 	ReleaseTupleDesc(tupdesc2);
 
@@ -1089,6 +1100,8 @@ record_eq(PG_FUNCTION_ARGS)
 	int			i2;
 	int			j;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/* Extract type info from the tuples */
@@ -1145,11 +1158,11 @@ record_eq(PG_FUNCTION_ARGS)
 	}
 
 	/* Break down the tuples into fields */
-	values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
-	nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+	values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+	nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
 	heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
-	values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
-	nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+	values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+	nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
 	heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 
 	/*
@@ -1265,10 +1278,10 @@ record_eq(PG_FUNCTION_ARGS)
 					 errmsg("cannot compare record types with different numbers of columns")));
 	}
 
-	pfree(values1);
-	pfree(nulls1);
-	pfree(values2);
-	pfree(nulls2);
+	stack_buffer_free(values1);
+	stack_buffer_free(nulls1);
+	stack_buffer_free(values2);
+	stack_buffer_free(nulls2);
 	ReleaseTupleDesc(tupdesc1);
 	ReleaseTupleDesc(tupdesc2);
 
@@ -1371,6 +1384,8 @@ record_image_cmp(FunctionCallInfo fcinfo)
 	int			i2;
 	int			j;
 
+	DECLARE_STACK_BUFFER();
+
 	/* Extract type info from the tuples */
 	tupType1 = HeapTupleHeaderGetTypeId(record1);
 	tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
@@ -1425,11 +1440,11 @@ record_image_cmp(FunctionCallInfo fcinfo)
 	}
 
 	/* Break down the tuples into fields */
-	values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
-	nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+	values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+	nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
 	heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
-	values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
-	nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+	values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+	nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
 	heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 
 	/*
@@ -1568,10 +1583,10 @@ record_image_cmp(FunctionCallInfo fcinfo)
 					 errmsg("cannot compare record types with different numbers of columns")));
 	}
 
-	pfree(values1);
-	pfree(nulls1);
-	pfree(values2);
-	pfree(nulls2);
+	stack_buffer_free(values1);
+	stack_buffer_free(nulls1);
+	stack_buffer_free(values2);
+	stack_buffer_free(nulls2);
 	ReleaseTupleDesc(tupdesc1);
 	ReleaseTupleDesc(tupdesc2);
 
@@ -1617,6 +1632,8 @@ record_image_eq(PG_FUNCTION_ARGS)
 	int			i2;
 	int			j;
 
+	DECLARE_STACK_BUFFER();
+
 	/* Extract type info from the tuples */
 	tupType1 = HeapTupleHeaderGetTypeId(record1);
 	tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
@@ -1671,11 +1688,11 @@ record_image_eq(PG_FUNCTION_ARGS)
 	}
 
 	/* Break down the tuples into fields */
-	values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
-	nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+	values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+	nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
 	heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
-	values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
-	nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+	values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+	nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
 	heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 
 	/*
@@ -1753,10 +1770,10 @@ record_image_eq(PG_FUNCTION_ARGS)
 					 errmsg("cannot compare record types with different numbers of columns")));
 	}
 
-	pfree(values1);
-	pfree(nulls1);
-	pfree(values2);
-	pfree(nulls2);
+	stack_buffer_free(values1);
+	stack_buffer_free(nulls1);
+	stack_buffer_free(values2);
+	stack_buffer_free(nulls2);
 	ReleaseTupleDesc(tupdesc1);
 	ReleaseTupleDesc(tupdesc2);
 
@@ -1822,6 +1839,8 @@ hash_record(PG_FUNCTION_ARGS)
 	Datum	   *values;
 	bool	   *nulls;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/* Extract type info from tuple */
@@ -1863,8 +1882,8 @@ hash_record(PG_FUNCTION_ARGS)
 	}
 
 	/* Break down the tuple into fields */
-	values = palloc_array(Datum, ncolumns);
-	nulls = palloc_array(bool, ncolumns);
+	values = stack_buffer_alloc_array(Datum, ncolumns);
+	nulls = stack_buffer_alloc_array(bool, ncolumns);
 	heap_deform_tuple(&tuple, tupdesc, values, nulls);
 
 	for (int i = 0; i < ncolumns; i++)
@@ -1918,8 +1937,8 @@ hash_record(PG_FUNCTION_ARGS)
 		result = (result << 5) - result + element_hash;
 	}
 
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 	ReleaseTupleDesc(tupdesc);
 
 	/* Avoid leaking memory when handed toasted input. */
@@ -1943,6 +1962,8 @@ hash_record_extended(PG_FUNCTION_ARGS)
 	Datum	   *values;
 	bool	   *nulls;
 
+	DECLARE_STACK_BUFFER();
+
 	check_stack_depth();		/* recurses for record-type columns */
 
 	/* Extract type info from tuple */
@@ -1984,8 +2005,8 @@ hash_record_extended(PG_FUNCTION_ARGS)
 	}
 
 	/* Break down the tuple into fields */
-	values = palloc_array(Datum, ncolumns);
-	nulls = palloc_array(bool, ncolumns);
+	values = stack_buffer_alloc_array(Datum, ncolumns);
+	nulls = stack_buffer_alloc_array(bool, ncolumns);
 	heap_deform_tuple(&tuple, tupdesc, values, nulls);
 
 	for (int i = 0; i < ncolumns; i++)
@@ -2041,8 +2062,8 @@ hash_record_extended(PG_FUNCTION_ARGS)
 		result = (result << 5) - result + element_hash;
 	}
 
-	pfree(values);
-	pfree(nulls);
+	stack_buffer_free(values);
+	stack_buffer_free(nulls);
 	ReleaseTupleDesc(tupdesc);
 
 	/* Avoid leaking memory when handed toasted input. */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 6298a37f88e..8e87d37058d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -67,6 +67,7 @@
 #include "utils/rel.h"
 #include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 #include "utils/varlena.h"
@@ -1298,6 +1299,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	char	   *str;
 	char	   *sep;
 
+	DECLARE_STACK_BUFFER();
+
 	/*
 	 * Fetch the pg_index tuple by the Oid of the index
 	 */
@@ -1358,9 +1361,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 
 		exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
 											Anum_pg_index_indexprs);
-		exprsString = TextDatumGetCString(exprsDatum);
+		exprsString = stack_buffer_text_datum_to_cstring(exprsDatum);
 		indexprs = (List *) stringToNode(exprsString);
-		pfree(exprsString);
+		stack_buffer_free(exprsString);
 	}
 	else
 		indexprs = NIL;
@@ -1554,9 +1557,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 			/* Convert text string to node tree */
 			predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
 											   Anum_pg_index_indpred);
-			predString = TextDatumGetCString(predDatum);
+			predString = stack_buffer_text_datum_to_cstring(predDatum);
 			node = (Node *) stringToNode(predString);
-			pfree(predString);
+			stack_buffer_free(predString);
 
 			/* Deparse */
 			str = deparse_expression_pretty(node, context, false, false,
@@ -1671,6 +1674,8 @@ pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
 	bool		has_exprs;
 	int			ncolumns;
 
+	DECLARE_STACK_BUFFER();
+
 	statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
 
 	if (!HeapTupleIsValid(statexttup))
@@ -1697,9 +1702,9 @@ pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
 
 		exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
 											Anum_pg_statistic_ext_stxexprs);
-		exprsString = TextDatumGetCString(exprsDatum);
+		exprsString = stack_buffer_text_datum_to_cstring(exprsDatum);
 		exprs = (List *) stringToNode(exprsString);
-		pfree(exprsString);
+		stack_buffer_free(exprsString);
 	}
 	else
 		exprs = NIL;
@@ -1848,6 +1853,8 @@ pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
 	char	   *tmp;
 	ArrayBuildState *astate = NULL;
 
+	DECLARE_STACK_BUFFER();
+
 	statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
 
 	if (!HeapTupleIsValid(statexttup))
@@ -1870,9 +1877,9 @@ pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
 	 */
 	datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
 								   Anum_pg_statistic_ext_stxexprs);
-	tmp = TextDatumGetCString(datum);
+	tmp = stack_buffer_text_datum_to_cstring(datum);
 	exprs = (List *) stringToNode(tmp);
-	pfree(tmp);
+	stack_buffer_free(tmp);
 
 	context = deparse_context_for(get_relation_name(statextrec->stxrelid),
 								  statextrec->stxrelid);
@@ -1950,6 +1957,8 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags,
 	char	   *str;
 	char	   *sep;
 
+	DECLARE_STACK_BUFFER();
+
 	tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(tuple))
 	{
@@ -1984,14 +1993,14 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags,
 
 		exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
 											Anum_pg_partitioned_table_partexprs);
-		exprsString = TextDatumGetCString(exprsDatum);
+		exprsString = stack_buffer_text_datum_to_cstring(exprsDatum);
 		partexprs = (List *) stringToNode(exprsString);
 
 		if (!IsA(partexprs, List))
 			elog(ERROR, "unexpected node type found in partexprs: %d",
 				 (int) nodeTag(partexprs));
 
-		pfree(exprsString);
+		stack_buffer_free(exprsString);
 	}
 	else
 		partexprs = NIL;
@@ -2717,13 +2726,15 @@ pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
 	Relation	rel = NULL;
 	char	   *str;
 
+	DECLARE_STACK_BUFFER();
+
 	/* Convert input pg_node_tree (really TEXT) object to C string */
-	exprstr = text_to_cstring(expr);
+	exprstr = stack_buffer_text_to_cstring(expr);
 
 	/* Convert expression to node tree */
 	node = (Node *) stringToNode(exprstr);
 
-	pfree(exprstr);
+	stack_buffer_free(exprstr);
 
 	/*
 	 * Throw error if the input is a querytree rather than an expression tree.
@@ -3315,6 +3326,8 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
 	ListCell   *nextargdefault = NULL;
 	int			i;
 
+	DECLARE_STACK_BUFFER();
+
 	numargs = get_func_arg_info(proctup,
 								&argtypes, &argnames, &argmodes);
 
@@ -3331,9 +3344,9 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
 		{
 			char	   *str;
 
-			str = TextDatumGetCString(proargdefaults);
+			str = stack_buffer_text_datum_to_cstring(proargdefaults);
 			argdefaults = castNode(List, stringToNode(str));
-			pfree(str);
+			stack_buffer_free(str);
 			nextargdefault = list_head(argdefaults);
 			/* nlackdefaults counts only *input* arguments lacking defaults */
 			nlackdefaults = proc->pronargs - list_length(argdefaults);
@@ -3506,6 +3519,8 @@ pg_get_function_arg_default(PG_FUNCTION_ARGS)
 	bool		isnull;
 	int			nth_default;
 
+	DECLARE_STACK_BUFFER();
+
 	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
 	if (!HeapTupleIsValid(proctup))
 		PG_RETURN_NULL();
@@ -3531,9 +3546,9 @@ pg_get_function_arg_default(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 	}
 
-	str = TextDatumGetCString(proargdefaults);
+	str = stack_buffer_text_datum_to_cstring(proargdefaults);
 	argdefaults = castNode(List, stringToNode(str));
-	pfree(str);
+	stack_buffer_free(str);
 
 	proc = (Form_pg_proc) GETSTRUCT(proctup);
 
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 71c7c7d3b3c..86c29f730ba 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -31,6 +31,7 @@
 #include "utils/builtins.h"
 #include "utils/regproc.h"
 #include "utils/rel.h"
+#include "utils/stack_buffer.h"
 
 
 typedef struct
@@ -2537,6 +2538,8 @@ ts_process_call(FuncCallContext *funcctx)
 	TSVectorStat *st;
 	StatEntry  *entry;
 
+	DECLARE_STACK_BUFFER();
+
 	st = (TSVectorStat *) funcctx->user_fctx;
 
 	entry = walkStatEntryTree(st);
@@ -2549,9 +2552,8 @@ ts_process_call(FuncCallContext *funcctx)
 		char		nentry[16];
 		HeapTuple	tuple;
 
-		values[0] = palloc(entry->lenlexeme + 1);
-		memcpy(values[0], entry->lexeme, entry->lenlexeme);
-		(values[0])[entry->lenlexeme] = '\0';
+		values[0] = stack_buffer_strdup_with_len(entry->lexeme,
+												 entry->lenlexeme);
 		sprintf(ndoc, "%d", entry->ndoc);
 		values[1] = ndoc;
 		sprintf(nentry, "%d", entry->nentry);
@@ -2560,7 +2562,7 @@ ts_process_call(FuncCallContext *funcctx)
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
 		result = HeapTupleGetDatum(tuple);
 
-		pfree(values[0]);
+		stack_buffer_free(values[0]);
 
 		/* mark entry as already visited */
 		entry->ndoc = 0;
-- 
2.53.0

