Updated and rebase patches.

0001 is the same as v6-0002, but with proper ACL checks on schemas after
cache lookup

0002 attempts to replace all possible ERRORs in the restore/clear functions
with WARNINGs. This is done with an eye towards reducing the set of things
that could potentially cause an upgrade to fail.

Spoke with Nathan about how best to batch the pg_stats fetches. I'll be
working on that now. Given that, the patch that optimized out
getAttributeStats() calls on indexes without expressions has been
withdrawn. It's a clear incremental gain, and we're looking for a couple
orders of magnitude gain.
From 9cd4b4e0e280d0fd8cb120ac105d6e65a491cd7e Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huin...@gmail.com>
Date: Tue, 4 Mar 2025 22:16:52 -0500
Subject: [PATCH v7 1/2] Split relation into schemaname and relname.

In order to further reduce potential error-failures in restores and
upgrades, replace the numerous casts of fully qualified relation names
into their schema+relname text components.

Further remove the ::name casts on attname and change the expected
datatype to text.

Add an ACL_USAGE check on the namespace oid after it is looked up.
---
 src/include/catalog/pg_proc.dat            |   8 +-
 src/include/statistics/stat_utils.h        |   2 +
 src/backend/statistics/attribute_stats.c   |  87 ++++--
 src/backend/statistics/relation_stats.c    |  65 +++--
 src/backend/statistics/stat_utils.c        |  37 +++
 src/bin/pg_dump/pg_dump.c                  |  25 +-
 src/bin/pg_dump/t/002_pg_dump.pl           |   6 +-
 src/test/regress/expected/stats_import.out | 307 +++++++++++++--------
 src/test/regress/sql/stats_import.sql      | 276 +++++++++++-------
 doc/src/sgml/func.sgml                     |  41 +--
 10 files changed, 566 insertions(+), 288 deletions(-)

diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index cede992b6e2..fdd4b8d7dba 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12443,8 +12443,8 @@
   descr => 'clear statistics on relation',
   proname => 'pg_clear_relation_stats', provolatile => 'v', proisstrict => 'f',
   proparallel => 'u', prorettype => 'void',
-  proargtypes => 'regclass',
-  proargnames => '{relation}',
+  proargtypes => 'text text',
+  proargnames => '{schemaname,relname}',
   prosrc => 'pg_clear_relation_stats' },
 { oid => '8461',
   descr => 'restore statistics on attribute',
@@ -12459,8 +12459,8 @@
   descr => 'clear statistics on attribute',
   proname => 'pg_clear_attribute_stats', provolatile => 'v', proisstrict => 'f',
   proparallel => 'u', prorettype => 'void',
-  proargtypes => 'regclass name bool',
-  proargnames => '{relation,attname,inherited}',
+  proargtypes => 'text text text bool',
+  proargnames => '{schemaname,relname,attname,inherited}',
   prosrc => 'pg_clear_attribute_stats' },
 
 # GiST stratnum implementations
diff --git a/src/include/statistics/stat_utils.h b/src/include/statistics/stat_utils.h
index 0eb4decfcac..cad042c8e4a 100644
--- a/src/include/statistics/stat_utils.h
+++ b/src/include/statistics/stat_utils.h
@@ -32,6 +32,8 @@ extern bool stats_check_arg_pair(FunctionCallInfo fcinfo,
 
 extern void stats_lock_check_privileges(Oid reloid);
 
+extern Oid stats_schema_check_privileges(const char *nspname);
+
 extern bool stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
 											 FunctionCallInfo positional_fcinfo,
 											 struct StatsArgInfo *arginfo);
diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c
index 6bcbee0edba..f87db2d6102 100644
--- a/src/backend/statistics/attribute_stats.c
+++ b/src/backend/statistics/attribute_stats.c
@@ -36,7 +36,8 @@
 
 enum attribute_stats_argnum
 {
-	ATTRELATION_ARG = 0,
+	ATTRELSCHEMA_ARG = 0,
+	ATTRELNAME_ARG,
 	ATTNAME_ARG,
 	ATTNUM_ARG,
 	INHERITED_ARG,
@@ -58,8 +59,9 @@ enum attribute_stats_argnum
 
 static struct StatsArgInfo attarginfo[] =
 {
-	[ATTRELATION_ARG] = {"relation", REGCLASSOID},
-	[ATTNAME_ARG] = {"attname", NAMEOID},
+	[ATTRELSCHEMA_ARG] = {"schemaname", TEXTOID},
+	[ATTRELNAME_ARG] = {"relname", TEXTOID},
+	[ATTNAME_ARG] = {"attname", TEXTOID},
 	[ATTNUM_ARG] = {"attnum", INT2OID},
 	[INHERITED_ARG] = {"inherited", BOOLOID},
 	[NULL_FRAC_ARG] = {"null_frac", FLOAT4OID},
@@ -80,7 +82,8 @@ static struct StatsArgInfo attarginfo[] =
 
 enum clear_attribute_stats_argnum
 {
-	C_ATTRELATION_ARG = 0,
+	C_ATTRELSCHEMA_ARG = 0,
+	C_ATTRELNAME_ARG,
 	C_ATTNAME_ARG,
 	C_INHERITED_ARG,
 	C_NUM_ATTRIBUTE_STATS_ARGS
@@ -88,8 +91,9 @@ enum clear_attribute_stats_argnum
 
 static struct StatsArgInfo cleararginfo[] =
 {
-	[C_ATTRELATION_ARG] = {"relation", REGCLASSOID},
-	[C_ATTNAME_ARG] = {"attname", NAMEOID},
+	[C_ATTRELSCHEMA_ARG] = {"relation", TEXTOID},
+	[C_ATTRELNAME_ARG] = {"relation", TEXTOID},
+	[C_ATTNAME_ARG] = {"attname", TEXTOID},
 	[C_INHERITED_ARG] = {"inherited", BOOLOID},
 	[C_NUM_ATTRIBUTE_STATS_ARGS] = {0}
 };
@@ -133,6 +137,9 @@ static void init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
 static bool
 attribute_statistics_update(FunctionCallInfo fcinfo)
 {
+	char	   *nspname;
+	Oid			nspoid;
+	char	   *relname;
 	Oid			reloid;
 	char	   *attname;
 	AttrNumber	attnum;
@@ -170,8 +177,23 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 
 	bool		result = true;
 
-	stats_check_required_arg(fcinfo, attarginfo, ATTRELATION_ARG);
-	reloid = PG_GETARG_OID(ATTRELATION_ARG);
+	stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
+	stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
+
+	nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
+	nspoid = stats_schema_check_privileges(nspname);
+	if (nspoid == InvalidOid)
+		return false;
+
+	relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
+	reloid = get_relname_relid(relname, nspoid);
+	if (reloid == InvalidOid)
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("Relation \"%s\".\"%s\" not found.", nspname, relname)));
+		return false;
+	}
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -185,21 +207,18 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 	/* user can specify either attname or attnum, but not both */
 	if (!PG_ARGISNULL(ATTNAME_ARG))
 	{
-		Name		attnamename;
-
 		if (!PG_ARGISNULL(ATTNUM_ARG))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("cannot specify both attname and attnum")));
-		attnamename = PG_GETARG_NAME(ATTNAME_ARG);
-		attname = NameStr(*attnamename);
+		attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
 		attnum = get_attnum(reloid, attname);
 		/* note that this test covers attisdropped cases too: */
 		if (attnum == InvalidAttrNumber)
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
-					 errmsg("column \"%s\" of relation \"%s\" does not exist",
-							attname, get_rel_name(reloid))));
+					 errmsg("column \"%s\" of relation \"%s\".\"%s\" does not exist",
+							attname, nspname, relname)));
 	}
 	else if (!PG_ARGISNULL(ATTNUM_ARG))
 	{
@@ -210,8 +229,8 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 			!SearchSysCacheExistsAttName(reloid, attname))
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
-					 errmsg("column %d of relation \"%s\" does not exist",
-							attnum, get_rel_name(reloid))));
+					 errmsg("column %d of relation \"%s\".\"%s\" does not exist",
+							attnum, nspname, relname)));
 	}
 	else
 	{
@@ -900,13 +919,33 @@ init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
 Datum
 pg_clear_attribute_stats(PG_FUNCTION_ARGS)
 {
+	char	   *nspname;
+	Oid			nspoid;
+	char	   *relname;
 	Oid			reloid;
-	Name		attname;
+	char	   *attname;
 	AttrNumber	attnum;
 	bool		inherited;
 
-	stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELATION_ARG);
-	reloid = PG_GETARG_OID(C_ATTRELATION_ARG);
+	stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG);
+	stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG);
+	stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
+	stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
+
+	nspname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELSCHEMA_ARG));
+	nspoid = stats_schema_check_privileges(nspname);
+	if (!OidIsValid(nspoid))
+		return false;
+
+	relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
+	reloid = get_relname_relid(relname, nspoid);
+	if (!OidIsValid(reloid))
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("Relation \"%s\".\"%s\" not found.", nspname, relname)));
+		return false;
+	}
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -916,23 +955,21 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
 
 	stats_lock_check_privileges(reloid);
 
-	stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
-	attname = PG_GETARG_NAME(C_ATTNAME_ARG);
-	attnum = get_attnum(reloid, NameStr(*attname));
+	attname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTNAME_ARG));
+	attnum = get_attnum(reloid, attname);
 
 	if (attnum < 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot clear statistics on system column \"%s\"",
-						NameStr(*attname))));
+						attname)));
 
 	if (attnum == InvalidAttrNumber)
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_COLUMN),
 				 errmsg("column \"%s\" of relation \"%s\" does not exist",
-						NameStr(*attname), get_rel_name(reloid))));
+						attname, get_rel_name(reloid))));
 
-	stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
 	inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
 
 	delete_pg_statistic(reloid, attnum, inherited);
diff --git a/src/backend/statistics/relation_stats.c b/src/backend/statistics/relation_stats.c
index 52dfa477187..fdc69bc93e2 100644
--- a/src/backend/statistics/relation_stats.c
+++ b/src/backend/statistics/relation_stats.c
@@ -19,9 +19,12 @@
 
 #include "access/heapam.h"
 #include "catalog/indexing.h"
+#include "catalog/namespace.h"
 #include "statistics/stat_utils.h"
+#include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/fmgrprotos.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -32,7 +35,8 @@
 
 enum relation_stats_argnum
 {
-	RELATION_ARG = 0,
+	RELSCHEMA_ARG = 0,
+	RELNAME_ARG,
 	RELPAGES_ARG,
 	RELTUPLES_ARG,
 	RELALLVISIBLE_ARG,
@@ -42,7 +46,8 @@ enum relation_stats_argnum
 
 static struct StatsArgInfo relarginfo[] =
 {
-	[RELATION_ARG] = {"relation", REGCLASSOID},
+	[RELSCHEMA_ARG] = {"schemaname", TEXTOID},
+	[RELNAME_ARG] = {"relname", TEXTOID},
 	[RELPAGES_ARG] = {"relpages", INT4OID},
 	[RELTUPLES_ARG] = {"reltuples", FLOAT4OID},
 	[RELALLVISIBLE_ARG] = {"relallvisible", INT4OID},
@@ -59,6 +64,9 @@ static bool
 relation_statistics_update(FunctionCallInfo fcinfo)
 {
 	bool		result = true;
+	char	   *nspname;
+	Oid			nspoid;
+	char	   *relname;
 	Oid			reloid;
 	Relation	crel;
 	BlockNumber relpages = 0;
@@ -76,6 +84,32 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 	bool		nulls[4] = {0};
 	int			nreplaces = 0;
 
+	stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG);
+	stats_check_required_arg(fcinfo, relarginfo, RELNAME_ARG);
+
+	nspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
+	nspoid = stats_schema_check_privileges(nspname);
+	if (!OidIsValid(nspoid))
+		return false;
+
+	relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
+	reloid = get_relname_relid(relname, nspoid);
+	if (!OidIsValid(reloid))
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("Relation \"%s\".\"%s\" not found.", nspname, relname)));
+		return false;
+	}
+
+	if (RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("recovery is in progress"),
+				 errhint("Statistics cannot be modified during recovery.")));
+
+	stats_lock_check_privileges(reloid);
+
 	if (!PG_ARGISNULL(RELPAGES_ARG))
 	{
 		relpages = PG_GETARG_UINT32(RELPAGES_ARG);
@@ -108,17 +142,6 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 		update_relallfrozen = true;
 	}
 
-	stats_check_required_arg(fcinfo, relarginfo, RELATION_ARG);
-	reloid = PG_GETARG_OID(RELATION_ARG);
-
-	if (RecoveryInProgress())
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("recovery is in progress"),
-				 errhint("Statistics cannot be modified during recovery.")));
-
-	stats_lock_check_privileges(reloid);
-
 	/*
 	 * Take RowExclusiveLock on pg_class, consistent with
 	 * vac_update_relstats().
@@ -187,20 +210,22 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 Datum
 pg_clear_relation_stats(PG_FUNCTION_ARGS)
 {
-	LOCAL_FCINFO(newfcinfo, 5);
+	LOCAL_FCINFO(newfcinfo, 6);
 
-	InitFunctionCallInfoData(*newfcinfo, NULL, 5, InvalidOid, NULL, NULL);
+	InitFunctionCallInfoData(*newfcinfo, NULL, 6, InvalidOid, NULL, NULL);
 
-	newfcinfo->args[0].value = PG_GETARG_OID(0);
+	newfcinfo->args[0].value = PG_GETARG_DATUM(0);
 	newfcinfo->args[0].isnull = PG_ARGISNULL(0);
-	newfcinfo->args[1].value = UInt32GetDatum(0);
-	newfcinfo->args[1].isnull = false;
-	newfcinfo->args[2].value = Float4GetDatum(-1.0);
+	newfcinfo->args[1].value = PG_GETARG_DATUM(1);
+	newfcinfo->args[1].isnull = PG_ARGISNULL(1);
+	newfcinfo->args[2].value = UInt32GetDatum(0);
 	newfcinfo->args[2].isnull = false;
-	newfcinfo->args[3].value = UInt32GetDatum(0);
+	newfcinfo->args[3].value = Float4GetDatum(-1.0);
 	newfcinfo->args[3].isnull = false;
 	newfcinfo->args[4].value = UInt32GetDatum(0);
 	newfcinfo->args[4].isnull = false;
+	newfcinfo->args[5].value = UInt32GetDatum(0);
+	newfcinfo->args[5].isnull = false;
 
 	relation_statistics_update(newfcinfo);
 	PG_RETURN_VOID();
diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c
index 9647f5108b3..e037d4994e8 100644
--- a/src/backend/statistics/stat_utils.c
+++ b/src/backend/statistics/stat_utils.c
@@ -18,7 +18,9 @@
 
 #include "access/relation.h"
 #include "catalog/index.h"
+#include "catalog/namespace.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_namespace.h"
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "statistics/stat_utils.h"
@@ -213,6 +215,41 @@ stats_lock_check_privileges(Oid reloid)
 	relation_close(table, NoLock);
 }
 
+
+/*
+ * Resolve a schema name into an Oid, ensure that the user has usage privs on
+ * that schema.
+ */
+Oid
+stats_schema_check_privileges(const char *nspname)
+{
+	Oid			nspoid;
+	AclResult	aclresult;
+
+	nspoid = get_namespace_oid(nspname, true);
+
+	if (nspoid == InvalidOid)
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_INVALID_SCHEMA_NAME),
+				 errmsg("schema %s does not exist", nspname)));
+		return InvalidOid;
+	}
+
+	aclresult = object_aclcheck(NamespaceRelationId, nspoid, GetUserId(), ACL_USAGE);
+
+	if (aclresult != ACLCHECK_OK)
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for schema %s", nspname)));
+		return InvalidOid;
+	}
+
+	return nspoid;
+}
+
+
 /*
  * Find the argument number for the given argument name, returning -1 if not
  * found.
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4f4ad2ee150..6cf2c7d1fe4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10492,7 +10492,6 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 	PQExpBuffer out;
 	DumpId	   *deps = NULL;
 	int			ndeps = 0;
-	char	   *qualified_name;
 	char		reltuples_str[FLOAT_SHORTEST_DECIMAL_LEN];
 	int			i_attname;
 	int			i_inherited;
@@ -10558,15 +10557,16 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 
 	out = createPQExpBuffer();
 
-	qualified_name = pg_strdup(fmtQualifiedDumpable(rsinfo));
-
 	/* restore relation stats */
 	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
 	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
 					  fout->remoteVersion);
-	appendPQExpBufferStr(out, "\t'relation', ");
-	appendStringLiteralAH(out, qualified_name, fout);
-	appendPQExpBufferStr(out, "::regclass,\n");
+	appendPQExpBufferStr(out, "\t'schemaname', ");
+	appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
+	appendPQExpBufferStr(out, ",\n");
+	appendPQExpBufferStr(out, "\t'relname', ");
+	appendStringLiteralAH(out, rsinfo->dobj.name, fout);
+	appendPQExpBufferStr(out, ",\n");
 	appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
 	float_to_shortest_decimal_buf(rsinfo->reltuples, reltuples_str);
 	appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", reltuples_str);
@@ -10606,9 +10606,10 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
 		appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
 						  fout->remoteVersion);
-		appendPQExpBufferStr(out, "\t'relation', ");
-		appendStringLiteralAH(out, qualified_name, fout);
-		appendPQExpBufferStr(out, "::regclass");
+		appendPQExpBufferStr(out, "\t'schemaname', ");
+		appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
+		appendPQExpBufferStr(out, ",\n\t'relname', ");
+		appendStringLiteralAH(out, rsinfo->dobj.name, fout);
 
 		if (PQgetisnull(res, rownum, i_attname))
 			pg_fatal("attname cannot be NULL");
@@ -10620,7 +10621,10 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		 * their attnames are not necessarily stable across dump/reload.
 		 */
 		if (rsinfo->nindAttNames == 0)
-			appendNamedArgument(out, fout, "attname", "name", attname);
+		{
+			appendPQExpBuffer(out, ",\n\t'attname', ");
+			appendStringLiteralAH(out, attname, fout);
+		}
 		else
 		{
 			bool		found = false;
@@ -10700,7 +10704,6 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 							  .deps = deps,
 							  .nDeps = ndeps));
 
-	free(qualified_name);
 	destroyPQExpBuffer(out);
 	destroyPQExpBuffer(query);
 }
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index c7bffc1b045..b037f239136 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4725,14 +4725,16 @@ my %tests = (
 		regexp => qr/^
 			\QSELECT * FROM pg_catalog.pg_restore_relation_stats(\E\s+
 			'version',\s'\d+'::integer,\s+
-			'relation',\s'dump_test.dup_test_post_data_ix'::regclass,\s+
+			'schemaname',\s'dump_test',\s+
+			'relname',\s'dup_test_post_data_ix',\s+
 			'relpages',\s'\d+'::integer,\s+
 			'reltuples',\s'\d+'::real,\s+
 			'relallvisible',\s'\d+'::integer\s+
 			\);\s+
 			\QSELECT * FROM pg_catalog.pg_restore_attribute_stats(\E\s+
 			'version',\s'\d+'::integer,\s+
-			'relation',\s'dump_test.dup_test_post_data_ix'::regclass,\s+
+			'schemaname',\s'dump_test',\s+
+			'relname',\s'dup_test_post_data_ix',\s+
 			'attnum',\s'2'::smallint,\s+
 			'inherited',\s'f'::boolean,\s+
 			'null_frac',\s'0'::real,\s+
diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out
index 1f46d5e7854..2f1295f2149 100644
--- a/src/test/regress/expected/stats_import.out
+++ b/src/test/regress/expected/stats_import.out
@@ -14,7 +14,8 @@ CREATE TABLE stats_import.test(
 ) WITH (autovacuum_enabled = false);
 SELECT
     pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', 18::integer,
         'reltuples', 21::real,
         'relallvisible', 24::integer,
@@ -36,7 +37,7 @@ ORDER BY relname;
  test    |       18 |        21 |            24 |           27
 (1 row)
 
-SELECT pg_clear_relation_stats('stats_import.test'::regclass);
+SELECT pg_clear_relation_stats('stats_import', 'test');
  pg_clear_relation_stats 
 -------------------------
  
@@ -45,33 +46,54 @@ SELECT pg_clear_relation_stats('stats_import.test'::regclass);
 --
 -- relstats tests
 --
---- error: relation is wrong type
+-- error: schemaname missing
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 0::oid,
+        'relname', 'test',
         'relpages', 17::integer);
-WARNING:  argument "relation" has type "oid", expected type "regclass"
-ERROR:  "relation" cannot be NULL
+ERROR:  "schemaname" cannot be NULL
+-- error: relname missing
+SELECT pg_catalog.pg_restore_relation_stats(
+        'schemaname', 'stats_import',
+        'relpages', 17::integer);
+ERROR:  "relname" cannot be NULL
+--- error: schemaname is wrong type
+SELECT pg_catalog.pg_restore_relation_stats(
+        'schemaname', 3.6::float,
+        'relname', 'test',
+        'relpages', 17::integer);
+WARNING:  argument "schemaname" has type "double precision", expected type "text"
+ERROR:  "schemaname" cannot be NULL
+--- error: relname is wrong type
+SELECT pg_catalog.pg_restore_relation_stats(
+        'schemaname', 'stats_import',
+        'relname', 0::oid,
+        'relpages', 17::integer);
+WARNING:  argument "relname" has type "oid", expected type "text"
+ERROR:  "relname" cannot be NULL
 -- error: relation not found
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 0::oid::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'nope',
         'relpages', 17::integer);
-ERROR:  could not open relation with OID 0
+WARNING:  Relation "stats_import"."nope" not found.
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
 -- error: odd number of variadic arguments cannot be pairs
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relallvisible');
 ERROR:  variadic arguments must be name/value pairs
 HINT:  Provide an even number of variadic arguments that can be divided into pairs.
 -- error: argument name is NULL
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         NULL, '17'::integer);
-ERROR:  name at variadic position 3 is NULL
--- error: argument name is not a text type
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        17, '17'::integer);
-ERROR:  name at variadic position 3 has type "integer", expected type "text"
+ERROR:  name at variadic position 5 is NULL
 -- starting stats
 SELECT relpages, reltuples, relallvisible, relallfrozen
 FROM pg_class
@@ -84,7 +106,8 @@ WHERE oid = 'stats_import.test_i'::regclass;
 -- regular indexes have special case locking rules
 BEGIN;
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.test_i'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test_i',
         'relpages', 18::integer);
  pg_restore_relation_stats 
 ---------------------------
@@ -132,7 +155,8 @@ WHERE oid = 'stats_import.part_parent'::regclass;
 --
 BEGIN;
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.part_parent_i'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'part_parent_i',
         'relpages', 2::integer);
  pg_restore_relation_stats 
 ---------------------------
@@ -166,7 +190,8 @@ WHERE oid = 'stats_import.part_parent_i'::regclass;
 
 -- ok: set all relstats, with version, no bounds checking
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'version', 150000::integer,
         'relpages', '-17'::integer,
         'reltuples', 400::real,
@@ -187,7 +212,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: set just relpages, rest stay same
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', '16'::integer);
  pg_restore_relation_stats 
 ---------------------------
@@ -204,7 +230,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: set just reltuples, rest stay same
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'reltuples', '500'::real);
  pg_restore_relation_stats 
 ---------------------------
@@ -221,7 +248,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: set just relallvisible, rest stay same
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relallvisible', 5::integer);
  pg_restore_relation_stats 
 ---------------------------
@@ -238,7 +266,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: just relallfrozen
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'version', 150000::integer,
         'relallfrozen', 3::integer);
  pg_restore_relation_stats 
@@ -256,7 +285,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- warn: bad relpages type, rest updated
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', 'nope'::text,
         'reltuples', 400.0::real,
         'relallvisible', 4::integer,
@@ -277,7 +307,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- unrecognized argument name, rest ok
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', '171'::integer,
         'nope', 10::integer);
 WARNING:  unrecognized argument name: "nope"
@@ -295,8 +326,7 @@ WHERE oid = 'stats_import.test'::regclass;
 (1 row)
 
 -- ok: clear stats
-SELECT pg_catalog.pg_clear_relation_stats(
-    relation => 'stats_import.test'::regclass);
+SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'test');
  pg_clear_relation_stats 
 -------------------------
  
@@ -313,87 +343,123 @@ WHERE oid = 'stats_import.test'::regclass;
 -- invalid relkinds for statistics
 CREATE SEQUENCE stats_import.testseq;
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.testseq'::regclass);
+        'schemaname', 'stats_import',
+        'relname', 'testseq');
 ERROR:  cannot modify statistics for relation "testseq"
 DETAIL:  This operation is not supported for sequences.
-SELECT pg_catalog.pg_clear_relation_stats(
-        'stats_import.testseq'::regclass);
+SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testseq');
 ERROR:  cannot modify statistics for relation "testseq"
 DETAIL:  This operation is not supported for sequences.
 CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test;
-SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.testview'::regclass);
-ERROR:  cannot modify statistics for relation "testview"
-DETAIL:  This operation is not supported for views.
-SELECT pg_catalog.pg_clear_relation_stats(
-        'stats_import.testview'::regclass);
+SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testview');
 ERROR:  cannot modify statistics for relation "testview"
 DETAIL:  This operation is not supported for views.
 --
 -- attribute stats
 --
--- error: object does not exist
+-- error: schemaname missing
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', '0'::oid::regclass,
-    'attname', 'id'::name,
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  could not open relation with OID 0
--- error: relation null
+ERROR:  "schemaname" cannot be NULL
+-- error: schema does not exist
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', NULL::oid::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'nope',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  "relation" cannot be NULL
+WARNING:  schema nope does not exist
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- error: relname missing
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'stats_import',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+ERROR:  "relname" cannot be NULL
+-- error: relname does not exist
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'stats_import',
+    'relname', 'nope',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+WARNING:  Relation "stats_import"."nope" not found.
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- error: relname null
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'stats_import',
+    'relname', NULL,
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+ERROR:  "relname" cannot be NULL
 -- error: NULL attname
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', NULL::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', NULL,
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 ERROR:  must specify either attname or attnum
 -- error: attname doesn't exist
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'nope'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'nope',
     'inherited', false::boolean,
     'null_frac', 0.1::real,
     'avg_width', 2::integer,
     'n_distinct', 0.3::real);
-ERROR:  column "nope" of relation "test" does not exist
+ERROR:  column "nope" of relation "stats_import"."test" does not exist
 -- error: both attname and attnum
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'attnum', 1::smallint,
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 ERROR:  cannot specify both attname and attnum
 -- error: neither attname nor attnum
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
+    'schemaname', 'stats_import',
+    'relname', 'test',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 ERROR:  must specify either attname or attnum
 -- error: attribute is system column
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'xmin'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'xmin',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 ERROR:  cannot modify statistics on system column "xmin"
 -- error: inherited null
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', NULL::boolean,
     'null_frac', 0.1::real);
 ERROR:  "inherited" cannot be NULL
 -- ok: just the fixed values, with version, no stakinds
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'version', 150000::integer,
     'null_frac', 0.2::real,
@@ -421,7 +487,8 @@ AND attname = 'id';
 -- for any stat-having relation.
 --
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
+    'schemaname', 'stats_import',
+    'relname', 'test',
     'attnum', 1::smallint,
     'inherited', false::boolean,
     'null_frac', 0.4::real);
@@ -443,8 +510,9 @@ AND attname = 'id';
 
 -- warn: unrecognized argument name, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.2::real,
     'nope', 0.5::real);
@@ -467,8 +535,9 @@ AND attname = 'id';
 
 -- warn: mcv / mcf null mismatch part 1, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.21::real,
     'most_common_freqs', '{0.1,0.2,0.3}'::real[]
@@ -492,8 +561,9 @@ AND attname = 'id';
 
 -- warn: mcv / mcf null mismatch part 2, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.21::real,
     'most_common_vals', '{1,2,3}'::text
@@ -517,8 +587,9 @@ AND attname = 'id';
 
 -- warn: mcf type mismatch, mcv-pair fails, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.22::real,
     'most_common_vals', '{2,1,3}'::text,
@@ -544,8 +615,9 @@ AND attname = 'id';
 
 -- warn: mcv cast failure, mcv-pair fails, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.23::real,
     'most_common_vals', '{2,four,3}'::text,
@@ -570,8 +642,9 @@ AND attname = 'id';
 
 -- ok: mcv+mcf
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'most_common_vals', '{2,1,3}'::text,
     'most_common_freqs', '{0.3,0.25,0.05}'::real[]
@@ -594,8 +667,9 @@ AND attname = 'id';
 
 -- warn: NULL in histogram array, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.24::real,
     'histogram_bounds', '{1,NULL,3,4}'::text
@@ -619,8 +693,9 @@ AND attname = 'id';
 
 -- ok: histogram_bounds
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'histogram_bounds', '{1,2,3,4}'::text
     );
@@ -642,8 +717,9 @@ AND attname = 'id';
 
 -- warn: elem_count_histogram null element, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.25::real,
     'elem_count_histogram', '{1,1,NULL,1,1,1,1,1}'::real[]
@@ -667,8 +743,9 @@ AND attname = 'tags';
 
 -- ok: elem_count_histogram
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.26::real,
     '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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}'::real[]
@@ -691,8 +768,9 @@ AND attname = 'tags';
 
 -- warn: range stats on a scalar type, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.27::real,
     'range_empty_frac', 0.5::real,
@@ -718,8 +796,9 @@ AND attname = 'id';
 
 -- warn: range_empty_frac range_length_hist null mismatch, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'null_frac', 0.28::real,
     'range_length_histogram', '{399,499,Infinity}'::text
@@ -743,8 +822,9 @@ AND attname = 'arange';
 
 -- warn: range_empty_frac range_length_hist null mismatch part 2, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'null_frac', 0.29::real,
     'range_empty_frac', 0.5::real
@@ -768,8 +848,9 @@ AND attname = 'arange';
 
 -- ok: range_empty_frac + range_length_hist
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'range_empty_frac', 0.5::real,
     'range_length_histogram', '{399,499,Infinity}'::text
@@ -792,8 +873,9 @@ AND attname = 'arange';
 
 -- warn: range bounds histogram on scalar, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.31::real,
     'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
@@ -818,8 +900,9 @@ AND attname = 'id';
 
 -- ok: range_bounds_histogram
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
     );
@@ -841,8 +924,9 @@ AND attname = 'arange';
 
 -- warn: cannot set most_common_elems for range type, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'null_frac', 0.32::real,
     'most_common_elems', '{3,1}'::text,
@@ -868,8 +952,9 @@ AND attname = 'arange';
 
 -- warn: scalars can't have mcelem, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.33::real,
     'most_common_elems', '{1,3}'::text,
@@ -895,8 +980,9 @@ AND attname = 'id';
 
 -- warn: mcelem / mcelem mismatch, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.34::real,
     'most_common_elems', '{one,two}'::text
@@ -920,8 +1006,9 @@ AND attname = 'tags';
 
 -- warn: mcelem / mcelem null mismatch part 2, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.35::real,
     'most_common_elem_freqs', '{0.3,0.2,0.2,0.3}'::real[]
@@ -945,8 +1032,9 @@ AND attname = 'tags';
 
 -- ok: mcelem
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'most_common_elems', '{one,three}'::text,
     'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[]
@@ -969,8 +1057,9 @@ AND attname = 'tags';
 
 -- warn: scalars can't have elem_count_histogram, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.36::real,
     'elem_count_histogram', '{1,1,1,1,1,1,1,1,1,1}'::real[]
@@ -1022,8 +1111,9 @@ SELECT s.schemaname, s.tablename, s.attname, s.inherited, r.*
 FROM pg_catalog.pg_stats AS s
 CROSS JOIN LATERAL
     pg_catalog.pg_restore_attribute_stats(
-        'relation', ('stats_import.' || s.tablename || '_clone')::regclass,
-        'attname', s.attname,
+        'schemaname', 'stats_import',
+        'relname', s.tablename::text || '_clone',
+        'attname', s.attname::text,
         'inherited', s.inherited,
         'version', 150000,
         'null_frac', s.null_frac,
@@ -1200,9 +1290,10 @@ AND attname = 'arange';
 (1 row)
 
 SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean);
+    schemaname => 'stats_import',
+    relname => 'test',
+    attname => 'arange',
+    inherited => false);
  pg_clear_attribute_stats 
 --------------------------
  
diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql
index 0ec590688c2..ccdc44e9236 100644
--- a/src/test/regress/sql/stats_import.sql
+++ b/src/test/regress/sql/stats_import.sql
@@ -17,7 +17,8 @@ CREATE TABLE stats_import.test(
 
 SELECT
     pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', 18::integer,
         'reltuples', 21::real,
         'relallvisible', 24::integer,
@@ -32,37 +33,52 @@ FROM pg_class
 WHERE oid = 'stats_import.test'::regclass
 ORDER BY relname;
 
-SELECT pg_clear_relation_stats('stats_import.test'::regclass);
+SELECT pg_clear_relation_stats('stats_import', 'test');
 
 --
 -- relstats tests
 --
 
---- error: relation is wrong type
+-- error: schemaname missing
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 0::oid,
+        'relname', 'test',
+        'relpages', 17::integer);
+
+-- error: relname missing
+SELECT pg_catalog.pg_restore_relation_stats(
+        'schemaname', 'stats_import',
+        'relpages', 17::integer);
+
+--- error: schemaname is wrong type
+SELECT pg_catalog.pg_restore_relation_stats(
+        'schemaname', 3.6::float,
+        'relname', 'test',
+        'relpages', 17::integer);
+
+--- error: relname is wrong type
+SELECT pg_catalog.pg_restore_relation_stats(
+        'schemaname', 'stats_import',
+        'relname', 0::oid,
         'relpages', 17::integer);
 
 -- error: relation not found
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 0::oid::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'nope',
         'relpages', 17::integer);
 
 -- error: odd number of variadic arguments cannot be pairs
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relallvisible');
 
 -- error: argument name is NULL
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         NULL, '17'::integer);
 
--- error: argument name is not a text type
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        17, '17'::integer);
-
 -- starting stats
 SELECT relpages, reltuples, relallvisible, relallfrozen
 FROM pg_class
@@ -71,7 +87,8 @@ WHERE oid = 'stats_import.test_i'::regclass;
 -- regular indexes have special case locking rules
 BEGIN;
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.test_i'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test_i',
         'relpages', 18::integer);
 
 SELECT mode FROM pg_locks
@@ -108,7 +125,8 @@ WHERE oid = 'stats_import.part_parent'::regclass;
 BEGIN;
 
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.part_parent_i'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'part_parent_i',
         'relpages', 2::integer);
 
 SELECT mode FROM pg_locks
@@ -127,7 +145,8 @@ WHERE oid = 'stats_import.part_parent_i'::regclass;
 
 -- ok: set all relstats, with version, no bounds checking
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'version', 150000::integer,
         'relpages', '-17'::integer,
         'reltuples', 400::real,
@@ -140,7 +159,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: set just relpages, rest stay same
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', '16'::integer);
 
 SELECT relpages, reltuples, relallvisible, relallfrozen
@@ -149,7 +169,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: set just reltuples, rest stay same
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'reltuples', '500'::real);
 
 SELECT relpages, reltuples, relallvisible, relallfrozen
@@ -158,7 +179,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: set just relallvisible, rest stay same
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relallvisible', 5::integer);
 
 SELECT relpages, reltuples, relallvisible, relallfrozen
@@ -167,7 +189,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: just relallfrozen
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'version', 150000::integer,
         'relallfrozen', 3::integer);
 
@@ -177,7 +200,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- warn: bad relpages type, rest updated
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', 'nope'::text,
         'reltuples', 400.0::real,
         'relallvisible', 4::integer,
@@ -189,7 +213,8 @@ WHERE oid = 'stats_import.test'::regclass;
 
 -- unrecognized argument name, rest ok
 SELECT pg_restore_relation_stats(
-        'relation', 'stats_import.test'::regclass,
+        'schemaname', 'stats_import',
+        'relname', 'test',
         'relpages', '171'::integer,
         'nope', 10::integer);
 
@@ -198,8 +223,7 @@ FROM pg_class
 WHERE oid = 'stats_import.test'::regclass;
 
 -- ok: clear stats
-SELECT pg_catalog.pg_clear_relation_stats(
-    relation => 'stats_import.test'::regclass);
+SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'test');
 
 SELECT relpages, reltuples, relallvisible
 FROM pg_class
@@ -209,48 +233,70 @@ WHERE oid = 'stats_import.test'::regclass;
 CREATE SEQUENCE stats_import.testseq;
 
 SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.testseq'::regclass);
+        'schemaname', 'stats_import',
+        'relname', 'testseq');
 
-SELECT pg_catalog.pg_clear_relation_stats(
-        'stats_import.testseq'::regclass);
+SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testseq');
 
 CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test;
 
-SELECT pg_catalog.pg_restore_relation_stats(
-        'relation', 'stats_import.testview'::regclass);
-
-SELECT pg_catalog.pg_clear_relation_stats(
-        'stats_import.testview'::regclass);
+SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testview');
 
 --
 -- attribute stats
 --
 
--- error: object does not exist
+-- error: schemaname missing
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', '0'::oid::regclass,
-    'attname', 'id'::name,
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: relation null
+-- error: schema does not exist
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', NULL::oid::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'nope',
+    'relname', 'test',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+
+-- error: relname missing
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'stats_import',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+
+-- error: relname does not exist
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'stats_import',
+    'relname', 'nope',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+
+-- error: relname null
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'stats_import',
+    'relname', NULL,
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
 -- error: NULL attname
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', NULL::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', NULL,
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
 -- error: attname doesn't exist
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'nope'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'nope',
     'inherited', false::boolean,
     'null_frac', 0.1::real,
     'avg_width', 2::integer,
@@ -258,36 +304,41 @@ SELECT pg_catalog.pg_restore_attribute_stats(
 
 -- error: both attname and attnum
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'attnum', 1::smallint,
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
 -- error: neither attname nor attnum
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
+    'schemaname', 'stats_import',
+    'relname', 'test',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
 -- error: attribute is system column
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'xmin'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'xmin',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
 -- error: inherited null
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', NULL::boolean,
     'null_frac', 0.1::real);
 
 -- ok: just the fixed values, with version, no stakinds
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'version', 150000::integer,
     'null_frac', 0.2::real,
@@ -307,7 +358,8 @@ AND attname = 'id';
 -- for any stat-having relation.
 --
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
+    'schemaname', 'stats_import',
+    'relname', 'test',
     'attnum', 1::smallint,
     'inherited', false::boolean,
     'null_frac', 0.4::real);
@@ -321,8 +373,9 @@ AND attname = 'id';
 
 -- warn: unrecognized argument name, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.2::real,
     'nope', 0.5::real);
@@ -336,8 +389,9 @@ AND attname = 'id';
 
 -- warn: mcv / mcf null mismatch part 1, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.21::real,
     'most_common_freqs', '{0.1,0.2,0.3}'::real[]
@@ -352,8 +406,9 @@ AND attname = 'id';
 
 -- warn: mcv / mcf null mismatch part 2, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.21::real,
     'most_common_vals', '{1,2,3}'::text
@@ -368,8 +423,9 @@ AND attname = 'id';
 
 -- warn: mcf type mismatch, mcv-pair fails, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.22::real,
     'most_common_vals', '{2,1,3}'::text,
@@ -385,8 +441,9 @@ AND attname = 'id';
 
 -- warn: mcv cast failure, mcv-pair fails, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.23::real,
     'most_common_vals', '{2,four,3}'::text,
@@ -402,8 +459,9 @@ AND attname = 'id';
 
 -- ok: mcv+mcf
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'most_common_vals', '{2,1,3}'::text,
     'most_common_freqs', '{0.3,0.25,0.05}'::real[]
@@ -418,8 +476,9 @@ AND attname = 'id';
 
 -- warn: NULL in histogram array, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.24::real,
     'histogram_bounds', '{1,NULL,3,4}'::text
@@ -434,8 +493,9 @@ AND attname = 'id';
 
 -- ok: histogram_bounds
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'histogram_bounds', '{1,2,3,4}'::text
     );
@@ -449,8 +509,9 @@ AND attname = 'id';
 
 -- warn: elem_count_histogram null element, rest get set
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.25::real,
     'elem_count_histogram', '{1,1,NULL,1,1,1,1,1}'::real[]
@@ -465,8 +526,9 @@ AND attname = 'tags';
 
 -- ok: elem_count_histogram
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.26::real,
     '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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}'::real[]
@@ -481,8 +543,9 @@ AND attname = 'tags';
 
 -- warn: range stats on a scalar type, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.27::real,
     'range_empty_frac', 0.5::real,
@@ -498,8 +561,9 @@ AND attname = 'id';
 
 -- warn: range_empty_frac range_length_hist null mismatch, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'null_frac', 0.28::real,
     'range_length_histogram', '{399,499,Infinity}'::text
@@ -514,8 +578,9 @@ AND attname = 'arange';
 
 -- warn: range_empty_frac range_length_hist null mismatch part 2, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'null_frac', 0.29::real,
     'range_empty_frac', 0.5::real
@@ -530,8 +595,9 @@ AND attname = 'arange';
 
 -- ok: range_empty_frac + range_length_hist
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'range_empty_frac', 0.5::real,
     'range_length_histogram', '{399,499,Infinity}'::text
@@ -546,8 +612,9 @@ AND attname = 'arange';
 
 -- warn: range bounds histogram on scalar, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.31::real,
     'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
@@ -562,8 +629,9 @@ AND attname = 'id';
 
 -- ok: range_bounds_histogram
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
     );
@@ -577,8 +645,9 @@ AND attname = 'arange';
 
 -- warn: cannot set most_common_elems for range type, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'arange'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'arange',
     'inherited', false::boolean,
     'null_frac', 0.32::real,
     'most_common_elems', '{3,1}'::text,
@@ -594,8 +663,9 @@ AND attname = 'arange';
 
 -- warn: scalars can't have mcelem, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.33::real,
     'most_common_elems', '{1,3}'::text,
@@ -611,8 +681,9 @@ AND attname = 'id';
 
 -- warn: mcelem / mcelem mismatch, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.34::real,
     'most_common_elems', '{one,two}'::text
@@ -627,8 +698,9 @@ AND attname = 'tags';
 
 -- warn: mcelem / mcelem null mismatch part 2, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'null_frac', 0.35::real,
     'most_common_elem_freqs', '{0.3,0.2,0.2,0.3}'::real[]
@@ -643,8 +715,9 @@ AND attname = 'tags';
 
 -- ok: mcelem
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'tags'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'tags',
     'inherited', false::boolean,
     'most_common_elems', '{one,three}'::text,
     'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[]
@@ -659,8 +732,9 @@ AND attname = 'tags';
 
 -- warn: scalars can't have elem_count_histogram, rest ok
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.36::real,
     'elem_count_histogram', '{1,1,1,1,1,1,1,1,1,1}'::real[]
@@ -707,8 +781,9 @@ SELECT s.schemaname, s.tablename, s.attname, s.inherited, r.*
 FROM pg_catalog.pg_stats AS s
 CROSS JOIN LATERAL
     pg_catalog.pg_restore_attribute_stats(
-        'relation', ('stats_import.' || s.tablename || '_clone')::regclass,
-        'attname', s.attname,
+        'schemaname', 'stats_import',
+        'relname', s.tablename::text || '_clone',
+        'attname', s.attname::text,
         'inherited', s.inherited,
         'version', 150000,
         'null_frac', s.null_frac,
@@ -853,9 +928,10 @@ AND inherited = false
 AND attname = 'arange';
 
 SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean);
+    schemaname => 'stats_import',
+    relname => 'test',
+    attname => 'arange',
+    inherited => false);
 
 SELECT COUNT(*)
 FROM pg_stats
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 51dd8ad6571..63a260a8ff8 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -30348,22 +30348,24 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
          <structname>mytable</structname>:
 <programlisting>
  SELECT pg_restore_relation_stats(
-    'relation',  'mytable'::regclass,
-    'relpages',  173::integer,
-    'reltuples', 10000::real);
+    'schemaname', 'myschema',
+    'relname',    'mytable',
+    'relpages',   173::integer,
+    'reltuples',  10000::real);
 </programlisting>
         </para>
         <para>
-         The argument <literal>relation</literal> with a value of type
-         <type>regclass</type> is required, and specifies the table. Other
-         arguments are the names and values of statistics corresponding to
-         certain columns in <link
+         The arguments <literal>schemaname</literal> with a value of type
+         <type>regclass</type> and <literal>relname</literal> are required,
+         and specifies the table. Other arguments are the names and values
+         of statistics corresponding to certain columns in <link
          linkend="catalog-pg-class"><structname>pg_class</structname></link>.
          The currently-supported relation statistics are
          <literal>relpages</literal> with a value of type
          <type>integer</type>, <literal>reltuples</literal> with a value of
-         type <type>real</type>, and <literal>relallvisible</literal> with a
-         value of type <type>integer</type>.
+         type <type>real</type>, <literal>relallvisible</literal> with a
+         value of type <type>integer</type>, and <literal>relallfrozen</literal>
+         with a value of type <type>integer</type>.
         </para>
         <para>
          Additionally, this function accepts argument name
@@ -30391,7 +30393,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
          <indexterm>
           <primary>pg_clear_relation_stats</primary>
          </indexterm>
-         <function>pg_clear_relation_stats</function> ( <parameter>relation</parameter> <type>regclass</type> )
+         <function>pg_clear_relation_stats</function> ( <parameter>schemaname</parameter> <type>text</type>, <parameter>relname</parameter> <type>text</type> )
          <returnvalue>void</returnvalue>
         </para>
         <para>
@@ -30440,16 +30442,18 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
          <structname>mytable</structname>:
 <programlisting>
  SELECT pg_restore_attribute_stats(
-    'relation',    'mytable'::regclass,
-    'attname',     'col1'::name,
-    'inherited',   false,
-    'avg_width',   125::integer,
-    'null_frac',   0.5::real);
+    'schemaname', 'myschema',
+    'relname',    'mytable',
+    'attname',    'col1',
+    'inherited',  false,
+    'avg_width',  125::integer,
+    'null_frac',  0.5::real);
 </programlisting>
         </para>
         <para>
-         The required arguments are <literal>relation</literal> with a value
-         of type <type>regclass</type>, which specifies the table; either
+         The required arguments are <literal>schemaname</literal> with a value
+         of type <type>regclass</type> and <literal>relname</literal> with a value
+         of type <type>text</type> which specify the table; either
          <literal>attname</literal> with a value of type <type>name</type> or
          <literal>attnum</literal> with a value of type <type>smallint</type>,
          which specifies the column; and <literal>inherited</literal>, which
@@ -30485,7 +30489,8 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
           <primary>pg_clear_attribute_stats</primary>
          </indexterm>
          <function>pg_clear_attribute_stats</function> (
-         <parameter>relation</parameter> <type>regclass</type>,
+         <parameter>schemaname</parameter> <type>text</type>,
+         <parameter>relname</parameter> <type>text</type>,
          <parameter>attname</parameter> <type>name</type>,
          <parameter>inherited</parameter> <type>boolean</type> )
          <returnvalue>void</returnvalue>

base-commit: 21f653cc0024100f8ecc279162631f2b1ba8c46c
-- 
2.48.1

From 4d8d76b78b87f53d0adbd6781a2a66beac5bc264 Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huin...@gmail.com>
Date: Sat, 8 Mar 2025 00:52:41 -0500
Subject: [PATCH v7 2/2] Downgrade as man pg_restore_*_stats errors to
 warnings.

We want to avoid errors that can potentially stop an otherwise
successful pg_upgrade or pg_restore operation. With that in mind, change
as many ERROR reports to WARNING + early termination with no data
updated.
---
 src/include/statistics/stat_utils.h        |   4 +-
 src/backend/statistics/attribute_stats.c   | 124 +++++++++++-----
 src/backend/statistics/relation_stats.c    |  10 +-
 src/backend/statistics/stat_utils.c        |  51 +++++--
 src/test/regress/expected/stats_import.out | 163 ++++++++++++++++-----
 src/test/regress/sql/stats_import.sql      |  36 ++---
 6 files changed, 277 insertions(+), 111 deletions(-)

diff --git a/src/include/statistics/stat_utils.h b/src/include/statistics/stat_utils.h
index cad042c8e4a..298cbae3436 100644
--- a/src/include/statistics/stat_utils.h
+++ b/src/include/statistics/stat_utils.h
@@ -21,7 +21,7 @@ struct StatsArgInfo
 	Oid			argtype;
 };
 
-extern void stats_check_required_arg(FunctionCallInfo fcinfo,
+extern bool stats_check_required_arg(FunctionCallInfo fcinfo,
 									 struct StatsArgInfo *arginfo,
 									 int argnum);
 extern bool stats_check_arg_array(FunctionCallInfo fcinfo,
@@ -30,7 +30,7 @@ extern bool stats_check_arg_pair(FunctionCallInfo fcinfo,
 								 struct StatsArgInfo *arginfo,
 								 int argnum1, int argnum2);
 
-extern void stats_lock_check_privileges(Oid reloid);
+extern bool stats_lock_check_privileges(Oid reloid);
 
 extern Oid stats_schema_check_privileges(const char *nspname);
 
diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c
index f87db2d6102..4f9bc18f8c6 100644
--- a/src/backend/statistics/attribute_stats.c
+++ b/src/backend/statistics/attribute_stats.c
@@ -100,7 +100,7 @@ static struct StatsArgInfo cleararginfo[] =
 
 static bool attribute_statistics_update(FunctionCallInfo fcinfo);
 static Node *get_attr_expr(Relation rel, int attnum);
-static void get_attr_stat_type(Oid reloid, AttrNumber attnum,
+static bool get_attr_stat_type(Oid reloid, AttrNumber attnum,
 							   Oid *atttypid, int32 *atttypmod,
 							   char *atttyptype, Oid *atttypcoll,
 							   Oid *eq_opr, Oid *lt_opr);
@@ -129,10 +129,12 @@ static void init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
  * stored as an anyarray, and the representation of the array needs to store
  * the correct element type, which must be derived from the attribute.
  *
- * Major errors, such as the table not existing, the attribute not existing,
- * or a permissions failure are always reported at ERROR. Other errors, such
- * as a conversion failure on one statistic kind, are reported as a WARNING
- * and other statistic kinds may still be updated.
+ * This function is called during database upgrades and restorations, therefore
+ * it is imperative to avoid ERRORs that could potentially end the upgrade or
+ * restore unless. Major errors, such as the table not existing, the attribute
+ * not existing, or permissions failure are reported as WARNINGs with an end to
+ * the function, thus allowing the upgrade/restore to continue, but without the
+ * stats that can be regenereated once the database is online again.
  */
 static bool
 attribute_statistics_update(FunctionCallInfo fcinfo)
@@ -149,8 +151,8 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 	HeapTuple	statup;
 
 	Oid			atttypid = InvalidOid;
-	int32		atttypmod;
-	char		atttyptype;
+	int32		atttypmod = -1;
+	char		atttyptype = TYPTYPE_PSEUDO; /* Not a great default, but there is no TYPTYPE_INVALID */
 	Oid			atttypcoll = InvalidOid;
 	Oid			eq_opr = InvalidOid;
 	Oid			lt_opr = InvalidOid;
@@ -177,17 +179,19 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 
 	bool		result = true;
 
-	stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
-	stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
+	if (!stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG))
+		return false;
+	if (!stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG))
+		return false;
 
 	nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
 	nspoid = stats_schema_check_privileges(nspname);
-	if (nspoid == InvalidOid)
+	if (!OidIsValid(nspoid))
 		return false;
 
 	relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
 	reloid = get_relname_relid(relname, nspoid);
-	if (reloid == InvalidOid)
+	if (!OidIsValid(reloid))
 	{
 		ereport(WARNING,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -196,29 +200,39 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 	}
 
 	if (RecoveryInProgress())
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("recovery is in progress"),
 				 errhint("Statistics cannot be modified during recovery.")));
+		return false;
+	}
 
 	/* lock before looking up attribute */
-	stats_lock_check_privileges(reloid);
+	if (!stats_lock_check_privileges(reloid))
+		return false;
 
 	/* user can specify either attname or attnum, but not both */
 	if (!PG_ARGISNULL(ATTNAME_ARG))
 	{
 		if (!PG_ARGISNULL(ATTNUM_ARG))
-			ereport(ERROR,
+		{
+			ereport(WARNING,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("cannot specify both attname and attnum")));
+			return false;
+		}
 		attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
 		attnum = get_attnum(reloid, attname);
 		/* note that this test covers attisdropped cases too: */
 		if (attnum == InvalidAttrNumber)
-			ereport(ERROR,
+		{
+			ereport(WARNING,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
 					 errmsg("column \"%s\" of relation \"%s\".\"%s\" does not exist",
 							attname, nspname, relname)));
+			return false;
+		}
 	}
 	else if (!PG_ARGISNULL(ATTNUM_ARG))
 	{
@@ -227,27 +241,33 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 		/* annoyingly, get_attname doesn't check attisdropped */
 		if (attname == NULL ||
 			!SearchSysCacheExistsAttName(reloid, attname))
-			ereport(ERROR,
+		{
+			ereport(WARNING,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
 					 errmsg("column %d of relation \"%s\".\"%s\" does not exist",
 							attnum, nspname, relname)));
+			return false;
+		}
 	}
 	else
 	{
-		ereport(ERROR,
+		ereport(WARNING,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("must specify either attname or attnum")));
-		attname = NULL;			/* keep compiler quiet */
-		attnum = 0;
+		return false;
 	}
 
 	if (attnum < 0)
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot modify statistics on system column \"%s\"",
 						attname)));
+		return false;
+	}
 
-	stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
+	if (!stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG))
+		return false;
 	inherited = PG_GETARG_BOOL(INHERITED_ARG);
 
 	/*
@@ -296,10 +316,11 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
 	}
 
 	/* derive information from attribute */
-	get_attr_stat_type(reloid, attnum,
-					   &atttypid, &atttypmod,
-					   &atttyptype, &atttypcoll,
-					   &eq_opr, &lt_opr);
+	if (!get_attr_stat_type(reloid, attnum,
+							&atttypid, &atttypmod,
+							&atttyptype, &atttypcoll,
+							&eq_opr, &lt_opr))
+		result = false;
 
 	/* if needed, derive element type */
 	if (do_mcelem || do_dechist)
@@ -579,7 +600,7 @@ get_attr_expr(Relation rel, int attnum)
 /*
  * Derive type information from the attribute.
  */
-static void
+static bool
 get_attr_stat_type(Oid reloid, AttrNumber attnum,
 				   Oid *atttypid, int32 *atttypmod,
 				   char *atttyptype, Oid *atttypcoll,
@@ -596,18 +617,26 @@ get_attr_stat_type(Oid reloid, AttrNumber attnum,
 
 	/* Attribute not found */
 	if (!HeapTupleIsValid(atup))
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_UNDEFINED_COLUMN),
 				 errmsg("attribute %d of relation \"%s\" does not exist",
 						attnum, RelationGetRelationName(rel))));
+		relation_close(rel, NoLock);
+		return false;
+	}
 
 	attr = (Form_pg_attribute) GETSTRUCT(atup);
 
 	if (attr->attisdropped)
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_UNDEFINED_COLUMN),
 				 errmsg("attribute %d of relation \"%s\" does not exist",
 						attnum, RelationGetRelationName(rel))));
+		relation_close(rel, NoLock);
+		return false;
+	}
 
 	expr = get_attr_expr(rel, attr->attnum);
 
@@ -656,6 +685,7 @@ get_attr_stat_type(Oid reloid, AttrNumber attnum,
 		*atttypcoll = DEFAULT_COLLATION_OID;
 
 	relation_close(rel, NoLock);
+	return true;
 }
 
 /*
@@ -781,6 +811,10 @@ set_stats_slot(Datum *values, bool *nulls, bool *replaces,
 	if (slotidx >= STATISTIC_NUM_SLOTS && first_empty >= 0)
 		slotidx = first_empty;
 
+	/*
+	 * Currently there is no datatype that can have more than STATISTIC_NUM_SLOTS
+	 * statistic kinds, so this can safely remain an ERROR for now.
+	 */
 	if (slotidx >= STATISTIC_NUM_SLOTS)
 		ereport(ERROR,
 				(errmsg("maximum number of statistics slots exceeded: %d",
@@ -927,15 +961,19 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
 	AttrNumber	attnum;
 	bool		inherited;
 
-	stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG);
-	stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG);
-	stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
-	stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
+	if (!stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG))
+		PG_RETURN_VOID();
+	if (!stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG))
+		PG_RETURN_VOID();
+	if (!stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG))
+		PG_RETURN_VOID();
+	if (!stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG))
+		PG_RETURN_VOID();
 
 	nspname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELSCHEMA_ARG));
 	nspoid = stats_schema_check_privileges(nspname);
 	if (!OidIsValid(nspoid))
-		return false;
+		PG_RETURN_VOID();
 
 	relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
 	reloid = get_relname_relid(relname, nspoid);
@@ -944,31 +982,41 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
 		ereport(WARNING,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
 				 errmsg("Relation \"%s\".\"%s\" not found.", nspname, relname)));
-		return false;
+		PG_RETURN_VOID();
 	}
 
 	if (RecoveryInProgress())
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("recovery is in progress"),
 				 errhint("Statistics cannot be modified during recovery.")));
+		PG_RETURN_VOID();
+	}
 
-	stats_lock_check_privileges(reloid);
+	if (!stats_lock_check_privileges(reloid))
+		PG_RETURN_VOID();
 
 	attname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTNAME_ARG));
 	attnum = get_attnum(reloid, attname);
 
 	if (attnum < 0)
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot clear statistics on system column \"%s\"",
 						attname)));
+		PG_RETURN_VOID();
+	}
 
 	if (attnum == InvalidAttrNumber)
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_UNDEFINED_COLUMN),
 				 errmsg("column \"%s\" of relation \"%s\" does not exist",
 						attname, get_rel_name(reloid))));
+		PG_RETURN_VOID();
+	}
 
 	inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
 
diff --git a/src/backend/statistics/relation_stats.c b/src/backend/statistics/relation_stats.c
index fdc69bc93e2..49109cf721d 100644
--- a/src/backend/statistics/relation_stats.c
+++ b/src/backend/statistics/relation_stats.c
@@ -84,8 +84,11 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 	bool		nulls[4] = {0};
 	int			nreplaces = 0;
 
-	stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG);
-	stats_check_required_arg(fcinfo, relarginfo, RELNAME_ARG);
+	if (!stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG))
+		return false;
+
+	if (!stats_check_required_arg(fcinfo, relarginfo, RELNAME_ARG))
+		return false;
 
 	nspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
 	nspoid = stats_schema_check_privileges(nspname);
@@ -108,7 +111,8 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 				 errmsg("recovery is in progress"),
 				 errhint("Statistics cannot be modified during recovery.")));
 
-	stats_lock_check_privileges(reloid);
+	if (!stats_lock_check_privileges(reloid))
+		return false;
 
 	if (!PG_ARGISNULL(RELPAGES_ARG))
 	{
diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c
index e037d4994e8..dd9d88ac1c5 100644
--- a/src/backend/statistics/stat_utils.c
+++ b/src/backend/statistics/stat_utils.c
@@ -34,16 +34,20 @@
 /*
  * Ensure that a given argument is not null.
  */
-void
+bool
 stats_check_required_arg(FunctionCallInfo fcinfo,
 						 struct StatsArgInfo *arginfo,
 						 int argnum)
 {
 	if (PG_ARGISNULL(argnum))
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("\"%s\" cannot be NULL",
 						arginfo[argnum].argname)));
+		return false;
+	}
+	return true;
 }
 
 /*
@@ -128,13 +132,14 @@ stats_check_arg_pair(FunctionCallInfo fcinfo,
  *   - the role owns the current database and the relation is not shared
  *   - the role has the MAINTAIN privilege on the relation
  */
-void
+bool
 stats_lock_check_privileges(Oid reloid)
 {
 	Relation	table;
 	Oid			table_oid = reloid;
 	Oid			index_oid = InvalidOid;
 	LOCKMODE	index_lockmode = NoLock;
+	bool		ok = true;
 
 	/*
 	 * For indexes, we follow the locking behavior in do_analyze_rel() and
@@ -174,14 +179,15 @@ stats_lock_check_privileges(Oid reloid)
 		case RELKIND_PARTITIONED_TABLE:
 			break;
 		default:
-			ereport(ERROR,
+			ereport(WARNING,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("cannot modify statistics for relation \"%s\"",
 							RelationGetRelationName(table)),
 					 errdetail_relkind_not_supported(table->rd_rel->relkind)));
+		ok = false;
 	}
 
-	if (OidIsValid(index_oid))
+	if (ok && (OidIsValid(index_oid)))
 	{
 		Relation	index;
 
@@ -194,25 +200,33 @@ stats_lock_check_privileges(Oid reloid)
 		relation_close(index, NoLock);
 	}
 
-	if (table->rd_rel->relisshared)
-		ereport(ERROR,
+	if (ok && (table->rd_rel->relisshared))
+	{
+		ereport(WARNING,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot modify statistics for shared relation")));
+		ok = false;
+	}
 
-	if (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()))
+	if (ok && (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId())))
 	{
 		AclResult	aclresult = pg_class_aclcheck(RelationGetRelid(table),
 												  GetUserId(),
 												  ACL_MAINTAIN);
 
 		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult,
-						   get_relkind_objtype(table->rd_rel->relkind),
-						   NameStr(table->rd_rel->relname));
+		{
+			ereport(WARNING,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+						errmsg("permission denied for relation %s",
+							   NameStr(table->rd_rel->relname))));
+			ok = false;
+		}
 	}
 
 	/* retain lock on table */
 	relation_close(table, NoLock);
+	return ok;
 }
 
 
@@ -318,9 +332,12 @@ stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
 								  &args, &types, &argnulls);
 
 	if (nargs % 2 != 0)
-		ereport(ERROR,
+	{
+		ereport(WARNING,
 				errmsg("variadic arguments must be name/value pairs"),
 				errhint("Provide an even number of variadic arguments that can be divided into pairs."));
+		return false;
+	}
 
 	/*
 	 * For each argument name/value pair, find corresponding positional
@@ -333,14 +350,20 @@ stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
 		char	   *argname;
 
 		if (argnulls[i])
-			ereport(ERROR,
+		{
+			ereport(WARNING,
 					(errmsg("name at variadic position %d is NULL", i + 1)));
+			return false;
+		}
 
 		if (types[i] != TEXTOID)
-			ereport(ERROR,
+		{
+			ereport(WARNING,
 					(errmsg("name at variadic position %d has type \"%s\", expected type \"%s\"",
 							i + 1, format_type_be(types[i]),
 							format_type_be(TEXTOID))));
+			return false;
+		}
 
 		if (argnulls[i + 1])
 			continue;
diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out
index 2f1295f2149..6551d6bf099 100644
--- a/src/test/regress/expected/stats_import.out
+++ b/src/test/regress/expected/stats_import.out
@@ -46,31 +46,51 @@ SELECT pg_clear_relation_stats('stats_import', 'test');
 --
 -- relstats tests
 --
--- error: schemaname missing
+-- warning: schemaname missing, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'relname', 'test',
         'relpages', 17::integer);
-ERROR:  "schemaname" cannot be NULL
--- error: relname missing
+WARNING:  "schemaname" cannot be NULL
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
+-- warning: relname missing, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relpages', 17::integer);
-ERROR:  "relname" cannot be NULL
---- error: schemaname is wrong type
+WARNING:  "relname" cannot be NULL
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
+--- warning: schemaname is wrong type, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 3.6::float,
         'relname', 'test',
         'relpages', 17::integer);
 WARNING:  argument "schemaname" has type "double precision", expected type "text"
-ERROR:  "schemaname" cannot be NULL
---- error: relname is wrong type
+WARNING:  "schemaname" cannot be NULL
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
+--- warning: relname is wrong type, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 0::oid,
         'relpages', 17::integer);
 WARNING:  argument "relname" has type "oid", expected type "text"
-ERROR:  "relname" cannot be NULL
--- error: relation not found
+WARNING:  "relname" cannot be NULL
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
+-- warning: relation not found, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 'nope',
@@ -81,19 +101,30 @@ WARNING:  Relation "stats_import"."nope" not found.
  f
 (1 row)
 
--- error: odd number of variadic arguments cannot be pairs
+-- warning: odd number of variadic arguments cannot be pairs, nothing updated
 SELECT pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 'test',
         'relallvisible');
-ERROR:  variadic arguments must be name/value pairs
+WARNING:  variadic arguments must be name/value pairs
 HINT:  Provide an even number of variadic arguments that can be divided into pairs.
--- error: argument name is NULL
+WARNING:  "schemaname" cannot be NULL
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
+-- warning: argument name is NULL, nothing updated
 SELECT pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 'test',
         NULL, '17'::integer);
-ERROR:  name at variadic position 5 is NULL
+WARNING:  name at variadic position 5 is NULL
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
 -- starting stats
 SELECT relpages, reltuples, relallvisible, relallfrozen
 FROM pg_class
@@ -345,26 +376,46 @@ CREATE SEQUENCE stats_import.testseq;
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 'testseq');
-ERROR:  cannot modify statistics for relation "testseq"
+WARNING:  cannot modify statistics for relation "testseq"
 DETAIL:  This operation is not supported for sequences.
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
 SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testseq');
-ERROR:  cannot modify statistics for relation "testseq"
+WARNING:  cannot modify statistics for relation "testseq"
 DETAIL:  This operation is not supported for sequences.
+ pg_clear_relation_stats 
+-------------------------
+ 
+(1 row)
+
 CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test;
 SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testview');
-ERROR:  cannot modify statistics for relation "testview"
+WARNING:  cannot modify statistics for relation "testview"
 DETAIL:  This operation is not supported for views.
+ pg_clear_relation_stats 
+-------------------------
+ 
+(1 row)
+
 --
 -- attribute stats
 --
--- error: schemaname missing
+-- warning: schemaname missing, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'relname', 'test',
     'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  "schemaname" cannot be NULL
--- error: schema does not exist
+WARNING:  "schemaname" cannot be NULL
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: schema does not exist, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'nope',
     'relname', 'test',
@@ -377,14 +428,19 @@ WARNING:  schema nope does not exist
  f
 (1 row)
 
--- error: relname missing
+-- warning: relname missing, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  "relname" cannot be NULL
--- error: relname does not exist
+WARNING:  "relname" cannot be NULL
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: relname does not exist, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'nope',
@@ -397,23 +453,33 @@ WARNING:  Relation "stats_import"."nope" not found.
  f
 (1 row)
 
--- error: relname null
+-- warning: relname null, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', NULL,
     'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  "relname" cannot be NULL
--- error: NULL attname
+WARNING:  "relname" cannot be NULL
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: NULL attname, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
     'attname', NULL,
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  must specify either attname or attnum
--- error: attname doesn't exist
+WARNING:  must specify either attname or attnum
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: attname doesn't exist, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
@@ -422,8 +488,13 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'null_frac', 0.1::real,
     'avg_width', 2::integer,
     'n_distinct', 0.3::real);
-ERROR:  column "nope" of relation "stats_import"."test" does not exist
--- error: both attname and attnum
+WARNING:  column "nope" of relation "stats_import"."test" does not exist
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: both attname and attnum, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
@@ -431,30 +502,50 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'attnum', 1::smallint,
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  cannot specify both attname and attnum
--- error: neither attname nor attnum
+WARNING:  cannot specify both attname and attnum
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: neither attname nor attnum, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  must specify either attname or attnum
--- error: attribute is system column
+WARNING:  must specify either attname or attnum
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: attribute is system column, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
     'attname', 'xmin',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  cannot modify statistics on system column "xmin"
--- error: inherited null
+WARNING:  cannot modify statistics on system column "xmin"
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- warning: inherited null, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
     'attname', 'id',
     'inherited', NULL::boolean,
     'null_frac', 0.1::real);
-ERROR:  "inherited" cannot be NULL
+WARNING:  "inherited" cannot be NULL
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
 -- ok: just the fixed values, with version, no stakinds
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql
index ccdc44e9236..dbbebce1673 100644
--- a/src/test/regress/sql/stats_import.sql
+++ b/src/test/regress/sql/stats_import.sql
@@ -39,41 +39,41 @@ SELECT pg_clear_relation_stats('stats_import', 'test');
 -- relstats tests
 --
 
--- error: schemaname missing
+-- warning: schemaname missing, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'relname', 'test',
         'relpages', 17::integer);
 
--- error: relname missing
+-- warning: relname missing, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relpages', 17::integer);
 
---- error: schemaname is wrong type
+--- warning: schemaname is wrong type, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 3.6::float,
         'relname', 'test',
         'relpages', 17::integer);
 
---- error: relname is wrong type
+--- warning: relname is wrong type, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 0::oid,
         'relpages', 17::integer);
 
--- error: relation not found
+-- warning: relation not found, nothing updated
 SELECT pg_catalog.pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 'nope',
         'relpages', 17::integer);
 
--- error: odd number of variadic arguments cannot be pairs
+-- warning: odd number of variadic arguments cannot be pairs, nothing updated
 SELECT pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 'test',
         'relallvisible');
 
--- error: argument name is NULL
+-- warning: argument name is NULL, nothing updated
 SELECT pg_restore_relation_stats(
         'schemaname', 'stats_import',
         'relname', 'test',
@@ -246,14 +246,14 @@ SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname
 -- attribute stats
 --
 
--- error: schemaname missing
+-- warning: schemaname missing, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'relname', 'test',
     'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: schema does not exist
+-- warning: schema does not exist, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'nope',
     'relname', 'test',
@@ -261,14 +261,14 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: relname missing
+-- warning: relname missing, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: relname does not exist
+-- warning: relname does not exist, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'nope',
@@ -276,7 +276,7 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: relname null
+-- warning: relname null, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', NULL,
@@ -284,7 +284,7 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: NULL attname
+-- warning: NULL attname, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
@@ -292,7 +292,7 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: attname doesn't exist
+-- warning: attname doesn't exist, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
@@ -302,7 +302,7 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'avg_width', 2::integer,
     'n_distinct', 0.3::real);
 
--- error: both attname and attnum
+-- warning: both attname and attnum, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
@@ -311,14 +311,14 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: neither attname nor attnum
+-- warning: neither attname nor attnum, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: attribute is system column
+-- warning: attribute is system column, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
@@ -326,7 +326,7 @@ SELECT pg_catalog.pg_restore_attribute_stats(
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: inherited null
+-- warning: inherited null, nothing updated
 SELECT pg_catalog.pg_restore_attribute_stats(
     'schemaname', 'stats_import',
     'relname', 'test',
-- 
2.48.1

Reply via email to