From 49d51e335885a1e2b21b1756d004b03ddbeaeaed Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Sun, 1 Feb 2026 04:22:40 -0500
Subject: [PATCH v2jsonb] Add support for "exprs" in
 pg_restore_extended_stats()

This commit adds support for the restore of extended statistics of the
kind "exprs".

The input format consists of a jsonb object which must be an array of
objects which are keyed by statistics parameter names, like this:

[{"stat_type1": "...", "stat_type2": "...", ...},
 {"stat_type1": "...", "stat_type2": "...", ...}, ...]

The outer array must have as many elements as there are expressions
defined in the statistics object. The keys of the inner objects are
names of the statistical columns in pg_stats_ext_exprs (i.e. everything
after "inherited"). The values paired with those keys are numeric for
the scalar values (null_frac, avg_width, n_distinct, correlation) and
string for the other values.  This is necessary because the elements
represented in the "most_common_vals" field might themselves be arrays,
and our existing array deconstruction infrastructure isn't capable of
stopping decomoposition at the second dimension. So, by force-casting
those elements to text, we ensure that the we can validate the
"most_common_vals" by applying  the proper input functions as defined
by the stxexprs for the that statistics object.
---
 src/backend/statistics/extended_stats_funcs.c | 802 ++++++++++++++-
 src/bin/pg_dump/pg_dump.c                     |  39 +-
 src/test/regress/expected/stats_import.out    | 922 +++++++++++++++++-
 src/test/regress/sql/stats_import.sql         | 716 +++++++++++++-
 doc/src/sgml/func/func-admin.sgml             |  17 +-
 5 files changed, 2485 insertions(+), 11 deletions(-)

diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c
index db107684607..aeaec1bb50a 100644
--- a/src/backend/statistics/extended_stats_funcs.c
+++ b/src/backend/statistics/extended_stats_funcs.c
@@ -32,8 +32,10 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 /*
@@ -51,6 +53,7 @@ enum extended_stats_argnum
 	MOST_COMMON_VALS_ARG,
 	MOST_COMMON_FREQS_ARG,
 	MOST_COMMON_BASE_FREQS_ARG,
+	EXPRESSIONS_ARG,
 	NUM_EXTENDED_STATS_ARGS,
 };
 
@@ -70,9 +73,49 @@ static struct StatsArgInfo extarginfo[] =
 	[MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTARRAYOID},
 	[MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT8ARRAYOID},
 	[MOST_COMMON_BASE_FREQS_ARG] = {"most_common_base_freqs", FLOAT8ARRAYOID},
+	[EXPRESSIONS_ARG] = {"exprs", JSONBOID},
 	[NUM_EXTENDED_STATS_ARGS] = {0},
 };
 
+/*
+ * An index of the elements of a stxdexprs Datum, which repeat for each
+ * expression in the extended statistics object.
+ *
+ * NOTE: the RANGE_LENGTH & RANGE_BOUNDS stats are not yet reflected in any
+ * version of pg_stat_ext_exprs.
+ */
+enum extended_stats_exprs_element
+{
+	NULL_FRAC_ELEM = 0,
+	AVG_WIDTH_ELEM,
+	N_DISTINCT_ELEM,
+	MOST_COMMON_VALS_ELEM,
+	MOST_COMMON_FREQS_ELEM,
+	HISTOGRAM_BOUNDS_ELEM,
+	CORRELATION_ELEM,
+	MOST_COMMON_ELEMS_ELEM,
+	MOST_COMMON_ELEM_FREQS_ELEM,
+	ELEM_COUNT_HISTOGRAM_ELEM,
+	NUM_ATTRIBUTE_STATS_ELEMS
+};
+
+/*
+ * The argument names of the repeating arguments for stxdexprs.
+ */
+static const char *extexprargname[NUM_ATTRIBUTE_STATS_ELEMS] =
+{
+	"null_frac",
+	"avg_width",
+	"n_distinct",
+	"most_common_vals",
+	"most_common_freqs",
+	"histogram_bounds",
+	"correlation",
+	"most_common_elems",
+	"most_common_elem_freqs",
+	"elem_count_histogram",
+};
+
 static bool extended_statistics_update(FunctionCallInfo fcinfo);
 
 static HeapTuple get_pg_statistic_ext(Relation pg_stext, Oid nspoid,
@@ -98,6 +141,10 @@ static void upsert_pg_statistic_ext_data(const Datum *values,
 
 static bool check_mcvlist_array(const ArrayType *arr, int argindex,
 								int required_ndims, int mcv_length);
+static Datum import_expressions(Relation pgsd, int numexprs,
+								Oid *atttypids, int32 *atttypmods,
+								Oid *atttypcolls, Jsonb *exprs_jsonb,
+								bool *ok);
 static Datum import_mcv(const ArrayType *mcv_arr,
 						const ArrayType *freqs_arr,
 						const ArrayType *base_freqs_arr,
@@ -105,6 +152,10 @@ static Datum import_mcv(const ArrayType *mcv_arr,
 						Oid *atttypcolls, int numattrs,
 						bool *ok);
 
+static char *jbv_string_get_cstr(JsonbValue *jval);
+static Datum jbv_string_get_text_datum(JsonbValue *jval);
+static Datum jbv_get_float4_datum(JsonbValue *jval, const char *argname, bool *ok);
+static Datum jbv_get_int4_datum(JsonbValue *jval, const char *argname, bool *ok);
 
 /*
  * Fetch a pg_statistic_ext row by name and namespace OID.
@@ -296,6 +347,7 @@ extended_statistics_update(FunctionCallInfo fcinfo)
 			   !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG));
 	has.ndistinct = !PG_ARGISNULL(NDISTINCT_ARG);
 	has.dependencies = !PG_ARGISNULL(DEPENDENCIES_ARG);
+	has.expressions = !PG_ARGISNULL(EXPRESSIONS_ARG);
 
 	if (RecoveryInProgress())
 	{
@@ -492,6 +544,20 @@ extended_statistics_update(FunctionCallInfo fcinfo)
 		}
 	}
 
+	/* If the object can't support expressions, we should not have them. */
+	if (has.expressions && !enabled.expressions)
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("cannot specify parameter \"%s\".",
+					   extarginfo[EXPRESSIONS_ARG].argname),
+				errhint("Extended statistics object "
+						"does not support statistics of this type."));
+
+		has.expressions = false;
+		success = false;
+	}
+
 	/*
 	 * Either of these statistic types requires that we supply a semi-filled
 	 * VacAttrStatP array.
@@ -501,7 +567,7 @@ extended_statistics_update(FunctionCallInfo fcinfo)
 	 * attstattarget is 0, and we may have statistics data to import for those
 	 * attributes.
 	 */
-	if (has.mcv)
+	if (has.mcv || has.expressions)
 	{
 		atttypids = palloc0_array(Oid, numattrs);
 		atttypmods = palloc0_array(int32, numattrs);
@@ -636,6 +702,42 @@ extended_statistics_update(FunctionCallInfo fcinfo)
 			success = false;
 	}
 
+	if (has.expressions)
+	{
+		Datum		datum;
+		Relation	pgsd;
+		bool		ok = false;
+
+		pgsd = table_open(StatisticRelationId, RowExclusiveLock);
+
+		/*
+		 * Generate the expressions array.
+		 *
+		 * The attytypids, attytypmods, and atttypcols arrays have all the
+		 * regular attributes listed first, so we can pass those arrays with a
+		 * start point after the last regular attribute, and there should be
+		 * numexprs elements remaining.
+		 */
+		datum = import_expressions(pgsd, numexprs,
+								   &atttypids[numattnums],
+								   &atttypmods[numattnums],
+								   &atttypcolls[numattnums],
+								   PG_GETARG_JSONB_P(EXPRESSIONS_ARG),
+								   &ok);
+
+		table_close(pgsd, RowExclusiveLock);
+
+		if (ok)
+		{
+			Assert(datum != (Datum) 0);
+			values[Anum_pg_statistic_ext_data_stxdexpr - 1] = datum;
+			replaces[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
+			nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = false;
+		}
+		else
+			success = false;
+	}
+
 	upsert_pg_statistic_ext_data(values, nulls, replaces);
 
 cleanup:
@@ -764,6 +866,704 @@ mcv_error:
 	return mcv;
 }
 
+/*
+ * Look for key in a json object, if found store in val.
+ */
+static bool
+key_lookup(JsonbContainer *cont, const char *key, JsonbValue *val)
+{
+	Assert(JsonContainerIsObject(cont));
+	return (getKeyJsonValueFromContainer(cont, key, strlen(key), val) != NULL);
+}
+
+/*
+ * A key lookup that is ignored if it is null.
+ */
+static bool
+key_lookup_notnull(JsonbContainer *cont, const char *key, JsonbValue *val)
+{
+	if (!key_lookup(cont, key, val))
+		return false;
+	return (val->type != jbvNull);
+}
+
+
+/*
+ * Check if an unterminated string is found in the list of expression argnames.
+ */
+static bool
+key_in_expr_argnames(const char *s, int len)
+{
+	for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
+	{
+		if (strncmp(extexprargname[i], s, len) == 0)
+			return true;
+	}
+	return false;
+}
+
+/*
+ * Verify that all of the keys in the object are valid argnames.
+ */
+static void
+check_valid_expr_argnames(JsonbContainer *cont)
+{
+	bool		all_keys_valid = true;
+
+	JsonbIterator *jbit;
+	JsonbIteratorToken jitok;
+	JsonbValue	jkey;
+
+	Assert(JsonContainerIsObject(cont));
+
+	jbit = JsonbIteratorInit(cont);
+
+	/* We always start off with a BEGIN OBJECT */
+	jitok = JsonbIteratorNext(&jbit, &jkey, false);
+	Assert(jitok == WJB_BEGIN_OBJECT);
+
+	while (true)
+	{
+		JsonbValue	jval;
+		char	   *s;
+
+		jitok = JsonbIteratorNext(&jbit, &jkey, false);
+
+		/*
+		 * We have run of keys. This is the only condition where it is
+		 * memory-safe to break out of the loop.
+		 */
+		if (jitok == WJB_END_OBJECT)
+			break;
+
+		/* We can only find keys inside an object */
+		Assert(jitok == WJB_KEY);
+		Assert(jkey.type == jbvString);
+
+		/* A value must follow the key, it must be a string or null */
+		jitok = JsonbIteratorNext(&jbit, &jval, false);
+		Assert(jitok == WJB_VALUE);
+
+		/*
+		 * If we have already found an invalid key, there is no point in
+		 * looking for more, because additional WARNINGs are just clutter. But
+		 * we must continue iterating over the json to ensure that we clean up
+		 * all memory.
+		 */
+		if (!all_keys_valid)
+			continue;
+
+		if (key_in_expr_argnames(jkey.val.string.val, jkey.val.string.len))
+			continue;
+
+		s = jbv_string_get_cstr(&jkey);
+
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("malformed expr expression: \"%s\": is not an expression key name, value ignored", s));
+
+		pfree(s);
+		all_keys_valid = false;
+	}
+}
+
+/*
+ * Simple conversion of jsonb string val to cstring
+ */
+static char *
+jbv_string_get_cstr(JsonbValue *jval)
+{
+	char	   *s;
+
+	Assert(jval->type == jbvString);
+
+	s = palloc0(jval->val.string.len + 1);
+	memcpy(s, jval->val.string.val, jval->val.string.len);
+
+	return s;
+}
+
+/*
+ * Simple conversion of jsonb string val to TextDatum
+ */
+static Datum
+jbv_string_get_text_datum(JsonbValue *jval)
+{
+	char	   *s = jbv_string_get_cstr(jval);
+	Datum		datum = CStringGetTextDatum(s);
+
+	pfree(s);
+	return datum;
+}
+
+/*
+ * Import a float4 datum from a json numeric value.
+ */
+static Datum
+jbv_get_float4_datum(JsonbValue *jval, const char *argname, bool *ok)
+{
+	ErrorSaveContext escontext = {T_ErrorSaveContext};
+	char	   *s;
+	Datum		datum;
+
+	/* JSON shouldn't permit NaN/Inf numerics, but check anyway */
+	if (jval->type != jbvNumeric ||
+		numeric_is_nan(jval->val.numeric) ||
+		numeric_is_inf(jval->val.numeric))
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("malformed expressions \"%s\": must be a regular numeric",
+					   argname));
+		return false;
+	}
+
+	s = OidOutputFunctionCall(F_NUMERIC_OUT, NumericGetDatum(jval->val.numeric));
+
+	*ok = DirectInputFunctionCallSafe(float4in, s, InvalidOid, -1,
+									  (Node *) &escontext, &datum);
+
+	if (!*ok)
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("could not parse expression element \"%s\" value \"%s\": invalid type \"%s\"",
+					   argname, s, "float4"));
+	}
+
+	pfree(s);
+	return datum;
+}
+
+/*
+ * Import a int4 datum from a json numeric value.
+ */
+static Datum
+jbv_get_int4_datum(JsonbValue *jval, const char *argname, bool *ok)
+{
+	ErrorSaveContext escontext = {T_ErrorSaveContext};
+	char	   *s;
+	Datum		datum;
+
+	if (jval->type != jbvNumeric ||
+		numeric_is_nan(jval->val.numeric) ||
+		numeric_is_inf(jval->val.numeric))
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("malformed expressions \"%s\": must be a regular numeric",
+					   argname));
+		return false;
+	}
+
+	s = OidOutputFunctionCall(F_NUMERIC_OUT, NumericGetDatum(jval->val.numeric));
+
+	*ok = DirectInputFunctionCallSafe(int4in, s, InvalidOid, -1,
+									  (Node *) &escontext, &datum);
+
+	if (!*ok)
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("could not parse expression element \"%s\" value \"%s\": invalid type \"%s\"",
+					   argname, s, "int4"));
+	}
+
+	pfree(s);
+	return datum;
+}
+
+/*
+ * Create a pg_statisitc tuple from an expression container.
+ *
+ * stxdexprs is an array of pg_statistic rows with all of the object
+ * identification fields left at defaults.
+ */
+static Datum
+import_pg_statistic(Relation pgsd, JsonbContainer *cont, FmgrInfo *array_in_fn,
+					Oid typid, int32 typmod, Oid typcoll, bool *ok)
+{
+	TypeCacheEntry *typcache;
+	Datum		values[Natts_pg_statistic];
+	bool		nulls[Natts_pg_statistic];
+	bool		replaces[Natts_pg_statistic];
+	JsonbValue	jval;
+	const char *vals_arg,
+			   *freqs_arg;
+	char	   *mcv = NULL;
+	char	   *mcf = NULL;
+	char	   *mcev = NULL;
+	char	   *mcef = NULL;
+	char	   *dechist = NULL;
+
+	HeapTuple	pgstup = NULL;
+	Datum		pgstdat = (Datum) 0;
+	Oid			elemtypid = InvalidOid;
+	Oid			elemeqopr = InvalidOid;
+
+	Assert(JsonContainerIsObject(cont));
+	*ok = false;
+
+	/*
+	 * XXX:
+	 *
+	 * We may need to duplicate some steps from statatt_get_type() that we do
+	 * not currently, those are:
+	 *
+	 * #include "catalog/pg_collation_d.h" if (typid == TSVECTOROID) stacoll =
+	 * DEFAULT_COLLATION_OID;
+	 *
+	 * The multirange step-down may also need to happen here too.
+	 */
+
+	/* This finds the right operators even if atttypid is a domain */
+	typcache = lookup_type_cache(typid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
+
+	statatt_init_empty_tuple(InvalidOid, InvalidAttrNumber, false,
+							 values, nulls, replaces);
+
+	/* null_frac */
+	if (key_lookup_notnull(cont, extexprargname[NULL_FRAC_ELEM], &jval))
+	{
+		bool		val_ok = false;
+
+		values[Anum_pg_statistic_stanullfrac - 1] =
+			jbv_get_float4_datum(&jval, extexprargname[NULL_FRAC_ELEM], &val_ok);
+		if (!val_ok)
+			goto error;
+	}
+
+	/* avg_width */
+	if (key_lookup_notnull(cont, extexprargname[AVG_WIDTH_ELEM], &jval))
+	{
+		bool		val_ok = false;
+
+		values[Anum_pg_statistic_stawidth - 1] =
+			jbv_get_int4_datum(&jval, extexprargname[NULL_FRAC_ELEM], &val_ok);
+		if (!val_ok)
+			goto error;
+	}
+
+	/* n_distinct */
+	if (key_lookup_notnull(cont, extexprargname[N_DISTINCT_ELEM], &jval))
+	{
+		bool		val_ok = false;
+
+		values[Anum_pg_statistic_stadistinct - 1] =
+			jbv_get_float4_datum(&jval, extexprargname[N_DISTINCT_ELEM], &val_ok);
+		if (!val_ok)
+			goto error;
+	}
+
+	/*
+	 * The STAKIND statistics are the same as the ones found in attribute
+	 * stats.  However, these are all derived from json strings, whereas the
+	 * ones derived for attribute stats are a mix of datatypes. This limits
+	 * the opportunities for code sharing between the two.
+	 *
+	 * Some statistic kinds have both a stanumbers and a stavalues components.
+	 * In those cases, both values must either be NOT NULL or both NULL, and
+	 * if they aren't then we need to reject that stakind completely.
+	 * Currently we go a step further and reject the expression array
+	 * completely.
+	 *
+	 * Once it is established that the pairs are in NULL/NOT-NULL alignment,
+	 * we can test either expr_nulls[] value to see if the stakind has
+	 * value(s) that we can set or not.
+	 */
+
+	/* STATISTIC_KIND_MCV */
+	vals_arg = extexprargname[MOST_COMMON_VALS_ELEM];
+	freqs_arg = extexprargname[MOST_COMMON_FREQS_ELEM];
+
+	if (key_lookup_notnull(cont, vals_arg, &jval))
+	{
+		if (jval.type != jbvString)
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("invalid expression element \"%s\": must be type string",
+						   vals_arg));
+			goto error;
+		}
+		mcv = jbv_string_get_cstr(&jval);
+	}
+
+	if (key_lookup_notnull(cont, freqs_arg, &jval))
+	{
+		if (jval.type != jbvString)
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("invalid expression element \"%s\": must be type string",
+						   freqs_arg));
+			goto error;
+		}
+		mcf = jbv_string_get_cstr(&jval);
+	}
+
+	if ((mcv == NULL) != (mcf == NULL))
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("invalid expression elements %s and %s: conflicting NULL/NOT NULL",
+					   vals_arg, freqs_arg),
+				errhint("The elements must both be NULL or both NOT NULL."));
+		goto error;
+	}
+
+	if (mcv != NULL)
+	{
+		Datum		scratch;
+		Datum		stavalues;
+		Datum		stanumbers;
+		bool		val_ok = false;
+
+		scratch = CStringGetTextDatum(mcv);
+		stavalues = statatt_build_stavalues(vals_arg,
+											array_in_fn, scratch,
+											typid, typmod, &val_ok);
+		pfree((void *) scratch);
+
+		if (!val_ok)
+			goto error;
+
+		scratch = CStringGetTextDatum(mcf);
+		stanumbers = statatt_build_stavalues(freqs_arg,
+											 array_in_fn, scratch,
+											 FLOAT4OID, -1, &val_ok);
+		pfree((void *) scratch);
+
+		if (!val_ok)
+			goto error;
+
+		statatt_set_slot(values, nulls, replaces,
+						 STATISTIC_KIND_MCV,
+						 typcache->eq_opr, typcoll,
+						 stanumbers, false, stavalues, false);
+	}
+
+	/* STATISTIC_KIND_HISTOGRAM */
+	if (key_lookup_notnull(cont, extexprargname[HISTOGRAM_BOUNDS_ELEM], &jval))
+	{
+		Datum		stavalues;
+		bool		val_ok = false;
+		Datum		scratch;
+
+		if (jval.type != jbvString)
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("invalid expression element \"%s\": must be type string",
+						   extexprargname[HISTOGRAM_BOUNDS_ELEM]));
+			goto error;
+		}
+		scratch = jbv_string_get_text_datum(&jval);
+		stavalues = statatt_build_stavalues(extexprargname[HISTOGRAM_BOUNDS_ELEM],
+											array_in_fn, scratch,
+											typid, typmod, &val_ok);
+		pfree((void *) scratch);
+
+		if (!val_ok)
+			goto error;
+
+		statatt_set_slot(values, nulls, replaces,
+						 STATISTIC_KIND_HISTOGRAM,
+						 typcache->lt_opr, typcoll,
+						 0, true, stavalues, false);
+	}
+
+	/* STATISTIC_KIND_CORRELATION */
+	if (key_lookup_notnull(cont, extexprargname[CORRELATION_ELEM], &jval))
+	{
+		bool		val_ok = false;
+		ArrayType  *arry;
+		Datum		stanumbers;
+		Datum		corr[] = {(Datum) 0};
+		const char *argname = extexprargname[CORRELATION_ELEM];
+
+		corr[0] = jbv_get_float4_datum(&jval, argname, &val_ok);
+
+		if (!val_ok)
+			goto error;
+
+		arry = construct_array_builtin(corr, 1, FLOAT4OID);
+
+		stanumbers = PointerGetDatum(arry);
+
+		statatt_set_slot(values, nulls, replaces,
+						 STATISTIC_KIND_CORRELATION,
+						 typcache->lt_opr, typcoll,
+						 stanumbers, false, 0, true);
+	}
+
+	/*
+	 * STATISTIC_KIND_MCELEM and STATISTIC_KIND_DECHIST are intertwined
+	 * because they are collections of base elements of the composite values
+	 * of the main values datatype. For that reason we find the base element
+	 * types before generating the datums for either.
+	 */
+	vals_arg = extexprargname[MOST_COMMON_ELEMS_ELEM];
+	freqs_arg = extexprargname[MOST_COMMON_ELEM_FREQS_ELEM];
+	if (key_lookup_notnull(cont, vals_arg, &jval))
+	{
+		if (jval.type != jbvString)
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("invalid expression element \"%s\": must be type string",
+						   vals_arg));
+			goto error;
+		}
+		mcev = jbv_string_get_cstr(&jval);
+	}
+
+	if (key_lookup_notnull(cont, freqs_arg, &jval))
+	{
+		if (jval.type != jbvString)
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("invalid expression element \"%s\": must be type string",
+						   freqs_arg));
+			goto error;
+		}
+		mcef = jbv_string_get_cstr(&jval);
+	}
+
+	if ((mcev == NULL) != (mcef == NULL))
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("invalid expression elements %s and %s: conflicting NULL/NOT NULL",
+					   vals_arg, freqs_arg),
+				errhint("The elements must both be NULL or both NOT NULL."));
+		goto error;
+	}
+
+	if (key_lookup_notnull(cont, extexprargname[ELEM_COUNT_HISTOGRAM_ELEM], &jval))
+	{
+		if (jval.type != jbvString)
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("invalid expression element \"%s\": must be type string",
+						   extexprargname[ELEM_COUNT_HISTOGRAM_ELEM]));
+			goto error;
+		}
+		dechist = jbv_string_get_cstr(&jval);
+	}
+
+	/*
+	 * We only need to fetch element type and eq operator if we have a stat of
+	 * type MCELEM or DECHIST, otherwise the values are unnecessary and not
+	 * meaningful.
+	 */
+	if ((mcev != NULL) || (dechist != NULL))
+	{
+		if (!statatt_get_elem_type(typid, typcache->typtype,
+								   &elemtypid, &elemeqopr))
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("could not determine element type of expression"));
+			goto error;
+		}
+	}
+
+	/* STATISTIC_KIND_MCELEM */
+	if (mcev != NULL)
+	{
+		Datum		scratch;
+		Datum		stavalues;
+		Datum		stanumbers;
+		bool		val_ok = false;
+
+		scratch = CStringGetTextDatum(mcev);
+		stavalues = statatt_build_stavalues(vals_arg,
+											array_in_fn, scratch,
+											elemtypid, typmod, &val_ok);
+		pfree((void *) scratch);
+
+		if (!val_ok)
+			goto error;
+
+		scratch = CStringGetTextDatum(mcef);
+		stanumbers = statatt_build_stavalues(freqs_arg,
+											 array_in_fn, scratch,
+											 FLOAT4OID, -1, &val_ok);
+		pfree((void *) scratch);
+
+		if (!val_ok)
+			goto error;
+
+		statatt_set_slot(values, nulls, replaces,
+						 STATISTIC_KIND_MCELEM,
+						 elemeqopr, typcoll,
+						 stanumbers, false, stavalues, false);
+	}
+
+	/* STATISTIC_KIND_DECHIST */
+	if (dechist != NULL)
+	{
+		Datum		scratch;
+		Datum		stanumbers;
+		bool		val_ok = false;
+
+		scratch = CStringGetTextDatum(dechist);
+		stanumbers = statatt_build_stavalues(extexprargname[ELEM_COUNT_HISTOGRAM_ELEM],
+											 array_in_fn, scratch,
+											 FLOAT4OID, -1, &val_ok);
+
+		pfree((void *) scratch);
+
+		if (!val_ok)
+			goto error;
+
+		statatt_set_slot(values, nulls, replaces, STATISTIC_KIND_DECHIST,
+						 elemeqopr, typcoll,
+						 stanumbers, false, 0, true);
+	}
+
+	/*
+	 * Look for invalid keys, warn on the first one found, but do not abandon
+	 * the tuple.
+	 */
+	check_valid_expr_argnames(cont);
+
+	/*
+	 * Currently there is no extended stats export of the statistic kinds
+	 * BOUNDS_HISTOGRAM or RANGE_LENGTH_HISTOGRAM so these cannot be imported.
+	 * These may be added in the future.
+	 */
+	pgstup = heap_form_tuple(RelationGetDescr(pgsd), values, nulls);
+	pgstdat = heap_copy_tuple_as_datum(pgstup, RelationGetDescr(pgsd));
+	*ok = true;
+
+error:
+	if (mcv != NULL)
+		pfree(mcv);
+	if (mcf != NULL)
+		pfree(mcf);
+	if (mcev != NULL)
+		pfree(mcev);
+	if (mcef != NULL)
+		pfree(mcef);
+	if (dechist != NULL)
+		pfree(dechist);
+	if (pgstup != NULL)
+		pfree(pgstup);
+
+	return pgstdat;
+}
+
+/*
+ * Create the stxdexprs datum using the user input in an array of array of
+ * text, referenced against the datatypes for the expressions.
+ *
+ * This datum is needed to fill out a complete pg_statistic_ext_data tuple.
+ *
+ * The input arrays should each have "numexprs" elements in them and they
+ * should be in the order that the expressions appear in the statistics
+ * object.
+ *
+ * It is not practical to update parts of an element of stxdexprs, so if any
+ * conversion errors are found in this function, then the entire attribute
+ * will be left unchanged.
+ */
+static Datum
+import_expressions(Relation pgsd, int numexprs,
+				   Oid *atttypids, int32 *atttypmods,
+				   Oid *atttypcolls, Jsonb *exprs_jsonb,
+				   bool *ok)
+{
+	Oid			pgstypoid = get_rel_type_id(StatisticRelationId);
+
+	ArrayBuildState *astate = NULL;
+	Datum		result = (Datum) 0;
+
+	const char *argname = extarginfo[EXPRESSIONS_ARG].argname;
+	JsonbContainer *root;
+	int			num_root_elements;
+
+	FmgrInfo	array_in_fn;
+
+	*ok = false;
+
+	/* Json schema must be [{expr},...] */
+	if (!JB_ROOT_IS_ARRAY(exprs_jsonb))
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("malformed expressions \"%s\": expected root level array",
+					   argname));
+		goto exprs_error;
+	}
+
+	root = &exprs_jsonb->root;
+
+	/*
+	 * The number of elements in the array must match the number of
+	 * expressions in the stats object definition.
+	 */
+	num_root_elements = JsonContainerSize(root);
+	if (numexprs != num_root_elements)
+	{
+		ereport(WARNING,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("malformed expressions \"%s\": exprected array of %d, found %d",
+					   argname, num_root_elements, numexprs));
+		goto exprs_error;
+	}
+
+	fmgr_info(F_ARRAY_IN, &array_in_fn);
+
+	/*
+	 * Iterate over each expected expression object in the array.
+	 *
+	 * The values/nulls/replaces arrays are deconstructed into a 1-D arrays,
+	 * so we have to advance an offset by NUM_ATTRIBUTE_STATS_ELEMS to get to
+	 * the next row of the 2-D array.
+	 */
+	for (int i = 0; i < numexprs; i++)
+	{
+		Datum		pgstdat;
+
+		JsonbValue *elem = getIthJsonbValueFromContainer(root, i);
+		JsonbContainer *exprcont;
+		bool		val_ok = false;
+
+		if (elem->type != jbvBinary)
+		{
+			ereport(WARNING,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("malformed expressions \"%s\": all array elements must be objects",
+						   argname));
+			goto exprs_error;
+		}
+		exprcont = (JsonbContainer *) elem->val.binary.data;
+
+		pgstdat = import_pg_statistic(pgsd, exprcont, &array_in_fn, atttypids[i],
+									  atttypmods[i], atttypcolls[i], &val_ok);
+
+		if (!val_ok)
+			goto exprs_error;
+
+		astate = accumArrayResult(astate, pgstdat, false, pgstypoid,
+								  CurrentMemoryContext);
+	}
+
+	if (astate != NULL)
+	{
+		result = makeArrayResult(astate, CurrentMemoryContext);
+		*ok = true;
+	}
+
+exprs_error:
+	/* cleanup */
+	return result;
+};
+
 /*
  * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext
  * row and "inherited" pair.
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2bebefd0ba2..4110ce3b32b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -18652,11 +18652,41 @@ dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
 		if (fout->remoteVersion >= 130000)
 			appendPQExpBufferStr(pq,
 								 "e.most_common_vals, e.most_common_freqs, "
-								 "e.most_common_base_freqs ");
+								 "e.most_common_base_freqs, ");
 		else
 			appendPQExpBufferStr(pq,
 								 "NULL AS most_common_vals, NULL AS most_common_freqs, "
-								 "NULL AS most_common_base_freqs ");
+								 "NULL AS most_common_base_freqs, ");
+
+		/* Expressions were introduced in v14 */
+		if (fout->remoteVersion >= 140000)
+		{
+			appendPQExpBufferStr(pq,
+								 "( "
+								 "SELECT jsonb_pretty(jsonb_agg("
+								 "    jsonb_build_object( "
+								 "       'null_frac', ee.null_frac, "
+								 "       'avg_width', ee.avg_width, "
+								 "       'n_distinct', ee.n_distinct, "
+								 "       'most_common_vals', ee.most_common_vals, "
+								 "       'most_common_freqs', ee.most_common_freqs, "
+								 "       'histogram_bounds', ee.histogram_bounds, "
+								 "       'correlation', ee.correlation, "
+								 "       'most_common_elems', ee.most_common_elems, "
+								 "       'most_common_elem_freqs', ee.most_common_elem_freqs, "
+								 "       'elem_count_histogram', ee.elem_count_histogram))) "
+								 "FROM pg_stats_ext_exprs AS ee "
+								 "WHERE ee.statistics_schemaname = $1 "
+								 "AND ee.statistics_name = $2 ");
+
+			/* Inherited expressions introduced in v15 */
+			if (fout->remoteVersion >= 150000)
+				appendPQExpBufferStr(pq, "AND ee.inherited = e.inherited");
+
+			appendPQExpBufferStr(pq, ") AS exprs ");
+		}
+		else
+			appendPQExpBufferStr(pq, "NULL AS exprs ");
 
 		/* pg_stats_ext introduced in v12 */
 		if (fout->remoteVersion >= 120000)
@@ -18710,6 +18740,7 @@ dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
 		int			i_mcv = PQfnumber(res, "most_common_vals");
 		int			i_mcf = PQfnumber(res, "most_common_freqs");
 		int			i_mcbf = PQfnumber(res, "most_common_base_freqs");
+		int			i_exprs = PQfnumber(res, "exprs");
 
 		for (int i = 0; i < nstats; i++)
 		{
@@ -18757,6 +18788,10 @@ dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
 				appendNamedArgument(out, fout, "most_common_base_freqs", "double precision[]",
 									PQgetvalue(res, i, i_mcbf));
 
+			if (!PQgetisnull(res, i, i_exprs))
+				appendNamedArgument(out, fout, "exprs", "jsonb",
+									PQgetvalue(res, i, i_exprs));
+
 			appendPQExpBufferStr(out, "\n);\n");
 		}
 
diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out
index 37131f9ceab..41e8250cc51 100644
--- a/src/test/regress/expected/stats_import.out
+++ b/src/test/regress/expected/stats_import.out
@@ -2155,8 +2155,21 @@ SELECT pg_catalog.pg_restore_extended_stats(
                         {red,"{[11,13),[15,19),[20,30)}","{[11,13),[15,19),[20,30),[10000,10200)}"},
                         {red,"{[21,23),[25,29),[120,130)}","{[21,23),[25,29),[120,130),[10000,10200)}"}}'::text[],
   'most_common_freqs', '{0.3333333333333333,0.3333333333333333,0.3333333333333333}'::double precision[],
-  'most_common_base_freqs', '{0.1111111111111111,0.1111111111111111,0.1111111111111111}'::double precision[]
-);
+  'most_common_base_freqs', '{0.1111111111111111,0.1111111111111111,0.1111111111111111}'::double precision[],
+  'exprs', '[
+              {
+                  "avg_width": 60,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": null,
+                  "histogram_bounds": null,
+                  "most_common_vals": null,
+                  "most_common_elems": null,
+                  "most_common_freqs": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
  pg_restore_extended_stats 
 ---------------------------
  t
@@ -2191,6 +2204,911 @@ most_common_val_nulls  | {{f,f,f},{f,f,f},{f,f,f}}
 most_common_freqs      | {0.3333333333333333,0.3333333333333333,0.3333333333333333}
 most_common_base_freqs | {0.1111111111111111,0.1111111111111111,0.1111111111111111}
 
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_mr_stat' AND
+    e.inherited = false
+\gx
+-[ RECORD 1 ]----------+---------------------------------------------
+expr                   | (mrange + '{[10000,10200)}'::int4multirange)
+null_frac              | 0
+avg_width              | 60
+n_distinct             | -1
+most_common_vals       | 
+most_common_freqs      | 
+histogram_bounds       | 
+correlation            | 
+most_common_elems      | 
+most_common_elem_freqs | 
+elem_count_histogram   | 
+
+-- Incorrect extended stats kind, exprs not supported
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_ndistinct',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  cannot specify parameter "exprs".
+HINT:  Extended statistics object does not support statistics of this type.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- Invalid exprs, not array
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '{
+               "avg_width": 4,
+                "null_frac": 0,
+                "n_distinct": -0.75,
+                "correlation": -0.6,
+                "histogram_bounds": "{-1,0}",
+                "most_common_vals": "{1}",
+                "most_common_elems": null,
+                "most_common_freqs": "{0.5}",
+                "elem_count_histogram": null,
+                "most_common_elem_freqs": null
+              }'::jsonb);
+WARNING:  malformed expressions "exprs": expected root level array
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs wrong number of exprs
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  malformed expressions "exprs": exprected array of 1, found 2
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs null_frac not a float
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": "BADNULLFRAC",
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  malformed expressions "null_frac": must be a regular numeric
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs avg_width not an integer
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": "BADAVGWIDTH",
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  malformed expressions "null_frac": must be a regular numeric
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs n_dinstinct not a float
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": "BADNDISTINCT",
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  malformed expressions "n_distinct": must be a regular numeric
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- MCV not null, MCF null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  invalid expression elements most_common_vals and most_common_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- MCV not null, MCF missing
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  invalid expression elements most_common_vals and most_common_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- MCV null, MCF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": null,
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  invalid expression elements most_common_vals and most_common_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- MCV missing, MCF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  invalid expression elements most_common_vals and most_common_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs most_common_vals element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{BADMCV}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  invalid input syntax for type integer: "BADMCV"
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs most_common_freqs element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{BADMCF}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  invalid input syntax for type real: "BADMCF"
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs histogram wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{BADHIST,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  invalid input syntax for type integer: "BADHIST"
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs correlation wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": "BADCORR",
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+WARNING:  malformed expressions "correlation": must be a regular numeric
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- ok: exprs
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+ pg_restore_extended_stats 
+---------------------------
+ t
+(1 row)
+
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_stat_clone' AND
+    e.inherited = false
+\gx
+-[ RECORD 1 ]----------+----------------------
+expr                   | lower(arange)
+null_frac              | 0
+avg_width              | 4
+n_distinct             | -0.75
+most_common_vals       | {1}
+most_common_freqs      | {0.5}
+histogram_bounds       | {-1,0}
+correlation            | -0.6
+most_common_elems      | 
+most_common_elem_freqs | 
+elem_count_histogram   | 
+-[ RECORD 2 ]----------+----------------------
+expr                   | array_length(tags, 1)
+null_frac              | 0.25
+avg_width              | 4
+n_distinct             | -0.5
+most_common_vals       | {2}
+most_common_freqs      | {0.5}
+histogram_bounds       | 
+correlation            | 1
+most_common_elems      | 
+most_common_elem_freqs | 
+elem_count_histogram   | 
+
+-- A statistics object for testing MCELEM values in expressions
+CREATE STATISTICS stats_import.test_stat_mcelem
+  ON name, (ARRAY[(comp).a, lower(arange)])
+  FROM stats_import.test;
+-- MCEV not null, MCEF null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": null
+              }
+            ]'::jsonb);
+WARNING:  invalid expression elements most_common_elems and most_common_elem_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- MCEV not null, MCEF missing
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}"
+              }
+            ]'::jsonb);
+WARNING:  invalid expression elements most_common_elems and most_common_elem_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- MCEV null, MCEF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": null,
+                  "most_common_freqs": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+WARNING:  invalid expression elements most_common_elems and most_common_elem_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- MCEV missing, MCEF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_freqs": null,
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+WARNING:  invalid expression elements most_common_elems and most_common_elem_freqs: conflicting NULL/NOT NULL
+HINT:  The elements must both be NULL or both NOT NULL.
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs most_common_elems element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,BADELEM,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+WARNING:  invalid input syntax for type integer: "BADELEM"
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs most_common_elem_freqs element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{BADELEMFREQ,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+WARNING:  invalid input syntax for type real: "BADELEMFREQ"
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- exprs histogram bounds element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{BADELEMHIST,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+WARNING:  invalid input syntax for type real: "BADELEMHIST"
+ pg_restore_extended_stats 
+---------------------------
+ f
+(1 row)
+
+-- ok: exprs mcelem
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+ pg_restore_extended_stats 
+---------------------------
+ t
+(1 row)
+
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_stat_mcelem' AND
+    e.inherited = false
+\gx
+-[ RECORD 1 ]----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+expr                   | ARRAY[(comp).a, lower(arange)]
+null_frac              | 0
+avg_width              | 33
+n_distinct             | -1
+most_common_vals       | 
+most_common_freqs      | 
+histogram_bounds       | {"{1,1}","{2,1}","{3,-1}","{NULL,0}"}
+correlation            | 1
+most_common_elems      | {-1,0,1,2,3}
+most_common_elem_freqs | {0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}
+elem_count_histogram   | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}
+
+-- ok, with warning: extra exprs param
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}",
+                  "bad_param": "text no one will ever parse"
+              }
+            ]'::jsonb);
+WARNING:  malformed expr expression: "bad_param": is not an expression key name, value ignored
+ pg_restore_extended_stats 
+---------------------------
+ t
+(1 row)
+
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_stat_mcelem' AND
+    e.inherited = false
+\gx
+-[ RECORD 1 ]----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+expr                   | ARRAY[(comp).a, lower(arange)]
+null_frac              | 0
+avg_width              | 33
+n_distinct             | -1
+most_common_vals       | 
+most_common_freqs      | 
+histogram_bounds       | {"{1,1}","{2,1}","{3,-1}","{NULL,0}"}
+correlation            | 1
+most_common_elems      | {-1,0,1,2,3}
+most_common_elem_freqs | {0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}
+elem_count_histogram   | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}
+
 -- Test the ability of pg_restore_extended_stats() to import all of the
 -- statistic values from an extended statistic object that has been
 -- populated via a regular ANALYZE.  This checks after the statistics
diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql
index 8db7cd93b88..8ba2b266213 100644
--- a/src/test/regress/sql/stats_import.sql
+++ b/src/test/regress/sql/stats_import.sql
@@ -1543,8 +1543,21 @@ SELECT pg_catalog.pg_restore_extended_stats(
                         {red,"{[11,13),[15,19),[20,30)}","{[11,13),[15,19),[20,30),[10000,10200)}"},
                         {red,"{[21,23),[25,29),[120,130)}","{[21,23),[25,29),[120,130),[10000,10200)}"}}'::text[],
   'most_common_freqs', '{0.3333333333333333,0.3333333333333333,0.3333333333333333}'::double precision[],
-  'most_common_base_freqs', '{0.1111111111111111,0.1111111111111111,0.1111111111111111}'::double precision[]
-);
+  'most_common_base_freqs', '{0.1111111111111111,0.1111111111111111,0.1111111111111111}'::double precision[],
+  'exprs', '[
+              {
+                  "avg_width": 60,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": null,
+                  "histogram_bounds": null,
+                  "most_common_vals": null,
+                  "most_common_elems": null,
+                  "most_common_freqs": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
 
 SELECT replace(e.n_distinct,   '}, ', E'},\n') AS n_distinct,
        replace(e.dependencies, '}, ', E'},\n') AS dependencies,
@@ -1557,6 +1570,705 @@ WHERE e.statistics_schemaname = 'stats_import' AND
     e.inherited = false
 \gx
 
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_mr_stat' AND
+    e.inherited = false
+\gx
+
+-- Incorrect extended stats kind, exprs not supported
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_ndistinct',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+
+-- Invalid exprs, not array
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '{
+               "avg_width": 4,
+                "null_frac": 0,
+                "n_distinct": -0.75,
+                "correlation": -0.6,
+                "histogram_bounds": "{-1,0}",
+                "most_common_vals": "{1}",
+                "most_common_elems": null,
+                "most_common_freqs": "{0.5}",
+                "elem_count_histogram": null,
+                "most_common_elem_freqs": null
+              }'::jsonb);
+-- exprs wrong number of exprs
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- exprs null_frac not a float
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": "BADNULLFRAC",
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- exprs avg_width not an integer
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": "BADAVGWIDTH",
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- exprs n_dinstinct not a float
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": "BADNDISTINCT",
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- MCV not null, MCF null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- MCV not null, MCF missing
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- MCV null, MCF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": null,
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- MCV missing, MCF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- exprs most_common_vals element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{BADMCV}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- exprs most_common_freqs element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{BADMCF}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- exprs histogram wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{BADHIST,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- exprs correlation wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": "BADCORR",
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+-- ok: exprs
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test_clone',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_clone',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 4,
+                  "null_frac": 0,
+                  "n_distinct": -0.75,
+                  "correlation": -0.6,
+                  "histogram_bounds": "{-1,0}",
+                  "most_common_vals": "{1}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              },
+              {
+                  "avg_width": 4,
+                  "null_frac": 0.25,
+                  "n_distinct": -0.5,
+                  "correlation": 1,
+                  "histogram_bounds": null,
+                  "most_common_vals": "{2}",
+                  "most_common_elems": null,
+                  "most_common_freqs": "{0.5}",
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": null
+              }
+          ]'::jsonb);
+
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_stat_clone' AND
+    e.inherited = false
+\gx
+
+-- A statistics object for testing MCELEM values in expressions
+CREATE STATISTICS stats_import.test_stat_mcelem
+  ON name, (ARRAY[(comp).a, lower(arange)])
+  FROM stats_import.test;
+
+-- MCEV not null, MCEF null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": null
+              }
+            ]'::jsonb);
+-- MCEV not null, MCEF missing
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}"
+              }
+            ]'::jsonb);
+-- MCEV null, MCEF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": null,
+                  "most_common_freqs": null,
+                  "elem_count_histogram": null,
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+-- MCEV missing, MCEF not null
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_freqs": null,
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+-- exprs most_common_elems element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,BADELEM,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+-- exprs most_common_elem_freqs element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{BADELEMFREQ,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+-- exprs histogram bounds element wrong type
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{BADELEMHIST,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+-- ok: exprs mcelem
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}"
+              }
+            ]'::jsonb);
+
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_stat_mcelem' AND
+    e.inherited = false
+\gx
+
+-- ok, with warning: extra exprs param
+SELECT pg_catalog.pg_restore_extended_stats(
+  'schemaname', 'stats_import',
+  'relname', 'test',
+  'statistics_schemaname', 'stats_import',
+  'statistics_name', 'test_stat_mcelem',
+  'inherited', false,
+  'exprs', '[
+              {
+                  "avg_width": 33,
+                  "null_frac": 0,
+                  "n_distinct": -1,
+                  "correlation": 1,
+                  "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}",
+                  "most_common_vals": null,
+                  "most_common_elems": "{-1,0,1,2,3}",
+                  "most_common_freqs": null,
+                  "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}",
+                  "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}",
+                  "bad_param": "text no one will ever parse"
+              }
+            ]'::jsonb);
+
+SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals,
+       e.most_common_freqs, e.histogram_bounds, e.correlation,
+       e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram
+FROM pg_stats_ext_exprs AS e
+WHERE e.statistics_schemaname = 'stats_import' AND
+    e.statistics_name = 'test_stat_mcelem' AND
+    e.inherited = false
+\gx
+
 -- Test the ability of pg_restore_extended_stats() to import all of the
 -- statistic values from an extended statistic object that has been
 -- populated via a regular ANALYZE.  This checks after the statistics
diff --git a/doc/src/sgml/func/func-admin.sgml b/doc/src/sgml/func/func-admin.sgml
index 3ac81905d1f..bdfad1c8929 100644
--- a/doc/src/sgml/func/func-admin.sgml
+++ b/doc/src/sgml/func/func-admin.sgml
@@ -2198,12 +2198,14 @@ SELECT pg_restore_attribute_stats(
          <structname>myschema.mystatsobj</structname>:
 <programlisting>
  SELECT pg_restore_extended_stats(
-    'schemaname',            'tab_schema'::name,
-    'relname',               'tab_name'::name,
-    'statistics_schemaname', 'stats_schema'::name,
-    'statistics_name',       'stats_name'::name,
+    'schemaname',            'tab_schema',
+    'relname',               'tab_name',
+    'statistics_schemaname', 'stats_schema',
+    'statistics_name',       'stats_name',
     'inherited',             false,
     'n_distinct',            '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct);
+    'dependencies',          '{"2 => 1": 1.000000, "2 => -1": 1.000000, "2 => -2": 1.000000}'::pg_dependencies,
+    'exprs',                 '{{0,4,-0.75,"{1}","{0.5}","{-1,0}",-0.6,NULL,NULL,NULL},{0.25,4,-0.5,"{2}","{0.5}",NULL,1,NULL,NULL,NULL}}'::text[]);
 </programlisting>
         </para>
         <para>
@@ -2226,6 +2228,13 @@ SELECT pg_restore_attribute_stats(
          <literal>dependencies</literal>, <literal>most_common_vals</literal>,
          <literal>most_common_freqs</literal>,
          and <literal>most_common_base_freqs</literal>.
+         To accept statistics for any expressions in the extended
+         statistics object, the parameter <literal>exprs</literal> with a type
+         <type>text[]</type> is available, the array must be two dimensional with
+         an outer array in length equal to the number of expressions in the object,
+         and the inner array elements for each of the statistical columns in
+         <link linkend="view-pg-stats-ext-exprs"><structname>pg_stats_ext_exprs</structname></link>,
+         some of which are themselves arrays.
         </para>
         <para>
          Additionally, this function accepts argument name

base-commit: d46aa32ea5ce0c61a464cdc2c74fa9a428df8bc1
-- 
2.52.0

