From ec6fa973b0bb6b3a422df1380ebae2432824a739 Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Sun, 28 Jun 2026 17:21:51 -0500
Subject: [PATCH v1 3/5] Change stats_fill_fcinfo_from_arg_pairs to
 NullableDatum array.

Change stats_fill_fcinfo_from_arg_pairs to NullableDatum array, and in
doing so rename to stats_fill_args_from_arg_pairs. Change all callers to
instead initialize an NullableDatum array of the proper size instead of
a full FunctionCallInfoData structure.
---
 src/backend/statistics/attribute_stats.c      | 91 ++++++++-----------
 src/backend/statistics/extended_stats_funcs.c | 11 +--
 src/backend/statistics/relation_stats.c       | 85 ++++++++---------
 src/backend/statistics/stat_utils.c           | 24 ++---
 src/include/statistics/stat_utils.h           |  6 +-
 5 files changed, 95 insertions(+), 122 deletions(-)

diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c
index a50d60223c9..e7ee0b3443f 100644
--- a/src/backend/statistics/attribute_stats.c
+++ b/src/backend/statistics/attribute_stats.c
@@ -104,7 +104,7 @@ static struct StatsArgInfo cleararginfo[] =
 	[C_NUM_ATTRIBUTE_STATS_ARGS] = {0}
 };
 
-static bool attribute_statistics_update(FunctionCallInfo fcinfo);
+static bool attribute_statistics_update(const NullableDatum *args);
 static bool attribute_statistics_update_internal(Oid reloid,
 												 const char *attname,
 												 AttrNumber attnum,
@@ -131,7 +131,7 @@ static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
  * and other statistic kinds may still be updated.
  */
 static bool
-attribute_statistics_update(FunctionCallInfo fcinfo)
+attribute_statistics_update(const NullableDatum *args)
 {
 	char	   *nspname;
 	char	   *relname;
@@ -141,11 +141,11 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 	bool		inherited;
 	Oid			locked_table = InvalidOid;
 
-	stats_check_required_arg(fcinfo->args, attarginfo, ATTRELSCHEMA_ARG);
-	stats_check_required_arg(fcinfo->args, attarginfo, ATTRELNAME_ARG);
+	stats_check_required_arg(args, attarginfo, ATTRELSCHEMA_ARG);
+	stats_check_required_arg(args, attarginfo, ATTRELNAME_ARG);
 
-	nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
-	relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
+	nspname = TextDatumGetCString(args[ATTRELSCHEMA_ARG].value);
+	relname = TextDatumGetCString(args[ATTRELNAME_ARG].value);
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -159,13 +159,13 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 									  RangeVarCallbackForStats, &locked_table);
 
 	/* user can specify either attname or attnum, but not both */
-	if (!PG_ARGISNULL(ATTNAME_ARG))
+	if (!args[ATTNAME_ARG].isnull)
 	{
-		if (!PG_ARGISNULL(ATTNUM_ARG))
+		if (!args[ATTNUM_ARG].isnull)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("cannot specify both \"%s\" and \"%s\"", "attname", "attnum")));
-		attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
+		attname = TextDatumGetCString(args[ATTNAME_ARG].value);
 		attnum = get_attnum(reloid, attname);
 		/* note that this test covers attisdropped cases too: */
 		if (attnum == InvalidAttrNumber)
@@ -174,9 +174,9 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 					 errmsg("column \"%s\" of relation \"%s\" does not exist",
 							attname, relname)));
 	}
-	else if (!PG_ARGISNULL(ATTNUM_ARG))
+	else if (!args[ATTNUM_ARG].isnull)
 	{
-		attnum = PG_GETARG_INT16(ATTNUM_ARG);
+		attnum = DatumGetInt16(args[ATTNUM_ARG].value);
 		attname = get_attname(reloid, attnum, true);
 		/* annoyingly, get_attname doesn't check attisdropped */
 		if (attname == NULL ||
@@ -201,11 +201,11 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 				 errmsg("cannot modify statistics on system column \"%s\"",
 						attname)));
 
-	stats_check_required_arg(fcinfo->args, attarginfo, INHERITED_ARG);
-	inherited = PG_GETARG_BOOL(INHERITED_ARG);
+	stats_check_required_arg(args, attarginfo, INHERITED_ARG);
+	inherited = DatumGetBool(args[INHERITED_ARG].value);
 
 	return attribute_statistics_update_internal(reloid, attname, attnum,
-												inherited, fcinfo->args);
+												inherited, args);
 }
 
 /*
@@ -698,17 +698,14 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
 Datum
 pg_restore_attribute_stats(PG_FUNCTION_ARGS)
 {
-	LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
+	NullableDatum positional_args[NUM_ATTRIBUTE_STATS_ARGS];
 	bool		result = true;
 
-	InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
-							 InvalidOid, NULL, NULL);
-
-	if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
-										  attarginfo))
+	if (!stats_fill_args_from_arg_pairs(fcinfo, positional_args,
+										attarginfo))
 		result = false;
 
-	if (!attribute_statistics_update(positional_fcinfo))
+	if (!attribute_statistics_update(positional_args))
 		result = false;
 
 	PG_RETURN_BOOL(result);
@@ -738,10 +735,11 @@ import_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited,
 							const NullableDatum *range_empty_frac,
 							const NullableDatum *range_bounds_histogram)
 {
-	LOCAL_FCINFO(newfcinfo, NUM_ATTRIBUTE_STATS_ARGS);
+	NullableDatum args[NUM_ATTRIBUTE_STATS_ARGS];
 	Oid			reloid = RelationGetRelid(rel);
 	char	   *relname = RelationGetRelationName(rel);
 	char	   *attname = get_attname(reloid, attnum, true);
+	NullableDatum unused = {.isnull = true, .value = (Datum) 0};
 
 	Assert(null_frac);
 	Assert(avg_width);
@@ -765,37 +763,28 @@ import_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited,
 				 errmsg("column %d of relation \"%s\" does not exist",
 						attnum, relname)));
 
-	InitFunctionCallInfoData(*newfcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
-							 InvalidOid, NULL, NULL);
-
-	newfcinfo->args[ATTRELSCHEMA_ARG].value =
-		CStringGetTextDatum(get_namespace_name(RelationGetNamespace(rel)));
-	newfcinfo->args[ATTRELSCHEMA_ARG].isnull = false;
-	newfcinfo->args[ATTRELNAME_ARG].value = CStringGetTextDatum(relname);
-	newfcinfo->args[ATTRELNAME_ARG].isnull = false;
-	newfcinfo->args[ATTNAME_ARG].value = CStringGetTextDatum(attname);
-	newfcinfo->args[ATTNAME_ARG].isnull = false;
-	newfcinfo->args[ATTNUM_ARG].value = Int16GetDatum(attnum);
-	newfcinfo->args[ATTNUM_ARG].isnull = false;
-	newfcinfo->args[INHERITED_ARG].value = BoolGetDatum(inherited);
-	newfcinfo->args[INHERITED_ARG].isnull = false;
-
-	newfcinfo->args[NULL_FRAC_ARG] = *null_frac;
-	newfcinfo->args[AVG_WIDTH_ARG] = *avg_width;
-	newfcinfo->args[N_DISTINCT_ARG] = *n_distinct;
-	newfcinfo->args[MOST_COMMON_VALS_ARG] = *most_common_vals;
-	newfcinfo->args[MOST_COMMON_FREQS_ARG] = *most_common_freqs;
-	newfcinfo->args[HISTOGRAM_BOUNDS_ARG] = *histogram_bounds;
-	newfcinfo->args[CORRELATION_ARG] = *correlation;
-	newfcinfo->args[MOST_COMMON_ELEMS_ARG] = *most_common_elems;
-	newfcinfo->args[MOST_COMMON_ELEM_FREQS_ARG] = *most_common_elem_freqs;
-	newfcinfo->args[ELEM_COUNT_HISTOGRAM_ARG] = *elem_count_histogram;
-	newfcinfo->args[RANGE_LENGTH_HISTOGRAM_ARG] = *range_length_histogram;
-	newfcinfo->args[RANGE_EMPTY_FRAC_ARG] = *range_empty_frac;
-	newfcinfo->args[RANGE_BOUNDS_HISTOGRAM_ARG] = *range_bounds_histogram;
+	args[ATTRELSCHEMA_ARG] = unused;
+	args[ATTRELNAME_ARG] = unused;
+	args[ATTNAME_ARG] = unused;
+	args[ATTNUM_ARG] = unused;
+	args[INHERITED_ARG] = unused;
+
+	args[NULL_FRAC_ARG] = *null_frac;
+	args[AVG_WIDTH_ARG] = *avg_width;
+	args[N_DISTINCT_ARG] = *n_distinct;
+	args[MOST_COMMON_VALS_ARG] = *most_common_vals;
+	args[MOST_COMMON_FREQS_ARG] = *most_common_freqs;
+	args[HISTOGRAM_BOUNDS_ARG] = *histogram_bounds;
+	args[CORRELATION_ARG] = *correlation;
+	args[MOST_COMMON_ELEMS_ARG] = *most_common_elems;
+	args[MOST_COMMON_ELEM_FREQS_ARG] = *most_common_elem_freqs;
+	args[ELEM_COUNT_HISTOGRAM_ARG] = *elem_count_histogram;
+	args[RANGE_LENGTH_HISTOGRAM_ARG] = *range_length_histogram;
+	args[RANGE_EMPTY_FRAC_ARG] = *range_empty_frac;
+	args[RANGE_BOUNDS_HISTOGRAM_ARG] = *range_bounds_histogram;
 
 	return attribute_statistics_update_internal(reloid, attname, attnum,
-												inherited, newfcinfo->args);
+												inherited, args);
 }
 
 /*
diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c
index b479c49c85e..96591e79c72 100644
--- a/src/backend/statistics/extended_stats_funcs.c
+++ b/src/backend/statistics/extended_stats_funcs.c
@@ -1718,22 +1718,19 @@ delete_pg_statistic_ext_data(Oid stxoid, bool inherited)
  * Restore (insert or replace) statistics for the given statistics object.
  *
  * This function accepts variadic arguments in key-value pairs, which are
- * given to stats_fill_fcinfo_from_arg_pairs to be mapped into positional
+ * given to stats_fill_args_from_arg_pairs to be mapped into positional
  * arguments.
  */
 Datum
 pg_restore_extended_stats(PG_FUNCTION_ARGS)
 {
-	LOCAL_FCINFO(positional_fcinfo, NUM_EXTENDED_STATS_ARGS);
+	NullableDatum positional_args[NUM_EXTENDED_STATS_ARGS];
 	bool		result = true;
 
-	InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_EXTENDED_STATS_ARGS,
-							 InvalidOid, NULL, NULL);
-
-	if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, extarginfo))
+	if (!stats_fill_args_from_arg_pairs(fcinfo, positional_args, extarginfo))
 		result = false;
 
-	if (!extended_statistics_update(positional_fcinfo->args))
+	if (!extended_statistics_update(positional_args))
 		result = false;
 
 	PG_RETURN_BOOL(result);
diff --git a/src/backend/statistics/relation_stats.c b/src/backend/statistics/relation_stats.c
index 6ec3f4d068d..392aeed5d25 100644
--- a/src/backend/statistics/relation_stats.c
+++ b/src/backend/statistics/relation_stats.c
@@ -59,7 +59,7 @@ static struct StatsArgInfo relarginfo[] =
 	[NUM_RELATION_STATS_ARGS] = {0}
 };
 
-static bool relation_statistics_update(FunctionCallInfo fcinfo);
+static bool relation_statistics_update(const NullableDatum *args);
 static bool relation_statistics_update_internal(Oid reloid,
 												const NullableDatum *args);
 
@@ -67,18 +67,18 @@ static bool relation_statistics_update_internal(Oid reloid,
  * Internal function for modifying statistics for a relation.
  */
 static bool
-relation_statistics_update(FunctionCallInfo fcinfo)
+relation_statistics_update(const NullableDatum *args)
 {
 	char	   *nspname;
 	char	   *relname;
 	Oid			reloid;
 	Oid			locked_table = InvalidOid;
 
-	stats_check_required_arg(fcinfo->args, relarginfo, RELSCHEMA_ARG);
-	stats_check_required_arg(fcinfo->args, relarginfo, RELNAME_ARG);
+	stats_check_required_arg(args, relarginfo, RELSCHEMA_ARG);
+	stats_check_required_arg(args, relarginfo, RELNAME_ARG);
 
-	nspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
-	relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
+	nspname = TextDatumGetCString(args[RELSCHEMA_ARG].value);
+	relname = TextDatumGetCString(args[RELNAME_ARG].value);
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -90,7 +90,7 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 									  ShareUpdateExclusiveLock, 0,
 									  RangeVarCallbackForStats, &locked_table);
 
-	return relation_statistics_update_internal(reloid, fcinfo->args);
+	return relation_statistics_update_internal(reloid, args);
 }
 
 /*
@@ -223,42 +223,36 @@ relation_statistics_update_internal(Oid reloid, const NullableDatum *args)
 Datum
 pg_clear_relation_stats(PG_FUNCTION_ARGS)
 {
-	LOCAL_FCINFO(newfcinfo, 6);
-
-	InitFunctionCallInfoData(*newfcinfo, NULL, 6, InvalidOid, NULL, NULL);
-
-	newfcinfo->args[0].value = PG_GETARG_DATUM(0);
-	newfcinfo->args[0].isnull = PG_ARGISNULL(0);
-	newfcinfo->args[1].value = PG_GETARG_DATUM(1);
-	newfcinfo->args[1].isnull = PG_ARGISNULL(1);
-	newfcinfo->args[2].value = Int32GetDatum(0);
-	newfcinfo->args[2].isnull = false;
-	newfcinfo->args[3].value = Float4GetDatum(-1.0);
-	newfcinfo->args[3].isnull = false;
-	newfcinfo->args[4].value = Int32GetDatum(0);
-	newfcinfo->args[4].isnull = false;
-	newfcinfo->args[5].value = Int32GetDatum(0);
-	newfcinfo->args[5].isnull = false;
-
-	relation_statistics_update(newfcinfo);
+	NullableDatum args[NUM_RELATION_STATS_ARGS];
+
+	args[RELSCHEMA_ARG].value = PG_GETARG_DATUM(0);
+	args[RELSCHEMA_ARG].isnull = PG_ARGISNULL(0);
+	args[RELNAME_ARG].value = PG_GETARG_DATUM(1);
+	args[RELNAME_ARG].isnull = PG_ARGISNULL(1);
+	args[RELPAGES_ARG].value = Int32GetDatum(0);
+	args[RELPAGES_ARG].isnull = false;
+	args[RELTUPLES_ARG].value = Float4GetDatum(-1.0);
+	args[RELTUPLES_ARG].isnull = false;
+	args[RELALLVISIBLE_ARG].value = Int32GetDatum(0);
+	args[RELALLVISIBLE_ARG].isnull = false;
+	args[RELALLFROZEN_ARG].value = Int32GetDatum(0);
+	args[RELALLFROZEN_ARG].isnull = false;
+
+	relation_statistics_update(args);
 	PG_RETURN_VOID();
 }
 
 Datum
 pg_restore_relation_stats(PG_FUNCTION_ARGS)
 {
-	LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS);
+	NullableDatum positional_args[NUM_RELATION_STATS_ARGS];
 	bool		result = true;
 
-	InitFunctionCallInfoData(*positional_fcinfo, NULL,
-							 NUM_RELATION_STATS_ARGS,
-							 InvalidOid, NULL, NULL);
-
-	if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
-										  relarginfo))
+	if (!stats_fill_args_from_arg_pairs(fcinfo, positional_args,
+										relarginfo))
 		result = false;
 
-	if (!relation_statistics_update(positional_fcinfo))
+	if (!relation_statistics_update(positional_args))
 		result = false;
 
 	PG_RETURN_BOOL(result);
@@ -279,28 +273,21 @@ import_relation_statistics(Relation rel,
 						   const NullableDatum *relallvisible,
 						   const NullableDatum *relallfrozen)
 {
-	LOCAL_FCINFO(newfcinfo, NUM_RELATION_STATS_ARGS);
+	NullableDatum args[NUM_RELATION_STATS_ARGS];
+	NullableDatum unused = {.isnull = true, .value = (Datum) 0};
 
 	Assert(relpages);
 	Assert(reltuples);
 	Assert(relallvisible);
 	Assert(relallfrozen);
 
-	InitFunctionCallInfoData(*newfcinfo, NULL, NUM_RELATION_STATS_ARGS,
-							 InvalidOid, NULL, NULL);
-
-	newfcinfo->args[RELSCHEMA_ARG].value =
-		CStringGetTextDatum(get_namespace_name(RelationGetNamespace(rel)));
-	newfcinfo->args[RELSCHEMA_ARG].isnull = false;
-	newfcinfo->args[RELNAME_ARG].value =
-		CStringGetTextDatum(RelationGetRelationName(rel));
-	newfcinfo->args[RELNAME_ARG].isnull = false;
+	args[RELSCHEMA_ARG] = unused;
+	args[RELNAME_ARG] = unused;
 
-	newfcinfo->args[RELPAGES_ARG] = *relpages;
-	newfcinfo->args[RELTUPLES_ARG] = *reltuples;
-	newfcinfo->args[RELALLVISIBLE_ARG] = *relallvisible;
-	newfcinfo->args[RELALLFROZEN_ARG] = *relallfrozen;
+	args[RELPAGES_ARG] = *relpages;
+	args[RELTUPLES_ARG] = *reltuples;
+	args[RELALLVISIBLE_ARG] = *relallvisible;
+	args[RELALLFROZEN_ARG] = *relallfrozen;
 
-	return relation_statistics_update_internal(RelationGetRelid(rel),
-											   newfcinfo->args);
+	return relation_statistics_update_internal(RelationGetRelid(rel), args);
 }
diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c
index c1698497d68..4befc887cd2 100644
--- a/src/backend/statistics/stat_utils.c
+++ b/src/backend/statistics/stat_utils.c
@@ -340,17 +340,17 @@ statatt_get_index_expr(Relation rel, int attnum)
 
 /*
  * Translate variadic argument pairs from 'pairs_fcinfo' into a
- * 'positional_fcinfo' appropriate for calling relation_statistics_update() or
- * attribute_statistics_update() with positional arguments.
+ * NullableDatum[] appropriate for calling the internal statistics update
+ * functions for relation stats, attribute stats, or extended stats.
  *
- * Caller should have already initialized positional_fcinfo with a size
- * appropriate for calling the intended positional function, and arginfo
- * should also match the intended positional function.
+ * Caller should have already initialized args with a size appropriate for
+ * calling the intended function, and arginfo should also match the intended
+ * function.
  */
 bool
-stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
-								 FunctionCallInfo positional_fcinfo,
-								 struct StatsArgInfo *arginfo)
+stats_fill_args_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
+							   NullableDatum *positional_args,
+							   struct StatsArgInfo *arginfo)
 {
 	Datum	   *args;
 	bool	   *argnulls;
@@ -361,8 +361,8 @@ stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
 	/* clear positional args */
 	for (int i = 0; arginfo[i].argname != NULL; i++)
 	{
-		positional_fcinfo->args[i].value = (Datum) 0;
-		positional_fcinfo->args[i].isnull = true;
+		positional_args[i].value = (Datum) 0;
+		positional_args[i].isnull = true;
 	}
 
 	nargs = extract_variadic_args(pairs_fcinfo, 0, true,
@@ -417,8 +417,8 @@ stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
 			continue;
 		}
 
-		positional_fcinfo->args[argnum].value = args[i + 1];
-		positional_fcinfo->args[argnum].isnull = false;
+		positional_args[argnum].value = args[i + 1];
+		positional_args[argnum].isnull = false;
 	}
 
 	return result;
diff --git a/src/include/statistics/stat_utils.h b/src/include/statistics/stat_utils.h
index 8209c0d8949..0560826fcfe 100644
--- a/src/include/statistics/stat_utils.h
+++ b/src/include/statistics/stat_utils.h
@@ -37,9 +37,9 @@ extern bool stats_check_arg_pair(const NullableDatum *args,
 extern void RangeVarCallbackForStats(const RangeVar *relation,
 									 Oid relId, Oid oldRelId, void *arg);
 
-extern bool stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
-											 FunctionCallInfo positional_fcinfo,
-											 struct StatsArgInfo *arginfo);
+extern bool stats_fill_args_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
+										   NullableDatum *positional_args,
+										   struct StatsArgInfo *arginfo);
 
 extern void statatt_get_type(Oid reloid, AttrNumber attnum,
 							 Oid *atttypid, int32 *atttypmod,
-- 
2.50.1 (Apple Git-155)

