v12-0003-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch

text/x-patch

Filename: v12-0003-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch
Type: text/x-patch
Part: 2
Message: Re: Statistics Import and Export

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v12-0003
Subject: Downgrade many pg_restore_*_stats errors to warnings.
File+
src/backend/statistics/attribute_stats.c 86 34
src/backend/statistics/relation_stats.c 9 3
src/backend/statistics/stat_utils.c 49 16
src/include/statistics/stat_utils.h 2 2
src/test/regress/expected/stats_import.out 145 39
src/test/regress/sql/stats_import.sql 18 18
From 16794820dedd79ec58f8692da5b50a4d8976620a Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Sat, 8 Mar 2025 00:52:41 -0500
Subject: [PATCH v12 3/3] Downgrade many 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   | 120 ++++++++++----
 src/backend/statistics/relation_stats.c    |  12 +-
 src/backend/statistics/stat_utils.c        |  65 ++++++--
 src/test/regress/expected/stats_import.out | 184 ++++++++++++++++-----
 src/test/regress/sql/stats_import.sql      |  36 ++--
 6 files changed, 309 insertions(+), 112 deletions(-)

diff --git a/src/include/statistics/stat_utils.h b/src/include/statistics/stat_utils.h
index 512eb776e0e..809c8263a41 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_lookup_relid(const char *nspname, const char *relname);
 
diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c
index f5eb17ba42d..b7ba1622391 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)
@@ -148,8 +150,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;
@@ -176,38 +178,52 @@ 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));
 	relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
 
 	reloid = stats_lookup_relid(nspname, relname);
+	if (!OidIsValid(reloid))
+		return false;
 
 	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\" does not exist",
 							attname, relname)));
+			return false;
+		}
 	}
 	else if (!PG_ARGISNULL(ATTNUM_ARG))
 	{
@@ -216,27 +232,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\" does not exist",
 							attnum, 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);
 
 	/*
@@ -285,10 +307,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)
@@ -568,7 +591,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,
@@ -585,18 +608,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);
 
@@ -645,6 +676,7 @@ get_attr_stat_type(Oid reloid, AttrNumber attnum,
 		*atttypcoll = DEFAULT_COLLATION_OID;
 
 	relation_close(rel, NoLock);
+	return true;
 }
 
 /*
@@ -770,6 +802,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",
@@ -915,38 +951,54 @@ 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));
 	relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
 
 	reloid = stats_lookup_relid(nspname, relname);
+	if (!OidIsValid(reloid))
+		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 cd3a75b621a..7c47af15c9f 100644
--- a/src/backend/statistics/relation_stats.c
+++ b/src/backend/statistics/relation_stats.c
@@ -83,13 +83,18 @@ 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));
 	relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
 
 	reloid = stats_lookup_relid(nspname, relname);
+	if (!OidIsValid(reloid))
+		return false;
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -97,7 +102,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 a9a3224efe6..d587e875457 100644
--- a/src/backend/statistics/stat_utils.c
+++ b/src/backend/statistics/stat_utils.c
@@ -33,16 +33,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;
 }
 
 /*
@@ -127,13 +131,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
@@ -173,14 +178,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;
 
@@ -193,25 +199,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;
 }
 
 /*
@@ -223,10 +237,20 @@ stats_lookup_relid(const char *nspname, const char *relname)
 	Oid			nspoid;
 	Oid			reloid;
 
-	nspoid = LookupExplicitNamespace(nspname, false);
+	nspoid = LookupExplicitNamespace(nspname, true);
+	if (!OidIsValid(nspoid))
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_UNDEFINED_TABLE),
+				 errmsg("relation \"%s.%s\" does not exist",
+						nspname, relname)));
+
+		return InvalidOid;
+	}
+
 	reloid = get_relname_relid(relname, nspoid);
 	if (!OidIsValid(reloid))
-		ereport(ERROR,
+		ereport(WARNING,
 				(errcode(ERRCODE_UNDEFINED_TABLE),
 				 errmsg("relation \"%s.%s\" does not exist",
 						nspname, relname)));
@@ -303,9 +327,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
@@ -318,14 +345,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 48d6392b4ad..161cf67b711 100644
--- a/src/test/regress/expected/stats_import.out
+++ b/src/test/regress/expected/stats_import.out
@@ -46,49 +46,85 @@ 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',
         'relpages', 17::integer);
-ERROR:  relation "stats_import.nope" does not exist
--- error: odd number of variadic arguments cannot be pairs
+WARNING:  relation "stats_import.nope" does not exist
+ pg_restore_relation_stats 
+---------------------------
+ f
+(1 row)
+
+-- 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
@@ -340,65 +376,110 @@ 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',
     'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  schema "nope" does not exist
--- error: relname missing
+WARNING:  relation "nope.test" does not exist
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- 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',
     'attname', 'id',
     'inherited', false::boolean,
     'null_frac', 0.1::real);
-ERROR:  relation "stats_import.nope" does not exist
--- error: relname null
+WARNING:  relation "stats_import.nope" does not exist
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- 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',
@@ -407,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 "test" does not exist
--- error: both attname and attnum
+WARNING:  column "nope" of relation "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',
@@ -416,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 d140733a750..be8045ceea5 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.49.0