From 5a8afc8271195af31df079e52138d2dc247ec8ee Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Wed, 4 Feb 2026 05:24:50 -0500
Subject: [PATCH v5 2/2] FAILED WIP: errcontext()

This doesn't actually work because errsave_finish() only calls
errfinish(), where the context stack is walked, if elevel >= ERROR.
---
 src/backend/statistics/extended_stats_funcs.c | 88 ++++++++++++++++---
 1 file changed, 74 insertions(+), 14 deletions(-)

diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c
index 2771ec2d8cb..cdad61e1dc5 100644
--- a/src/backend/statistics/extended_stats_funcs.c
+++ b/src/backend/statistics/extended_stats_funcs.c
@@ -1,4 +1,4 @@
-/*-------------------------------------------------------------------------
+/*------------------------------------------------------------------------
  *
  * extended_stats_funcs.c
  *	  Functions for manipulating extended statistics.
@@ -134,6 +134,15 @@ typedef struct
 	bool		expressions;
 } StakindFlags;
 
+/*
+ *
+ */
+typedef struct
+{
+	AttrNumber	exprnum;
+	const char *element_name;
+}			ImportErrorExprContextArg;
+
 static void expand_stxkind(HeapTuple tup, StakindFlags *enabled);
 static void upsert_pg_statistic_ext_data(const Datum *values,
 										 const bool *nulls,
@@ -144,7 +153,7 @@ static bool check_mcvlist_array(const ArrayType *arr, int argindex,
 static Datum import_expressions(Relation pgsd, int numexprs,
 								Oid *atttypids, int32 *atttypmods,
 								Oid *atttypcolls, Jsonb *exprs_jsonb,
-								bool *ok);
+								bool *exprs_is_perfect);
 static Datum import_mcv(const ArrayType *mcv_arr,
 						const ArrayType *freqs_arr,
 						const ArrayType *base_freqs_arr,
@@ -871,6 +880,23 @@ mcv_error:
 	return mcv;
 }
 
+/*
+ * Callback for ImportErrorExprContext.
+ */
+static void
+import_error_expr_context_callback(void *arg)
+{
+	ImportErrorExprContextArg *ctx = arg;
+
+	errcontext("Expression Number %d", ctx->exprnum);
+
+	/*
+	 * if (ctx->element_name != NULL) errcontext("Expression Number %d,
+	 * element \"%s\"", ctx->exprnum, ctx->element_name); else
+	 * errcontext("Expression Number %d", ctx->exprnum);
+	 */
+}
+
 /*
  * Check if a given unterminated string is found in the list of expression argnames.
  */
@@ -998,9 +1024,8 @@ jbv_to_infunc_datum(JsonbValue *jval, PGFunction func, AttrNumber exprnum,
 	if (!ok)
 		ereport(WARNING,
 				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				errmsg("could not parse expression element"),
-				errhint("Expression %d, element \"%s\", value \"%s\": invalid type \"%s\"",
-						exprnum, argname, s, argtype));
+				errmsg("could not parse expression \"%s\" as type \"%s\"",
+					   s, argtype));
 
 	pfree(s);
 	return ok;
@@ -1031,8 +1056,21 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 	bool		found[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
 	JsonbValue	val[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
 
+	ImportErrorExprContextArg errctx_args = {
+		.exprnum = exprnum,
+		.element_name = NULL
+	};
+
+	ErrorContextCallback err_context = {
+		.callback = import_error_expr_context_callback,
+		.arg = &errctx_args,
+		.previous = error_context_stack
+	};
+
 	Assert(JsonContainerIsObject(cont));
 
+	error_context_stack = &err_context;
+
 	*row_is_perfect = true;
 
 	/*
@@ -1056,16 +1094,18 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 			case jbvNull:
 				break;
 			default:
+				errctx_args.element_name = s;
 				ereport(WARNING,
 						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						errmsg("invalid expression element"),
-						errhint("Expression %d, element \"%s\" must be type null or string",
-								exprnum, s));
+						errmsg("invalid expression element,"),
+						errhint("Must be type null or string"));
 				found[i] = false;
 				*row_is_perfect = false;
 		}
 	}
 
+	errctx_args.element_name = NULL;
+
 	/*
 	 * Look for invalid keys, warn on the first one found, but do not abandon
 	 * the tuple.
@@ -1084,8 +1124,8 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		ereport(WARNING,
 				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				errmsg("inconsistent expression elements"),
-				errhint("Expression %d, element \"%s\" and \"%s\" must be either both be strings or both be nulls.",
-						exprnum, extexprargname[MOST_COMMON_VALS_ELEM],
+				errhint("Elements \"%s\" and \"%s\" must be either both be strings or both be nulls.",
+						extexprargname[MOST_COMMON_VALS_ELEM],
 						extexprargname[MOST_COMMON_FREQS_ELEM]));
 		found[MOST_COMMON_VALS_ELEM] = false;
 		found[MOST_COMMON_FREQS_ELEM] = false;
@@ -1096,8 +1136,8 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		ereport(WARNING,
 				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				errmsg("inconsistent expression elements"),
-				errhint("Expression %d, element \"%s\" and \"%s\" must be either both be strings or both be nulls.",
-						exprnum, extexprargname[MOST_COMMON_ELEMS_ELEM],
+				errhint("Element \"%s\" and \"%s\" must be either both be strings or both be nulls.",
+						extexprargname[MOST_COMMON_ELEMS_ELEM],
 						extexprargname[MOST_COMMON_ELEM_FREQS_ELEM]));
 		found[MOST_COMMON_ELEMS_ELEM] = false;
 		found[MOST_COMMON_ELEM_FREQS_ELEM] = false;
@@ -1134,8 +1174,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		{
 			ereport(WARNING,
 					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					errmsg("could not determine element type of expression"),
-					errhint("Expression %d", exprnum));
+					errmsg("could not determine element type of expression"));
 
 			/* Must disable both */
 			found[MOST_COMMON_ELEMS_ELEM] = false;
@@ -1149,12 +1188,14 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 	{
 		Datum		datum;
 
+		errctx_args.element_name = extexprargname[NULL_FRAC_ELEM];
 		if (jbv_to_infunc_datum(&val[NULL_FRAC_ELEM], float4in, exprnum,
 								extexprargname[NULL_FRAC_ELEM], "float4",
 								&datum))
 			values[Anum_pg_statistic_stanullfrac - 1] = datum;
 		else
 			*row_is_perfect = false;
+		errctx_args.element_name = NULL;
 	}
 
 	/* avg_width */
@@ -1162,12 +1203,14 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 	{
 		Datum		datum;
 
+		errctx_args.element_name = extexprargname[AVG_WIDTH_ELEM];
 		if (jbv_to_infunc_datum(&val[AVG_WIDTH_ELEM], int4in, exprnum,
 								extexprargname[AVG_WIDTH_ELEM], "int4",
 								&datum))
 			values[Anum_pg_statistic_stawidth - 1] = datum;
 		else
 			*row_is_perfect = false;
+		errctx_args.element_name = NULL;
 	}
 
 	/* n_distinct */
@@ -1175,12 +1218,14 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 	{
 		Datum		datum;
 
+		errctx_args.element_name = extexprargname[N_DISTINCT_ELEM];
 		if (jbv_to_infunc_datum(&val[N_DISTINCT_ELEM], float4in, exprnum,
 								extexprargname[N_DISTINCT_ELEM], "float4",
 								&datum))
 			values[Anum_pg_statistic_stadistinct - 1] = datum;
 		else
 			*row_is_perfect = false;
+		errctx_args.element_name = NULL;
 	}
 
 	/*
@@ -1208,17 +1253,20 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		bool		val_ok = false;
 		bool		num_ok = false;
 
+		errctx_args.element_name = extexprargname[MOST_COMMON_VALS_ELEM];
 		scratch = jbv_string_get_text_datum(&val[MOST_COMMON_VALS_ELEM]);
 		stavalues = statatt_build_stavalues(extexprargname[MOST_COMMON_VALS_ELEM],
 											array_in_fn, scratch,
 											typid, typmod, &val_ok);
 		pfree((void *) scratch);
 
+		errctx_args.element_name = extexprargname[MOST_COMMON_FREQS_ELEM];
 		scratch = jbv_string_get_text_datum(&val[MOST_COMMON_FREQS_ELEM]);
 		stanumbers = statatt_build_stavalues(extexprargname[MOST_COMMON_FREQS_ELEM],
 											 array_in_fn, scratch,
 											 FLOAT4OID, -1, &num_ok);
 		pfree((void *) scratch);
+		errctx_args.element_name = NULL;
 
 		/* Only set the slot if both datums built */
 		if (val_ok && num_ok)
@@ -1237,6 +1285,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		bool		val_ok = false;
 		Datum		scratch;
 
+		errctx_args.element_name = extexprargname[HISTOGRAM_BOUNDS_ELEM];
 		scratch = jbv_string_get_text_datum(&val[HISTOGRAM_BOUNDS_ELEM]);
 		stavalues = statatt_build_stavalues(extexprargname[HISTOGRAM_BOUNDS_ELEM],
 											array_in_fn, scratch,
@@ -1250,6 +1299,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 							 0, true, stavalues, false);
 		else
 			*row_is_perfect = false;
+		errctx_args.element_name = NULL;
 	}
 
 	/* STATISTIC_KIND_CORRELATION */
@@ -1257,6 +1307,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 	{
 		Datum		corr[] = {(Datum) 0};
 
+		errctx_args.element_name = extexprargname[CORRELATION_ELEM];
 		if (jbv_to_infunc_datum(&val[CORRELATION_ELEM], float4in, exprnum,
 								extexprargname[CORRELATION_ELEM], "float4",
 								&corr[0]))
@@ -1271,6 +1322,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		}
 		else
 			*row_is_perfect = false;
+		errctx_args.element_name = NULL;
 	}
 
 	/* STATISTIC_KIND_MCELEM */
@@ -1282,17 +1334,20 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		bool		val_ok = false;
 		bool		num_ok = false;
 
+		errctx_args.element_name = extexprargname[MOST_COMMON_ELEMS_ELEM];
 		scratch = jbv_string_get_text_datum(&val[MOST_COMMON_ELEMS_ELEM]);
 		stavalues = statatt_build_stavalues(extexprargname[MOST_COMMON_ELEMS_ELEM],
 											array_in_fn, scratch,
 											elemtypid, typmod, &val_ok);
 		pfree((void *) scratch);
 
+		errctx_args.element_name = extexprargname[MOST_COMMON_ELEM_FREQS_ELEM];
 		scratch = jbv_string_get_text_datum(&val[MOST_COMMON_ELEM_FREQS_ELEM]);
 		stanumbers = statatt_build_stavalues(extexprargname[MOST_COMMON_ELEM_FREQS_ELEM],
 											 array_in_fn, scratch,
 											 FLOAT4OID, -1, &num_ok);
 		pfree((void *) scratch);
+		errctx_args.element_name = NULL;
 
 		/* Only set the slot if both datums built */
 		if (val_ok && num_ok)
@@ -1311,6 +1366,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 		Datum		stanumbers;
 		bool		num_ok = false;
 
+		errctx_args.element_name = extexprargname[ELEM_COUNT_HISTOGRAM_ELEM];
 		scratch = jbv_string_get_text_datum(&val[ELEM_COUNT_HISTOGRAM_ELEM]);
 		stanumbers = statatt_build_stavalues(extexprargname[ELEM_COUNT_HISTOGRAM_ELEM],
 											 array_in_fn, scratch,
@@ -1323,6 +1379,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 							 elemeqopr, typcoll, stanumbers, false, 0, true);
 		else
 			*row_is_perfect = false;
+		errctx_args.element_name = NULL;
 	}
 
 	/*
@@ -1335,6 +1392,9 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
 
 	pfree(pgstup);
 
+	Assert(error_context_stack == &err_context);
+	error_context_stack = err_context.previous;
+
 	return pgstdat;
 }
 
-- 
2.52.0

