From 72d4b9fc128e6d4ef73bb24ebba41797d06a7d9e Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Tue, 4 Mar 2025 22:16:52 -0500
Subject: [PATCH v9j] Stats: use schemaname/relname instead of regclass.

For import and export, use schemaname/relname rather than
regclass.

This is more natural during export, fits with the other arguments
better, and it gives better control over error handling in case we
need to downgrade more errors to warnings.

Also, use text for the argument types for schemaname, relname, and
attname so that casts to "name" are not required.

Author: Corey Huinker <corey.huinker@gmail.com>
Discussion: https://postgr.es/m/CADkLM=ceOSsx_=oe73QQ-BxUFR2Cwqum7-UP_fPe22DBY0NerA@mail.gmail.com
---
 doc/src/sgml/func.sgml                     |  50 +--
 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/include/catalog/catversion.h           |   2 +-
 src/include/catalog/pg_proc.dat            |   8 +-
 src/include/statistics/stat_utils.h        |   2 +
 src/test/regress/expected/stats_import.out | 353 ++++++++++++++-------
 src/test/regress/sql/stats_import.sql      | 306 ++++++++++++------
 11 files changed, 647 insertions(+), 294 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6fa1d6586b8..f8c1deb04ee 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -30364,22 +30364,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
+         The arguments <literal>schemaname</literal> and
+         <literal>relname</literal> are required, and specify 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
@@ -30407,7 +30409,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>
@@ -30456,22 +30458,23 @@ 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
-         <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
-         specifies whether the statistics include values from child tables.
-         Other arguments are the names and values of statistics corresponding
-         to columns in <link
+         The required arguments are <literal>schemaname</literal> 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>text</type> or <literal>attnum</literal> with a
+         value of type <type>smallint</type>, which specifies the column; and
+         <literal>inherited</literal>, which specifies whether the statistics
+         include values from child tables.  Other arguments are the names and
+         values of statistics corresponding to columns in <link
          linkend="view-pg-stats"><structname>pg_stats</structname></link>.
         </para>
         <para>
@@ -30501,8 +30504,9 @@ 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>attname</parameter> <type>name</type>,
+         <parameter>schemaname</parameter> <type>text</type>,
+         <parameter>relname</parameter> <type>text</type>,
+         <parameter>attname</parameter> <type>text</type>,
          <parameter>inherited</parameter> <type>boolean</type> )
          <returnvalue>void</returnvalue>
         </para>
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..b444f6871df 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 = LookupExplicitNamespace(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 428ed2d60fc..239664c459d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10498,7 +10498,6 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 	PQExpBuffer out;
 	DumpId	   *deps = NULL;
 	int			ndeps = 0;
-	char	   *qualified_name;
 	int			i_attname;
 	int			i_inherited;
 	int			i_null_frac;
@@ -10563,15 +10562,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);
 	appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
 	appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer\n);\n",
@@ -10610,9 +10610,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");
@@ -10624,7 +10625,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;
@@ -10704,7 +10708,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 d281e27aa67..d3e84f44c6c 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4741,14 +4741,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/include/catalog/catversion.h b/src/include/catalog/catversion.h
index cf381867e40..c68ff9cbf83 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202503241
+#define CATALOG_VERSION_NO	202503242
 
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0d29ef50ff2..3f7b82e02bb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12453,8 +12453,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',
@@ -12469,8 +12469,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..ba09b431c11 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/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out
index 1f46d5e7854..302e77743e3 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(
+    'relname', 'test',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+ERROR:  "schemaname" cannot be NULL
+-- error: schema does not exist
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'nope',
+    'relname', 'test',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+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(
-    'relation', '0'::oid::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', 'nope',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  could not open relation with OID 0
--- error: relation null
+WARNING:  Relation "stats_import"."nope" not found.
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- error: relname null
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', NULL::oid::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'stats_import',
+    'relname', NULL,
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  "relation" cannot be NULL
+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 
 --------------------------
  
@@ -1219,6 +1310,52 @@ AND attname = 'arange';
      0
 (1 row)
 
+-- temp tables
+CREATE TEMP TABLE stats_temp(i int);
+SELECT pg_restore_relation_stats(
+        'schemaname', 'pg_temp',
+        'relname', 'stats_temp',
+        'relpages', '-19'::integer,
+        'reltuples', 401::real,
+        'relallvisible', 5::integer,
+        'relallfrozen', 3::integer);
+ pg_restore_relation_stats 
+---------------------------
+ t
+(1 row)
+
+SELECT relname, relpages, reltuples, relallvisible, relallfrozen
+FROM pg_class
+WHERE oid = 'pg_temp.stats_temp'::regclass
+ORDER BY relname;
+  relname   | relpages | reltuples | relallvisible | relallfrozen 
+------------+----------+-----------+---------------+--------------
+ stats_temp |      -19 |       401 |             5 |            3
+(1 row)
+
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'pg_temp',
+    'relname', 'stats_temp',
+    'attname', 'i',
+    'inherited', false::boolean,
+    'null_frac', 0.0123::real
+    );
+ pg_restore_attribute_stats 
+----------------------------
+ t
+(1 row)
+
+SELECT tablename, null_frac
+FROM pg_stats
+WHERE schemaname like 'pg_temp%'
+AND tablename = 'stats_temp'
+AND inherited = false
+AND attname = 'i';
+ tablename  | null_frac 
+------------+-----------
+ stats_temp |    0.0123
+(1 row)
+
 DROP SCHEMA stats_import CASCADE;
 NOTICE:  drop cascades to 6 other objects
 DETAIL:  drop cascades to type stats_import.complex_type
diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql
index 0ec590688c2..35a9a1e3e7a 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(
+    'relname', 'test',
+    'attname', 'id',
+    'inherited', false::boolean,
+    'null_frac', 0.1::real);
+
+-- error: schema does not exist
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', '0'::oid::regclass,
-    'attname', 'id'::name,
+    'schemaname', 'nope',
+    'relname', 'test',
+    'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
 
--- error: relation null
+-- error: relname missing
 SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', NULL::oid::regclass,
-    'attname', 'id'::name,
+    '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
@@ -864,4 +940,34 @@ AND tablename = 'test'
 AND inherited = false
 AND attname = 'arange';
 
+-- temp tables
+CREATE TEMP TABLE stats_temp(i int);
+SELECT pg_restore_relation_stats(
+        'schemaname', 'pg_temp',
+        'relname', 'stats_temp',
+        'relpages', '-19'::integer,
+        'reltuples', 401::real,
+        'relallvisible', 5::integer,
+        'relallfrozen', 3::integer);
+
+SELECT relname, relpages, reltuples, relallvisible, relallfrozen
+FROM pg_class
+WHERE oid = 'pg_temp.stats_temp'::regclass
+ORDER BY relname;
+
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'schemaname', 'pg_temp',
+    'relname', 'stats_temp',
+    'attname', 'i',
+    'inherited', false::boolean,
+    'null_frac', 0.0123::real
+    );
+
+SELECT tablename, null_frac
+FROM pg_stats
+WHERE schemaname like 'pg_temp%'
+AND tablename = 'stats_temp'
+AND inherited = false
+AND attname = 'i';
+
 DROP SCHEMA stats_import CASCADE;
-- 
2.34.1

