public inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v6 2/3] Add index_get_partition convenience function
88+ messages / 14 participants
[nested] [flat]

* [PATCH v6 2/3] Add index_get_partition convenience function
@ 2019-02-28 20:44 Alvaro Herrera <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Alvaro Herrera @ 2019-02-28 20:44 UTC (permalink / raw)

This new function simplifies some existing coding, as well as supports
future patches.

This may end up backpatched to 11, per
https://postgr.es/m/[email protected]

Discussion: https://postgr.es/m/[email protected]
Reviewed-by: Amit Langote
---
 src/backend/catalog/partition.c  | 36 ++++++++++++++++++++++++++++
 src/backend/commands/tablecmds.c | 40 +++++++++-----------------------
 src/include/catalog/partition.h  |  1 +
 3 files changed, 48 insertions(+), 29 deletions(-)

diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 3ccdaff8c45..8ea7a62418f 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -145,6 +145,42 @@ get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
 	get_partition_ancestors_worker(inhRel, parentOid, ancestors);
 }
 
+/*
+ * index_get_partition
+ *		Return the OID of index of the given partition that is a child
+ *		of the given index, or InvalidOid if there isn't one.
+ */
+Oid
+index_get_partition(Relation partition, Oid indexId)
+{
+	List	   *idxlist = RelationGetIndexList(partition);
+	ListCell   *l;
+
+	foreach(l, idxlist)
+	{
+		Oid			partIdx = lfirst_oid(l);
+		HeapTuple	tup;
+		Form_pg_class classForm;
+		bool		ispartition;
+
+		tup = SearchSysCache1(RELOID, ObjectIdGetDatum(partIdx));
+		if (!tup)
+			elog(ERROR, "cache lookup failed for relation %u", partIdx);
+		classForm = (Form_pg_class) GETSTRUCT(tup);
+		ispartition = classForm->relispartition;
+		ReleaseSysCache(tup);
+		if (!ispartition)
+			continue;
+		if (get_partition_parent(lfirst_oid(l)) == indexId)
+		{
+			list_free(idxlist);
+			return partIdx;
+		}
+	}
+
+	return InvalidOid;
+}
+
 /*
  * map_partition_varattnos - maps varattno of any Vars in expr from the
  * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 515c29072c8..3183b2aaa12 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -15649,36 +15649,18 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 static void
 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
 {
-	Relation	pg_inherits;
-	ScanKeyData key;
-	HeapTuple	tuple;
-	SysScanDesc scan;
+	Oid			existingIdx;
 
-	pg_inherits = table_open(InheritsRelationId, AccessShareLock);
-	ScanKeyInit(&key, Anum_pg_inherits_inhparent,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(RelationGetRelid(parentIdx)));
-	scan = systable_beginscan(pg_inherits, InheritsParentIndexId, true,
-							  NULL, 1, &key);
-	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
-	{
-		Form_pg_inherits inhForm;
-		Oid			tab;
-
-		inhForm = (Form_pg_inherits) GETSTRUCT(tuple);
-		tab = IndexGetRelation(inhForm->inhrelid, false);
-		if (tab == RelationGetRelid(partitionTbl))
-			ereport(ERROR,
-					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-					 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
-							RelationGetRelationName(partIdx),
-							RelationGetRelationName(parentIdx)),
-					 errdetail("Another index is already attached for partition \"%s\".",
-							   RelationGetRelationName(partitionTbl))));
-	}
-
-	systable_endscan(scan);
-	table_close(pg_inherits, AccessShareLock);
+	existingIdx = index_get_partition(partitionTbl,
+									  RelationGetRelid(parentIdx));
+	if (OidIsValid(existingIdx))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
+						RelationGetRelationName(partIdx),
+						RelationGetRelationName(parentIdx)),
+				 errdetail("Another index is already attached for partition \"%s\".",
+						   RelationGetRelationName(partitionTbl))));
 }
 
 /*
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index d84e3259835..616e18af308 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -21,6 +21,7 @@
 
 extern Oid	get_partition_parent(Oid relid);
 extern List *get_partition_ancestors(Oid relid);
+extern Oid	index_get_partition(Relation partition, Oid indexId);
 extern List *map_partition_varattnos(List *expr, int fromrel_varno,
 						Relation to_rel, Relation from_rel,
 						bool *found_whole_row);
-- 
2.17.1


--n8g4imXOkfNTN/H1
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="v6-0003-support-FKs-referencing-partitioned-tables.patch"



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* [PATCH] Add new GUC compression_algorithm
@ 2019-08-04 00:02 Petr Jelinek <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Petr Jelinek @ 2019-08-04 00:02 UTC (permalink / raw)

Sets which algorithm to use for TOAST and WAL compression.

Currently allows either pglz which is the standard PostgreSQL algorithm
or lz4 if the PostgreSQL was configured with --with-lz4 (which is the
default).

The implementation allows different values to have different compression
algorithms and also supports reading old TOAST format which always uses
the pglz.
---
 configure                               | 116 ++++++++++++++++++--
 configure.in                            |  19 ++++
 doc/src/sgml/config.sgml                |  38 +++++++
 doc/src/sgml/storage.sgml               |   5 +-
 src/Makefile.global.in                  |   1 +
 src/backend/access/heap/tuptoaster.c    |  86 +++++++++++----
 src/backend/access/transam/xloginsert.c |   5 +-
 src/backend/utils/misc/guc.c            |  23 +++-
 src/common/pg_lzcompress.c              | 134 +++++++++++++++++++++++-
 src/include/common/pg_lzcompress.h      |  49 +++++++--
 src/include/pg_config.h.in              |   3 +
 src/include/postgres.h                  |   3 +-
 src/test/Makefile                       |   8 +-
 src/test/toast/.gitignore               |   2 +
 src/test/toast/Makefile                 |  25 +++++
 src/test/toast/README                   |  25 +++++
 src/test/toast/t/001_lz4.pl             | 124 ++++++++++++++++++++++
 17 files changed, 619 insertions(+), 47 deletions(-)
 create mode 100644 src/test/toast/.gitignore
 create mode 100644 src/test/toast/Makefile
 create mode 100644 src/test/toast/README
 create mode 100644 src/test/toast/t/001_lz4.pl

diff --git a/configure b/configure
index 7a6bfc2339..edd2bfefd6 100755
--- a/configure
+++ b/configure
@@ -704,6 +704,7 @@ with_system_tzdata
 with_libxslt
 with_libxml
 XML2_CONFIG
+with_lz4
 UUID_EXTRA_OBJS
 with_uuid
 with_systemd
@@ -795,6 +796,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -859,6 +861,7 @@ with_readline
 with_libedit_preferred
 with_uuid
 with_ossp_uuid
+with_lz4
 with_libxml
 with_libxslt
 with_system_tzdata
@@ -932,6 +935,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1184,6 +1188,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1321,7 +1334,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1474,6 +1487,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1564,6 +1578,7 @@ Optional Packages:
                           prefer BSD Libedit over GNU Readline
   --with-uuid=LIB         build contrib/uuid-ossp using LIB (bsd,e2fs,ossp)
   --with-ossp-uuid        obsolete spelling of --with-uuid=ossp
+  --without-lz4           do not build with LZ4 support
   --with-libxml           build with XML support
   --with-libxslt          use XSLT support when building contrib/xml2
   --with-system-tzdata=DIR
@@ -8115,6 +8130,34 @@ fi
 
 
 
+#
+# LZ4
+#
+
+
+
+# Check whether --with-lz4 was given.
+if test "${with_lz4+set}" = set; then :
+  withval=$with_lz4;
+  case $withval in
+    yes)
+      :
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-lz4 option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_lz4=yes
+
+fi
+
+
+
 
 #
 # XML
@@ -12661,6 +12704,55 @@ fi
 fi
 
 
+# for lz4 compression support
+if test "$with_lz4" = yes ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LZ4_sizeofState in -llz4" >&5
+$as_echo_n "checking for LZ4_sizeofState in -llz4... " >&6; }
+if ${ac_cv_lib_lz4_LZ4_sizeofState+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-llz4  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char LZ4_sizeofState ();
+int
+main ()
+{
+return LZ4_sizeofState ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_lz4_LZ4_sizeofState=yes
+else
+  ac_cv_lib_lz4_LZ4_sizeofState=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lz4_LZ4_sizeofState" >&5
+$as_echo "$ac_cv_lib_lz4_LZ4_sizeofState" >&6; }
+if test "x$ac_cv_lib_lz4_LZ4_sizeofState" = xyes; then :
+  COMPRESSION_LIBS=" -llz4"
+else
+  as_fn_error $? "library 'lz4' is required for LZ4 compression support" "$LINENO" 5
+fi
+
+
+$as_echo "#define HAVE_LZ4 1" >>confdefs.h
+
+fi
+LIBS="$LIBS$COMPRESSION_LIBS"
 
 ##
 ## Header files
@@ -13340,6 +13432,18 @@ fi
 
 done
 
+fi
+
+# for lz4 compression support
+if test "$with_lz4" = yes ; then
+  ac_fn_c_check_header_mongrel "$LINENO" "lz4.h" "ac_cv_header_lz4_h" "$ac_includes_default"
+if test "x$ac_cv_header_lz4_h" = xyes; then :
+
+else
+  as_fn_error $? "header file <lz4.h> is required for LZ4 support" "$LINENO" 5
+fi
+
+
 fi
 
 if test "$PORTNAME" = "win32" ; then
@@ -14683,7 +14787,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -14729,7 +14833,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -14753,7 +14857,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -14798,7 +14902,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -14822,7 +14926,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
diff --git a/configure.in b/configure.in
index dde3eec89f..393a820cc2 100644
--- a/configure.in
+++ b/configure.in
@@ -915,6 +915,12 @@ fi
 AC_SUBST(with_uuid)
 AC_SUBST(UUID_EXTRA_OBJS)
 
+#
+# LZ4
+#
+PGAC_ARG_BOOL(with, lz4, yes,
+              [do not build with LZ4 support])
+AC_SUBST(with_lz4)
 
 #
 # XML
@@ -1263,6 +1269,14 @@ elif test "$with_uuid" = ossp ; then
 fi
 AC_SUBST(UUID_LIBS)
 
+# for lz4 compression support
+if test "$with_lz4" = yes ; then
+  AC_CHECK_LIB(lz4, LZ4_sizeofState,
+    [COMPRESSION_LIBS=" -llz4"],
+    [AC_MSG_ERROR([library 'lz4' is required for LZ4 compression support])])
+  AC_DEFINE([HAVE_LZ4], 1, [Define to 1 to build with LZ4 support])
+fi
+LIBS="$LIBS$COMPRESSION_LIBS"
 
 ##
 ## Header files
@@ -1443,6 +1457,11 @@ elif test "$with_uuid" = ossp ; then
       [AC_MSG_ERROR([header file <ossp/uuid.h> or <uuid.h> is required for OSSP UUID])])])
 fi
 
+# for lz4 compression support
+if test "$with_lz4" = yes ; then
+  AC_CHECK_HEADER(lz4.h, [], [AC_MSG_ERROR([header file <lz4.h> is required for LZ4 support])])
+fi
+
 if test "$PORTNAME" = "win32" ; then
    AC_CHECK_HEADERS(crtdefs.h)
 fi
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index c91e3e1550..81bd26b653 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1793,6 +1793,42 @@ include_dir 'conf.d'
      <title>Disk</title>
 
      <variablelist>
+
+     <varlistentry id="guc-compression-algorithm" xreflabel="compression_algorithm">
+      <term><varname>compression_algorithm</varname> (<type>enum</type>)
+      <indexterm>
+       <primary><varname>compression_algorithm</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Which compression algorithm to use for compressing
+        <acronym>TOAST</acronym> data and when
+        <xref linkend="guc-wal-compression"/> is turned on also for
+        <acronym>WAL</acronym>.
+        Possible values are:
+       </para>
+       <itemizedlist>
+        <listitem>
+        <para>
+         <literal>pglz</literal> (the internal PostgreSQL LZ family compression algorithm)
+        </para>
+        </listitem>
+        <listitem>
+        <para>
+         <literal>lz4</literal> (the LZ4 compression algorithm)
+        </para>
+        </listitem>
+       </itemizedlist>
+       <para>
+        Not all of these choices are available on all platforms.
+        The default is the pglz algorithm which is the one used by PostgrSQL
+        version 12 and earlier.
+        Only superusers can change this setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-temp-file-limit" xreflabel="temp_file_limit">
       <term><varname>temp_file_limit</varname> (<type>integer</type>)
       <indexterm>
@@ -2728,6 +2764,8 @@ include_dir 'conf.d'
         <xref linkend="guc-full-page-writes"/> is on or during a base backup.
         A compressed page image will be decompressed during WAL replay.
         The default value is <literal>off</literal>.
+        The compression used is the one specified by
+        <xref linkend="guc-compression-algorithm"/>
         Only superusers can change this setting.
        </para>
 
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 1047c77a63..306a0bb3d3 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -394,9 +394,8 @@ Further details appear in <xref linkend="storage-toast-inmemory"/>.
 
 <para>
 The compression technique used for either in-line or out-of-line compressed
-data is a fairly simple and very fast member
-of the LZ family of compression techniques.  See
-<filename>src/common/pg_lzcompress.c</filename> for the details.
+data is chosen based on the <xref linkend="guc-compression-algorithm"/>
+setting.
 </para>
 
 <sect2 id="storage-toast-ondisk">
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index dc3f207e1c..8c3d38db1c 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -195,6 +195,7 @@ with_libxslt	= @with_libxslt@
 with_llvm	= @with_llvm@
 with_system_tzdata = @with_system_tzdata@
 with_uuid	= @with_uuid@
+with_lz4	= @with_lz4@
 with_zlib	= @with_zlib@
 enable_rpath	= @enable_rpath@
 enable_nls	= @enable_nls@
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 74233bb931..14b64936ab 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -52,7 +52,20 @@
 typedef struct toast_compress_header
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
-	int32		rawsize;
+	/*
+	 * The length cannot be more than 1GB due to general toast limitations
+	 * we have the 2 high bits to encode aditional information.
+	 *
+	 * We use the last (highest) bit to mark this toast as the "new
+	 * compression format" as the new pg_compress has it's own header
+	 * which and the original pglz format is not distiguishable in any
+	 * way from the format used by pg_compress. Thanks to this information
+	 * the toast_decompress_datum can pick to either directly use
+	 * pglz_decompress directly when dealing with data writen by older
+	 * versions of postgres or let pg_decompress to autodetect format.
+	 */
+	int32		rawsize:31;
+	uint32		cformat:1;
 } toast_compress_header;
 
 /*
@@ -61,10 +74,13 @@ typedef struct toast_compress_header
  */
 #define TOAST_COMPRESS_HDRSZ		((int32) sizeof(toast_compress_header))
 #define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
+#define TOAST_COMPRESS_CFORMAT(ptr) (((toast_compress_header *) (ptr))->cformat)
 #define TOAST_COMPRESS_RAWDATA(ptr) \
 	(((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
 #define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
 	(((toast_compress_header *) (ptr))->rawsize = (len))
+#define TOAST_COMPRESS_SET_CFORMAT(ptr, fmt) \
+	(((toast_compress_header *) (ptr))->cformat = (fmt))
 
 static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
 static Datum toast_save_datum(Relation rel, Datum value,
@@ -385,7 +401,7 @@ toast_raw_datum_size(Datum value)
 	else if (VARATT_IS_COMPRESSED(attr))
 	{
 		/* here, va_rawsize is just the payload size */
-		result = VARRAWSIZE_4B_C(attr) + VARHDRSZ;
+		result = TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ;
 	}
 	else if (VARATT_IS_SHORT(attr))
 	{
@@ -1363,6 +1379,7 @@ toast_compress_datum(Datum value)
 {
 	struct varlena *tmp;
 	int32		valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+	int32		buffer_capacity;
 	int32		len;
 
 	Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
@@ -1376,11 +1393,11 @@ toast_compress_datum(Datum value)
 		valsize > PGLZ_strategy_default->max_input_size)
 		return PointerGetDatum(NULL);
 
-	tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
-									TOAST_COMPRESS_HDRSZ);
+	buffer_capacity = pg_compress_bound(valsize);
+	tmp = (struct varlena *) palloc(buffer_capacity + TOAST_COMPRESS_HDRSZ);
 
 	/*
-	 * We recheck the actual size even if pglz_compress() reports success,
+	 * We recheck the actual size even if pg_compress() reports success,
 	 * because it might be satisfied with having saved as little as one byte
 	 * in the compressed data --- which could turn into a net loss once you
 	 * consider header and alignment padding.  Worst case, the compressed
@@ -1389,14 +1406,17 @@ toast_compress_datum(Datum value)
 	 * only one header byte and no padding if the value is short enough.  So
 	 * we insist on a savings of more than 2 bytes to ensure we have a gain.
 	 */
-	len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
-						valsize,
-						TOAST_COMPRESS_RAWDATA(tmp),
-						PGLZ_strategy_default);
+	len = pg_compress(VARDATA_ANY(DatumGetPointer(value)),
+					  valsize,
+					  TOAST_COMPRESS_RAWDATA(tmp),
+					  buffer_capacity,
+					  PGLZ_strategy_default);
+
 	if (len >= 0 &&
 		len + TOAST_COMPRESS_HDRSZ < valsize - 2)
 	{
 		TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
+		TOAST_COMPRESS_SET_CFORMAT(tmp, 1);
 		SET_VARSIZE_COMPRESSED(tmp, len + TOAST_COMPRESS_HDRSZ);
 		/* successful compression */
 		return PointerGetDatum(tmp);
@@ -1520,7 +1540,7 @@ toast_save_datum(Relation rel, Datum value,
 		data_p = VARDATA(dval);
 		data_todo = VARSIZE(dval) - VARHDRSZ;
 		/* rawsize in a compressed datum is just the size of the payload */
-		toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
+		toast_pointer.va_rawsize = TOAST_COMPRESS_RAWSIZE(dval) + VARHDRSZ;
 		toast_pointer.va_extsize = data_todo;
 		/* Assert that the numbers look like it's compressed */
 		Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
@@ -2277,18 +2297,40 @@ static struct varlena *
 toast_decompress_datum(struct varlena *attr)
 {
 	struct varlena *result;
+	uint32			compression_format;
+	int32			raw_size;
 
 	Assert(VARATT_IS_COMPRESSED(attr));
 
-	result = (struct varlena *)
-		palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
-	SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+	raw_size = TOAST_COMPRESS_RAWSIZE(attr);
 
-	if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
-						VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
-						VARDATA(result),
-						TOAST_COMPRESS_RAWSIZE(attr), true) < 0)
-		elog(ERROR, "compressed data is corrupted");
+	compression_format = TOAST_COMPRESS_CFORMAT(attr);
+
+	result = (struct varlena *) palloc(raw_size + VARHDRSZ);
+	SET_VARSIZE(result, raw_size + VARHDRSZ);
+
+	/*
+	 * Support for legacy compressed TOAST format which is always using pglz.
+	 */
+	switch (compression_format)
+	{
+		case 0:
+			if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
+								VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+								VARDATA(result),
+								raw_size, true) < 0)
+				elog(ERROR, "compressed data is corrupted");
+			break;
+		case 1:
+			if (pg_decompress(TOAST_COMPRESS_RAWDATA(attr),
+							  VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+							  VARDATA(result),
+							  raw_size, true) < 0)
+				elog(ERROR, "compressed data is corrupted");
+			break;
+		default:
+			pg_unreachable();
+	}
 
 	return result;
 }
@@ -2311,10 +2353,10 @@ toast_decompress_datum_slice(struct varlena *attr, int32 slicelength)
 
 	result = (struct varlena *) palloc(slicelength + VARHDRSZ);
 
-	rawsize = pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
-							  VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
-							  VARDATA(result),
-							  slicelength, false);
+	rawsize = pg_decompress(TOAST_COMPRESS_RAWDATA(attr),
+							VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+							VARDATA(result),
+							slicelength, false);
 	if (rawsize < 0)
 		elog(ERROR, "compressed data is corrupted");
 
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 3ec67d468b..76decbf426 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -830,11 +830,12 @@ XLogCompressBackupBlock(char *page, uint16 hole_offset, uint16 hole_length,
 		source = page;
 
 	/*
-	 * We recheck the actual size even if pglz_compress() reports success and
+	 * We recheck the actual size even if pg_compress() reports success and
 	 * see if the number of bytes saved by compression is larger than the
 	 * length of extra data needed for the compressed version of block image.
 	 */
-	len = pglz_compress(source, orig_len, dest, PGLZ_strategy_default);
+	len = pg_compress(source, orig_len, dest, PGLZ_MAX_BLCKSZ,
+					  PGLZ_strategy_default);
 	if (len >= 0 &&
 		len + extra_bytes < orig_len)
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fc463601ff..04d99b97bf 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -42,6 +42,7 @@
 #include "commands/vacuum.h"
 #include "commands/variable.h"
 #include "commands/trigger.h"
+#include "common/pg_lzcompress.h"
 #include "common/string.h"
 #include "funcapi.h"
 #include "jit/jit.h"
@@ -470,6 +471,14 @@ static struct config_enum_entry shared_memory_options[] = {
 	{NULL, 0, false}
 };
 
+static const struct config_enum_entry compression_algorithm_options[] = {
+	{"pglz", COMPRESS_ALGO_PGLZ, false},
+#ifdef HAVE_LZ4
+	{"lz4", COMPRESS_ALGO_LZ4, false},
+#endif
+	{NULL, 0, false}
+};
+
 /*
  * Options for enum values stored in other modules
  */
@@ -4540,7 +4549,7 @@ static struct config_enum ConfigureNamesEnum[] =
 
 	{
 		{"ssl_max_protocol_version", PGC_SIGHUP, CONN_AUTH_SSL,
-			gettext_noop("Sets the maximum SSL/TLS protocol version to use."),
+			gettext_noop("Chooses the compression algorithm for TOAST and WAL."),
 			NULL,
 			GUC_SUPERUSER_ONLY
 		},
@@ -4550,6 +4559,18 @@ static struct config_enum ConfigureNamesEnum[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"compression_algorithm", PGC_SIGHUP, RESOURCES_DISK,
+			gettext_noop("Sets the maximum SSL/TLS protocol version to use."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&compression_algorithm,
+		COMPRESS_ALGO_PGLZ,
+		compression_algorithm_options,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/common/pg_lzcompress.c b/src/common/pg_lzcompress.c
index 988b3987d0..8e8b966d67 100644
--- a/src/common/pg_lzcompress.c
+++ b/src/common/pg_lzcompress.c
@@ -187,6 +187,7 @@
 
 #include "common/pg_lzcompress.h"
 
+int compression_algorithm = COMPRESS_ALGO_PGLZ;
 
 /* ----------
  * Local definitions
@@ -505,8 +506,8 @@ pglz_find_match(int16 *hstart, const char *input, const char *end,
  *		bytes written in buffer dest, or -1 if compression fails.
  * ----------
  */
-int32
-pglz_compress(const char *source, int32 slen, char *dest,
+static int32
+pglz_compress(const char *source, int32 slen, char *dest, int32 capacity,
 			  const PGLZ_Strategy *strategy)
 {
 	unsigned char *bp = (unsigned char *) dest;
@@ -771,3 +772,132 @@ pglz_decompress(const char *source, int32 slen, char *dest,
 	 */
 	return (char *) dp - dest;
 }
+
+#ifdef HAVE_LZ4
+#include "utils/elog.h"
+
+static int32
+lz4_compress(const char *source, int32 slen, char *dest, int32 capacity,
+			 const PGLZ_Strategy *strategy)
+{
+	int	ret;
+
+	ret = LZ4_compress_default(source, dest, slen, capacity);
+
+	/*
+	 * In case of compression error, return -1 which callers should take
+	 * as incompressible data.
+	 */
+	if (ret	== 0)
+		return -1;
+
+	return ret;
+}
+
+int32
+lz4_decompress(const char *source, int32 slen, char *dest,
+			   int32 rawsize, bool check_complete)
+{
+	int	ret;
+
+	if (check_complete)
+	{
+		ret = LZ4_decompress_safe(source, dest, slen, rawsize);
+
+		/*
+		 * Check we decompressed the right amount.
+		 */
+		if (ret != rawsize)
+			return -1;
+	}
+	else
+		ret = LZ4_decompress_safe_partial(source, dest, slen, rawsize,
+										  rawsize);
+
+	return ret;
+}
+#endif
+
+/*
+ * Compress using configured algorithm
+ */
+int32
+pg_compress(const char *source, int32 slen, char *dest, int32 capacity,
+			const PGLZ_Strategy *strategy)
+{
+	int32	ret;
+
+	switch (compression_algorithm)
+	{
+		case COMPRESS_ALGO_PGLZ:
+			dest[0] = COMPRESS_ALGO_PGLZ;
+			ret = pglz_compress(source, slen, &dest[1], capacity - 1,
+								strategy);
+			break;
+#ifdef HAVE_LZ4
+		case COMPRESS_ALGO_LZ4:
+			dest[0] = COMPRESS_ALGO_LZ4;
+			ret = lz4_compress(source, slen, &dest[1], capacity - 1,
+							   strategy);
+			break;
+#endif
+		default:
+			pg_unreachable();
+	}
+
+	if (ret >= 0)
+		return ret + 1;
+
+	return ret;
+}
+
+/*
+ * Decompress data compressed with one of the supported algorithms.
+ */
+int32
+pg_decompress(const char *source, int32 slen, char *dest,
+			  int32 rawsize, bool check_complete)
+{
+	switch (source[0])
+	{
+		case COMPRESS_ALGO_PGLZ:
+			return pglz_decompress(&source[1], slen - 1, dest, rawsize,
+								   check_complete);
+#ifdef HAVE_LZ4
+		case COMPRESS_ALGO_LZ4:
+			return lz4_decompress(&source[1], slen - 1, dest, rawsize,
+								  check_complete);
+#endif
+		default:
+			Assert(false); /* XXX: Can't elog here. */
+	}
+
+	pg_unreachable();
+}
+
+/*
+ * Compute the buffer size required by pg_compress for a configured algorithm
+ * including our header size.
+ */
+int32
+pg_compress_bound(int32 slen)
+{
+	switch (compression_algorithm)
+	{
+		case COMPRESS_ALGO_PGLZ:
+			/*
+			 * For pglz we allow 4 bytes for overrun before detecting
+			 * compression failure.
+			 */
+			return slen + 4 + SIZEOF_PG_COMPRESS_HEADER;
+#ifdef HAVE_LZ4
+		case COMPRESS_ALGO_LZ4:
+			/* LZ4 provides direct interface for calculating needed space. */
+			return LZ4_compressBound(slen) + SIZEOF_PG_COMPRESS_HEADER;
+#endif
+		default:
+			pg_unreachable();
+	}
+
+	pg_unreachable();
+}
diff --git a/src/include/common/pg_lzcompress.h b/src/include/common/pg_lzcompress.h
index 555576436c..04453f4574 100644
--- a/src/include/common/pg_lzcompress.h
+++ b/src/include/common/pg_lzcompress.h
@@ -10,15 +10,35 @@
 #ifndef _PG_LZCOMPRESS_H_
 #define _PG_LZCOMPRESS_H_
 
+#ifdef HAVE_LZ4
+#include "lz4.h"
 
-/* ----------
- * PGLZ_MAX_OUTPUT -
+#define SIZEOF_PG_COMPRESS_HEADER	1
+/*
+ * Macro version of pg_compress_bound, less precise, usable in places where
+ * we need compile time size information.
+ * We add +1 compared to what algorithms need because that's the size of
+ * pg_compress header.
+ */
+#define PGLZ_MAX_OUTPUT(_dlen)	(Max((_dlen) + 4, LZ4_COMPRESSBOUND(_dlen)) + \
+								 SIZEOF_PG_COMPRESS_HEADER)
+#else
+#define PGLZ_MAX_OUTPUT(_dlen)	((_dlen) + 4 + SIZEOF_PG_COMPRESS_HEADER)
+#endif
+
+/*
+ * PGLZCompressionAlgo
  *
- *		Macro to compute the buffer size required by pglz_compress().
- *		We allow 4 bytes for overrun before detecting compression failure.
- * ----------
+ * Which algorithm to use for TOAST and WAL compression.
+ *
+ * COMPRESS_ALGO_PGLZ - use the builtin pglz algorithm
+ * COMPRESS_ALGO_LZ4 - use the LZ4 library
  */
-#define PGLZ_MAX_OUTPUT(_dlen)			((_dlen) + 4)
+typedef enum
+{
+	COMPRESS_ALGO_PGLZ = 0,
+	COMPRESS_ALGO_LZ4
+}	PGLZCompressAlgo;
 
 
 /* ----------
@@ -78,14 +98,25 @@ typedef struct PGLZ_Strategy
 extern const PGLZ_Strategy *const PGLZ_strategy_default;
 extern const PGLZ_Strategy *const PGLZ_strategy_always;
 
+/*
+ * Compression algorithm.
+ */
+
+extern int	compression_algorithm;
 
 /* ----------
  * Global function declarations
  * ----------
  */
-extern int32 pglz_compress(const char *source, int32 slen, char *dest,
-						   const PGLZ_Strategy *strategy);
 extern int32 pglz_decompress(const char *source, int32 slen, char *dest,
-							 int32 rawsize, bool check_complete);
+				int32 rawsize, bool check_complete);
+extern int32 lz4_decompress(const char *source, int32 slen, char *dest,
+				int32 rawsize, bool check_complete);
+extern int32 pg_compress(const char *source, int32 slen, char *dest, int32 capacity,
+			const PGLZ_Strategy *strategy);
+extern int32 pg_decompress(const char *source, int32 slen, char *dest,
+			  int32 rawsize, bool check_complete);
+
+extern int32 pg_compress_bound(int32 slen);
 
 #endif							/* _PG_LZCOMPRESS_H_ */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 512213aa32..5e37aab4c5 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -718,6 +718,9 @@
 /* Define to 1 if you have the <uuid/uuid.h> header file. */
 #undef HAVE_UUID_UUID_H
 
+/* Define to 1 to build with LZ4 support. */
+#undef HAVE_LZ4
+
 /* Define to 1 if you have the <wchar.h> header file. */
 #undef HAVE_WCHAR_H
 
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 057a3413ac..9578588880 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -145,7 +145,8 @@ typedef union
 	struct						/* Compressed-in-line format */
 	{
 		uint32		va_header;
-		uint32		va_rawsize; /* Original data size (excludes header) */
+		uint32		va_rawsize:31; /* Original data size (excludes header) */
+		int			va_cformat:1;
 		char		va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
 	}			va_compressed;
 } varattrib_4b;
diff --git a/src/test/Makefile b/src/test/Makefile
index efb206aa75..c6287e7a04 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -32,12 +32,18 @@ ifneq (,$(filter ssl,$(PG_TEST_EXTRA)))
 SUBDIRS += ssl
 endif
 endif
+ifeq ($(with_lz4),yes)
+ifneq (,$(filter toast,$(PG_TEST_EXTRA)))
+SUBDIRS += toast
+endif
+endif
 
 # We don't build or execute these by default, but we do want "make
 # clean" etc to recurse into them.  (We must filter out those that we
 # have conditionally included into SUBDIRS above, else there will be
 # make confusion.)
-ALWAYS_SUBDIRS = $(filter-out $(SUBDIRS),examples kerberos ldap locale thread ssl)
+ALWAYS_SUBDIRS = $(filter-out $(SUBDIRS),examples kerberos ldap locale thread \
+				 ssl toast)
 
 # We want to recurse to all subdirs for all standard targets, except that
 # installcheck and install should not recurse into the subdirectory "modules".
diff --git a/src/test/toast/.gitignore b/src/test/toast/.gitignore
new file mode 100644
index 0000000000..871e943d50
--- /dev/null
+++ b/src/test/toast/.gitignore
@@ -0,0 +1,2 @@
+# Generated by test suite
+/tmp_check/
diff --git a/src/test/toast/Makefile b/src/test/toast/Makefile
new file mode 100644
index 0000000000..5a1ff09a13
--- /dev/null
+++ b/src/test/toast/Makefile
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/toast
+#
+# Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/toast/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/recovery
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+export with_lz4
+
+check:
+	$(prove_check)
+
+installcheck:
+	$(prove_installcheck)
+
+clean distclean maintainer-clean:
+	rm -rf tmp_check
diff --git a/src/test/toast/README b/src/test/toast/README
new file mode 100644
index 0000000000..8802ecbe06
--- /dev/null
+++ b/src/test/toast/README
@@ -0,0 +1,25 @@
+src/test/tosat/README
+
+Regression tests for TOAST compression
+======================================
+
+This directory contains a test suite for TOAST compression and replication.
+
+Running the tests
+=================
+
+NOTE: You must have given the --enable-tap-tests argument to configure.
+Also, to use "make installcheck", you must have built and installed
+contrib/test_decoding in addition to the core code.
+
+Run
+    make check
+or
+    make installcheck
+You can use "make installcheck" if you previously did "make install".
+In that case, the code in the installation tree is tested.  With
+"make check", a temporary installation tree is built from the current
+sources and then tested.
+
+Either way, this test initializes, starts, and stops several test Postgres
+clusters.
diff --git a/src/test/toast/t/001_lz4.pl b/src/test/toast/t/001_lz4.pl
new file mode 100644
index 0000000000..89f7cd177f
--- /dev/null
+++ b/src/test/toast/t/001_lz4.pl
@@ -0,0 +1,124 @@
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More;
+
+use File::Copy;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+if ($ENV{with_lz4} eq 'yes')
+{
+	plan tests => 10;
+}
+else
+{
+	plan skip_all => 'LZ4 not supported by this build';
+}
+
+#### Set up the server.
+note "setting up data directory";
+my $node = get_new_node('master');
+$node->init;
+$node->append_conf('postgresql.conf', qq[
+compression_algorithm = lz4
+wal_compression = on
+]);
+$node->start;
+
+# Run this before we lock down access below.
+my $result = $node->safe_psql('postgres', "SHOW compression_algorithm");
+is($result, 'lz4', 'compression_algorithm set to lz4');
+
+$node->safe_psql('postgres',
+	qq[
+	CREATE TABLE toast_test (
+		id int,
+		data text
+	)]);
+
+$node->safe_psql('postgres',
+	'ALTER TABLE toast_test ALTER COLUMN data SET STORAGE MAIN');
+
+# This will actually be compressed inline as it's easy to compress
+$node->safe_psql('postgres',
+qq[
+	INSERT INTO toast_test
+		SELECT n, repeat('toasted', 1000)
+		  FROM generate_series(1, 100) s(n);
+]);
+
+
+my $toast_size = $node->safe_psql('postgres',
+qq[
+	SELECT pg_relation_size((SELECT reltoastrelid FROM pg_catalog.pg_class WHERE relname = 'toast_test'));
+]);
+
+ok($toast_size == 0, 'toast table is used');
+
+$node->safe_psql('postgres',
+	'ALTER TABLE toast_test ALTER COLUMN data SET STORAGE EXTENDED');
+
+# Something less easily compressable so that it's in TOAST table
+$node->safe_psql('postgres',
+qq[
+	INSERT INTO toast_test
+		SELECT n, (SELECT string_agg(md5(t::text),'')
+		             FROM generate_series(1, 200) q(t))
+		  FROM generate_series(101, 200) s(n);
+]);
+
+$toast_size = $node->safe_psql('postgres',
+qq[
+	SELECT pg_relation_size((SELECT reltoastrelid FROM pg_catalog.pg_class WHERE relname = 'toast_test'));
+]);
+
+ok($toast_size > 0, 'toast table is used');
+
+# check if we can select data
+is($node->safe_psql('postgres',
+		qq[SELECT id, length(data) FROM toast_test WHERE id = 1]),
+	'1|7000', 'can select compressed data');
+is($node->safe_psql('postgres',
+		qq[SELECT id, length(data) FROM toast_test WHERE id = 200]),
+	'200|6400', 'can select TOAST compressed data');
+
+# test slicing
+is($node->safe_psql('postgres',
+		qq[SELECT id, substr(data, 1, 10) FROM toast_test WHERE id = 50]),
+	'50|toastedtoa', 'slicing of compressed data works');
+
+is($node->safe_psql('postgres',
+		qq[SELECT id, substr(data, 1, 10) FROM toast_test WHERE id = 150]),
+	'150|c4ca4238a0', 'slicing of TOAST works');
+
+$node->append_conf('postgresql.conf', qq[
+compression_algorithm = pglz
+]);
+$node->reload;
+
+# Run this before we lock down access below.
+$result = $node->safe_psql('postgres', "SHOW compression_algorithm");
+is($result, 'pglz', 'compression_algorithm set to pglz');
+
+$node->safe_psql('postgres',
+qq[
+	INSERT INTO toast_test
+		SELECT n, (SELECT string_agg(md5(t::text),'')
+		             FROM generate_series(1, 200) q(t))
+		  FROM generate_series(201, 300) s(n);
+]);
+
+is($node->safe_psql('postgres',
+		qq[SELECT id, length(data) FROM toast_test WHERE id IN (200, 201)]),
+q[200|6400
+201|6400], 'can select TOAST with different compression for different rows');
+
+is($node->safe_psql('postgres',
+		qq[SELECT id, substr(data, 1, 10) FROM toast_test WHERE id IN (150, 250)]),
+q[150|c4ca4238a0
+250|c4ca4238a0], 'slicing of TOAST works with different compression for different row');
+
+done_testing();
-- 
2.20.1


--------------E1FE46135503D8F9FB86E4E2--





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
@ 2024-01-22 06:09 Peter Smith <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Peter Smith @ 2024-01-22 06:09 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Tomas Vondra <[email protected]>; Ashutosh Bapat <[email protected]>; [email protected]

2024-01 Commitfest.

Hi, This patch has a CF status of "Needs Review" [1], but it seems
there were CFbot test failures last time it was run [2]. Please have a
look and post an updated version if necessary.

======
[1] https://commitfest.postgresql.org/46/4538/
[2] https://cirrus-ci.com/github/postgresql-cfbot/postgresql/commitfest/46/4538

Kind Regards,
Peter Smith.





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* [PATCH v21 3/8] Row pattern recognition patch (rewriter).
@ 2024-08-26 04:32 Tatsuo Ishii <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Tatsuo Ishii @ 2024-08-26 04:32 UTC (permalink / raw)

---
 src/backend/utils/adt/ruleutils.c | 103 ++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 00eda1b34c..dff7e169e7 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -426,6 +426,10 @@ static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
 								 bool omit_parens, deparse_context *context);
 static void get_rule_orderby(List *orderList, List *targetList,
 							 bool force_colno, deparse_context *context);
+static void get_rule_pattern(List *patternVariable, List *patternRegexp,
+							 bool force_colno, deparse_context *context);
+static void get_rule_define(List *defineClause, List *patternVariables,
+							bool force_colno, deparse_context *context);
 static void get_rule_windowclause(Query *query, deparse_context *context);
 static void get_rule_windowspec(WindowClause *wc, List *targetList,
 								deparse_context *context);
@@ -6460,6 +6464,67 @@ get_rule_orderby(List *orderList, List *targetList,
 	}
 }
 
+/*
+ * Display a PATTERN clause.
+ */
+static void
+get_rule_pattern(List *patternVariable, List *patternRegexp,
+				 bool force_colno, deparse_context *context)
+{
+	StringInfo	buf = context->buf;
+	const char *sep;
+	ListCell   *lc_var,
+			   *lc_reg = list_head(patternRegexp);
+
+	sep = "";
+	appendStringInfoChar(buf, '(');
+	foreach(lc_var, patternVariable)
+	{
+		char	   *variable = strVal((String *) lfirst(lc_var));
+		char	   *regexp = NULL;
+
+		if (lc_reg != NULL)
+		{
+			regexp = strVal((String *) lfirst(lc_reg));
+
+			lc_reg = lnext(patternRegexp, lc_reg);
+		}
+
+		appendStringInfo(buf, "%s%s", sep, variable);
+		if (regexp !=NULL)
+			appendStringInfoString(buf, regexp);
+
+		sep = " ";
+	}
+	appendStringInfoChar(buf, ')');
+}
+
+/*
+ * Display a DEFINE clause.
+ */
+static void
+get_rule_define(List *defineClause, List *patternVariables,
+				bool force_colno, deparse_context *context)
+{
+	StringInfo	buf = context->buf;
+	const char *sep;
+	ListCell   *lc_var,
+			   *lc_def;
+
+	sep = "  ";
+	Assert(list_length(patternVariables) == list_length(defineClause));
+
+	forboth(lc_var, patternVariables, lc_def, defineClause)
+	{
+		char	   *varName = strVal(lfirst(lc_var));
+		TargetEntry *te = (TargetEntry *) lfirst(lc_def);
+
+		appendStringInfo(buf, "%s%s AS ", sep, varName);
+		get_rule_expr((Node *) te->expr, context, false);
+		sep = ",\n  ";
+	}
+}
+
 /*
  * Display a WINDOW clause.
  *
@@ -6597,6 +6662,44 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
 			appendStringInfoString(buf, "EXCLUDE GROUP ");
 		else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)
 			appendStringInfoString(buf, "EXCLUDE TIES ");
+		/* RPR */
+		if (wc->rpSkipTo == ST_NEXT_ROW)
+			appendStringInfoString(buf,
+								   "\n  AFTER MATCH SKIP TO NEXT ROW ");
+		else if (wc->rpSkipTo == ST_PAST_LAST_ROW)
+			appendStringInfoString(buf,
+								   "\n  AFTER MATCH SKIP PAST LAST ROW ");
+		else if (wc->rpSkipTo == ST_FIRST_VARIABLE)
+			appendStringInfo(buf,
+							 "\n  AFTER MATCH SKIP TO FIRST %s ",
+							 wc->rpSkipVariable);
+		else if (wc->rpSkipTo == ST_LAST_VARIABLE)
+			appendStringInfo(buf,
+							 "\n  AFTER MATCH SKIP TO LAST %s ",
+							 wc->rpSkipVariable);
+		else if (wc->rpSkipTo == ST_VARIABLE)
+			appendStringInfo(buf,
+							 "\n  AFTER MATCH SKIP TO %s ",
+							 wc->rpSkipVariable);
+
+		if (wc->initial)
+			appendStringInfoString(buf, "\n  INITIAL");
+
+		if (wc->patternVariable)
+		{
+			appendStringInfoString(buf, "\n  PATTERN ");
+			get_rule_pattern(wc->patternVariable, wc->patternRegexp,
+							 false, context);
+		}
+
+		if (wc->defineClause)
+		{
+			appendStringInfoString(buf, "\n  DEFINE\n");
+			get_rule_define(wc->defineClause, wc->patternVariable,
+							false, context);
+			appendStringInfoChar(buf, ' ');
+		}
+
 		/* we will now have a trailing space; remove it */
 		buf->len--;
 	}
-- 
2.25.1


----Next_Part(Mon_Aug_26_13_39_47_2024_878)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="v21-0004-Row-pattern-recognition-patch-planner.patch"



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
@ 2025-03-07 00:58 ` Jeff Davis <[email protected]>
  2025-03-07 01:47   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2 siblings, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-07 00:58 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Thu, 2025-03-06 at 08:49 -0500, Corey Huinker wrote:
> Unless some check was being done by the 'foo.bar'::regclass cast, I
> understand why we should add one.

"For schemas, allows access to objects contained in the schema
(assuming that the objects' own privilege requirements are also met).
Essentially this allows the grantee to “look up” objects within the
schema. Without this permission, it is still possible to see the object
names, e.g., by querying system catalogs. Also, after revoking this
permission, existing sessions might have statements that have
previously performed this lookup, so this is not a completely secure
way to prevent object access."

https://www.postgresql.org/docs/current/ddl-priv.html

The above text indicates that we should do the check, but also that
it's not terribly important for actual security.

> If we do, we'll want to change downgrade the following errors to
> warn+return false:

Perhaps we should consider the schemaname/relname change as one patch,
which maintains relation lookup failures as hard ERRORs, and a
"downgrade errors to warnings" as a separate patch.

> I agree, but the thread conversation had already shifted to doing
> just one single call to pg_stats, so this was just a demonstration.

It's a simple patch and the discussion seems to be shifting toward
parallelism[1] rather than batching[2]. In that case it still seems
like a good change to me, so I'm inclined to commit it after I verify
that it improves performance.

Regards,
	Jeff Davis

[1] 
https://www.postgresql.org/message-id/[email protected]

[2] https://www.postgresql.org/message-id/[email protected]





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 00:58 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-07 01:47   ` Corey Huinker <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Corey Huinker @ 2025-03-07 01:47 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
>
> https://www.postgresql.org/docs/current/ddl-priv.html
>
> The above text indicates that we should do the check, but also that
> it's not terribly important for actual security.
>

Ok, I'm convinced.



>
> > If we do, we'll want to change downgrade the following errors to
> > warn+return false:
>
> Perhaps we should consider the schemaname/relname change as one patch,
> which maintains relation lookup failures as hard ERRORs, and a
> "downgrade errors to warnings" as a separate patch.
>

+1


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
@ 2025-03-07 01:42 ` Jeff Davis <[email protected]>
  2025-03-07 02:56   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-07 16:22   ` Re: Statistics Import and Export Andres Freund <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2 siblings, 3 replies; 88+ messages in thread

From: Jeff Davis @ 2025-03-07 01:42 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; Andres Freund <[email protected]>; +Cc: Corey Huinker <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Thu, 2025-03-06 at 11:15 -0500, Robert Haas wrote:
> To be honest, I am a bit surprised that we decided to enable this by
> default. It's not obvious to me that statistics should be regarded as
> part of the database in the same way that table definitions or table
> data are. That said, I'm not overwhelmingly opposed to that choice.
> However, even if it's the right choice in theory, we should maybe
> rethink if it's going to be too slow or use too much memory.

I don't have a strong opinion about whether stats will be opt-out or
opt-in for v18, but if they are opt-in, we would need to adjust the
available options a bit.

At minimum, we would need to at least add the option "--with-
statistics", because right now the only way to explicitly request stats
is to say "--statistics-only".

To generalize this concept: for each of {schema, data, stats} users
might want "yes", "no", or "only".

If we use this options scheme, it would be easy to change the default
for stats independently of the other options, if necessary, without
surprising consequences.

Patch attached. This patch does NOT change the default; stats are still
opt-out. But it makes it easier for users to start specifying what they
want or not explicitly, or to rely on the defaults if they prefer.

Note that the patch would mean we go from 2 options in v17:
  --{schema|data}-only

to 9 options in v18:
  --{with|no}-{schema|data|stats} and
  --{schema|data|stats}-only

I suggest we adjust the options now with something resembling the
attached patch and decide on changing the default sometime during beta.

Regards,
	Jeff Davis



Attachments:

  [text/x-patch] v1-0001-Add-pg_dump-with-X-options.patch (13.0K, ../../[email protected]/2-v1-0001-Add-pg_dump-with-X-options.patch)
  download | inline diff:
From c47fc9e570ddd083097f4bfc708465cf644f48c2 Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Thu, 6 Mar 2025 17:35:41 -0800
Subject: [PATCH v1] Add pg_dump --with-X options.

---
 doc/src/sgml/ref/pg_dump.sgml    | 27 +++++++++++++++++++
 doc/src/sgml/ref/pg_dumpall.sgml | 27 +++++++++++++++++++
 doc/src/sgml/ref/pg_restore.sgml | 27 +++++++++++++++++++
 src/bin/pg_dump/pg_dump.c        | 46 +++++++++++++++++++++++++++++---
 src/bin/pg_dump/pg_dumpall.c     | 12 +++++++++
 src/bin/pg_dump/pg_restore.c     | 44 +++++++++++++++++++++++++-----
 6 files changed, 173 insertions(+), 10 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 1975054d7bf..9eba285687e 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -1223,6 +1223,33 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--with-data</option></term>
+      <listitem>
+       <para>
+        Dump data. This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>--with-schema</option></term>
+      <listitem>
+       <para>
+        Dump schema (data definitions). This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>--with-statistics</option></term>
+      <listitem>
+       <para>
+        Dump statistics. This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--on-conflict-do-nothing</option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index c2fa5be9519..45f127f0dc9 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -551,6 +551,33 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--with-data</option></term>
+      <listitem>
+       <para>
+        Dump data. This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>--with-schema</option></term>
+      <listitem>
+       <para>
+        Dump schema (data definitions). This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>--with-statistics</option></term>
+      <listitem>
+       <para>
+        Dump statistics. This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--no-unlogged-table-data</option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 199ea3345f3..51e6411c8fe 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -795,6 +795,33 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--with-data</option></term>
+      <listitem>
+       <para>
+        Dump data. This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>--with-schema</option></term>
+      <listitem>
+       <para>
+        Dump schema (data definitions). This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>--with-statistics</option></term>
+      <listitem>
+       <para>
+        Dump statistics. This is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
        <term><option>--section=<replaceable class="parameter">sectionname</replaceable></option></term>
        <listitem>
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4f4ad2ee150..31c4ac1ee57 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -433,6 +433,9 @@ main(int argc, char **argv)
 	bool		data_only = false;
 	bool		schema_only = false;
 	bool		statistics_only = false;
+	bool		with_data = false;
+	bool		with_schema = false;
+	bool		with_statistics = false;
 	bool		no_data = false;
 	bool		no_schema = false;
 	bool		no_statistics = false;
@@ -508,6 +511,9 @@ main(int argc, char **argv)
 		{"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
 		{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
 		{"no-sync", no_argument, NULL, 7},
+		{"with-data", no_argument, NULL, 22},
+		{"with-schema", no_argument, NULL, 23},
+		{"with-statistics", no_argument, NULL, 24},
 		{"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
 		{"rows-per-insert", required_argument, NULL, 10},
 		{"include-foreign-data", required_argument, NULL, 11},
@@ -776,6 +782,18 @@ main(int argc, char **argv)
 				no_statistics = true;
 				break;
 
+			case 22:
+				with_data = true;
+				break;
+
+			case 23:
+				with_schema = true;
+				break;
+
+			case 24:
+				with_statistics = true;
+				break;
+
 			default:
 				/* getopt_long already emitted a complaint */
 				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -811,6 +829,7 @@ main(int argc, char **argv)
 	if (dopt.binary_upgrade)
 		dopt.sequence_data = 1;
 
+	/* reject conflicting "-only" options */
 	if (data_only && schema_only)
 		pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
 	if (schema_only && statistics_only)
@@ -818,6 +837,7 @@ main(int argc, char **argv)
 	if (data_only && statistics_only)
 		pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
 
+	/* reject conflicting "-only" and "no-" options */
 	if (data_only && no_data)
 		pg_fatal("options -a/--data-only and --no-data cannot be used together");
 	if (schema_only && no_schema)
@@ -825,6 +845,14 @@ main(int argc, char **argv)
 	if (statistics_only && no_statistics)
 		pg_fatal("options --statistics-only and --no-statistics cannot be used together");
 
+	/* reject conflicting "with-" and "no-" options */
+	if (with_data && no_data)
+		pg_fatal("options --with-data and --no-data cannot be used together");
+	if (with_schema && no_schema)
+		pg_fatal("options --with-schema and --no-schema cannot be used together");
+	if (with_statistics && no_statistics)
+		pg_fatal("options --with-statistics and --no-statistics cannot be used together");
+
 	if (schema_only && foreign_servers_include_patterns.head != NULL)
 		pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
 
@@ -837,10 +865,20 @@ main(int argc, char **argv)
 	if (dopt.if_exists && !dopt.outputClean)
 		pg_fatal("option --if-exists requires option -c/--clean");
 
-	/* set derivative flags */
-	dopt.dumpData = data_only || (!schema_only && !statistics_only && !no_data);
-	dopt.dumpSchema = schema_only || (!data_only && !statistics_only && !no_schema);
-	dopt.dumpStatistics = statistics_only || (!data_only && !schema_only && !no_statistics);
+	/*
+	 * Set derivative flags. An "-only" option may be overridden by an
+	 * explicit "with-" option; e.g. "--schema-only --with-statistics" will
+	 * include schema and statistics. Other ambiguous or nonsensical
+	 * combinations, e.g. "--schema-only --no-schema", will have already
+	 * caused an error in one of the checks above.
+	 */
+	dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
+					 (data_only || with_data)) && !no_data;
+	dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
+					   (schema_only || with_schema)) && !no_schema;
+	dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
+						   (statistics_only || with_statistics)) && !no_statistics;
+
 
 	/*
 	 * --inserts are already implied above if --column-inserts or
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index e0867242526..a7e8c0d2ad5 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -110,6 +110,9 @@ static int	no_subscriptions = 0;
 static int	no_toast_compression = 0;
 static int	no_unlogged_table_data = 0;
 static int	no_role_passwords = 0;
+static int	with_data = 0;
+static int	with_schema = 0;
+static int	with_statistics = 0;
 static int	server_version;
 static int	load_via_partition_root = 0;
 static int	on_conflict_do_nothing = 0;
@@ -182,6 +185,9 @@ main(int argc, char *argv[])
 		{"no-sync", no_argument, NULL, 4},
 		{"no-toast-compression", no_argument, &no_toast_compression, 1},
 		{"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
+		{"with-data", no_argument, &with_data, 1},
+		{"with-schema", no_argument, &with_schema, 1},
+		{"with-statistics", no_argument, &with_statistics, 1},
 		{"on-conflict-do-nothing", no_argument, &on_conflict_do_nothing, 1},
 		{"rows-per-insert", required_argument, NULL, 7},
 		{"statistics-only", no_argument, &statistics_only, 1},
@@ -471,6 +477,12 @@ main(int argc, char *argv[])
 		appendPQExpBufferStr(pgdumpopts, " --no-toast-compression");
 	if (no_unlogged_table_data)
 		appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data");
+	if (with_data)
+		appendPQExpBufferStr(pgdumpopts, " --with-data");
+	if (with_schema)
+		appendPQExpBufferStr(pgdumpopts, " --with-schema");
+	if (with_statistics)
+		appendPQExpBufferStr(pgdumpopts, " --with-statistics");
 	if (on_conflict_do_nothing)
 		appendPQExpBufferStr(pgdumpopts, " --on-conflict-do-nothing");
 	if (statistics_only)
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 13e4dc507e0..f22046127b7 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -81,6 +81,9 @@ main(int argc, char **argv)
 	static int	no_subscriptions = 0;
 	static int	strict_names = 0;
 	static int	statistics_only = 0;
+	static int	with_data = 0;
+	static int	with_schema = 0;
+	static int	with_statistics = 0;
 
 	struct option cmdopts[] = {
 		{"clean", 0, NULL, 'c'},
@@ -134,6 +137,9 @@ main(int argc, char **argv)
 		{"no-security-labels", no_argument, &no_security_labels, 1},
 		{"no-subscriptions", no_argument, &no_subscriptions, 1},
 		{"no-statistics", no_argument, &no_statistics, 1},
+		{"with-data", no_argument, &with_data, 1},
+		{"with-schema", no_argument, &with_schema, 1},
+		{"with-statistics", no_argument, &with_statistics, 1},
 		{"statistics-only", no_argument, &statistics_only, 1},
 		{"filter", required_argument, NULL, 4},
 
@@ -349,12 +355,29 @@ main(int argc, char **argv)
 		opts->useDB = 1;
 	}
 
+	/* reject conflicting "-only" options */
 	if (data_only && schema_only)
 		pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
-	if (data_only && statistics_only)
-		pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
 	if (schema_only && statistics_only)
 		pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
+	if (data_only && statistics_only)
+		pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
+
+	/* reject conflicting "-only" and "no-" options */
+	if (data_only && no_data)
+		pg_fatal("options -a/--data-only and --no-data cannot be used together");
+	if (schema_only && no_schema)
+		pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
+	if (statistics_only && no_statistics)
+		pg_fatal("options --statistics-only and --no-statistics cannot be used together");
+
+	/* reject conflicting "with-" and "no-" options */
+	if (with_data && no_data)
+		pg_fatal("options --with-data and --no-data cannot be used together");
+	if (with_schema && no_schema)
+		pg_fatal("options --with-schema and --no-schema cannot be used together");
+	if (with_statistics && no_statistics)
+		pg_fatal("options --with-statistics and --no-statistics cannot be used together");
 
 	if (data_only && opts->dropSchema)
 		pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
@@ -373,10 +396,19 @@ main(int argc, char **argv)
 	if (opts->single_txn && numWorkers > 1)
 		pg_fatal("cannot specify both --single-transaction and multiple jobs");
 
-	/* set derivative flags */
-	opts->dumpData = data_only || (!no_data && !schema_only && !statistics_only);
-	opts->dumpSchema = schema_only || (!no_schema && !data_only && !statistics_only);
-	opts->dumpStatistics = statistics_only || (!no_statistics && !data_only && !schema_only);
+	/*
+	 * Set derivative flags. An "-only" option may be overridden by an
+	 * explicit "with-" option; e.g. "--schema-only --with-statistics" will
+	 * include schema and statistics. Other ambiguous or nonsensical
+	 * combinations, e.g. "--schema-only --no-schema", will have already
+	 * caused an error in one of the checks above.
+	 */
+	opts->dumpData = ((opts->dumpData && !schema_only && !statistics_only) ||
+					  (data_only || with_data)) && !no_data;
+	opts->dumpSchema = ((opts->dumpSchema && !data_only && !statistics_only) ||
+						(schema_only || with_schema)) && !no_schema;
+	opts->dumpStatistics = ((opts->dumpStatistics && !schema_only && !data_only) ||
+							(statistics_only || with_statistics)) && !no_statistics;
 
 	opts->disable_triggers = disable_triggers;
 	opts->enable_row_security = enable_row_security;
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-07 02:56   ` Corey Huinker <[email protected]>
  2 siblings, 0 replies; 88+ messages in thread

From: Corey Huinker @ 2025-03-07 02:56 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
>
> Patch attached. This patch does NOT change the default; stats are still
> opt-out. But it makes it easier for users to start specifying what they
> want or not explicitly, or to rely on the defaults if they prefer.
>
> Note that the patch would mean we go from 2 options in v17:
>   --{schema|data}-only
>
> to 9 options in v18:
>   --{with|no}-{schema|data|stats} and
>   --{schema|data|stats}-only
>
> I suggest we adjust the options now with something resembling the
> attached patch and decide on changing the default sometime during beta.
>

Patch is straightforward. Comments are very clear as are docs. I can't see
anything that needs to be changed.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-07 16:22   ` Andres Freund <[email protected]>
  2025-03-07 16:53     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2 siblings, 1 reply; 88+ messages in thread

From: Andres Freund @ 2025-03-07 16:22 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Haas <[email protected]>; Corey Huinker <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Hi,

On 2025-03-06 17:42:30 -0800, Jeff Davis wrote:
> At minimum, we would need to at least add the option "--with-
> statistics", because right now the only way to explicitly request stats
> is to say "--statistics-only".

+1, this has been annoying me while testing.

I did get confused for a while because I used --statistics, as the opposite of
--no-statistics, while going back and forth between the two. Kinda appears to
work, but actually means --statistics-only, something rather different...


> To generalize this concept: for each of {schema, data, stats} users
> might want "yes", "no", or "only".

> If we use this options scheme, it would be easy to change the default
> for stats independently of the other options, if necessary, without
> surprising consequences.
> 
> Patch attached. This patch does NOT change the default; stats are still
> opt-out. But it makes it easier for users to start specifying what they
> want or not explicitly, or to rely on the defaults if they prefer.
> 
> Note that the patch would mean we go from 2 options in v17:
>   --{schema|data}-only
> 
> to 9 options in v18:
>   --{with|no}-{schema|data|stats} and
>   --{schema|data|stats}-only

Could we, instead of having --with-$foo, just use --$foo?

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 16:22   ` Re: Statistics Import and Export Andres Freund <[email protected]>
@ 2025-03-07 16:53     ` Jeff Davis <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Jeff Davis @ 2025-03-07 16:53 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Robert Haas <[email protected]>; Corey Huinker <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, 2025-03-07 at 11:22 -0500, Andres Freund wrote:
> +1, this has been annoying me while testing.

IIRC, originally someone had questioned the need for options that
expressed what was already the default, but I can't find it right now.
Regardless, now the need is clear enough.

> Could we, instead of having --with-$foo, just use --$foo?

That creates a conflict with the existing --schema option, which is a
namespace filter.

Another idea: we could use --definitions/--data/--statistics.

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-07 17:41   ` Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2 siblings, 1 reply; 88+ messages in thread

From: Robert Treat @ 2025-03-07 17:41 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Haas <[email protected]>; Andres Freund <[email protected]>; Corey Huinker <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Thu, Mar 6, 2025 at 8:42 PM Jeff Davis <[email protected]> wrote:
> On Thu, 2025-03-06 at 11:15 -0500, Robert Haas wrote:
> Patch attached. This patch does NOT change the default; stats are still
> opt-out. But it makes it easier for users to start specifying what they
> want or not explicitly, or to rely on the defaults if they prefer.
>
> Note that the patch would mean we go from 2 options in v17:
>   --{schema|data}-only
>
> to 9 options in v18:
>   --{with|no}-{schema|data|stats} and
>   --{schema|data|stats}-only
>

Ugh... this feels like a bit of the combinatorial explosion,
especially if we ever need to add another option. I wonder if it would
be possible to do something simple like just providing
"--include={schema|data|stats}" where you specify only what you want,
and leave out what you don't. At the risk of not providing as many
typing shortcuts, if the logic is simpler and more extensible for
future options...


Robert Treat
https://xzilla.net





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
@ 2025-03-07 18:41     ` Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-07 18:41 UTC (permalink / raw)
  To: Robert Treat <[email protected]>; +Cc: Robert Haas <[email protected]>; Andres Freund <[email protected]>; Corey Huinker <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, 2025-03-07 at 12:41 -0500, Robert Treat wrote:
> Ugh... this feels like a bit of the combinatorial explosion,
> especially if we ever need to add another option.

Not quite that bad, because ideally the yes/no/only  would not be
expanding as well. But I agree that it feels like a lot of options.

> I wonder if it would
> be possible to do something simple like just providing
> "--include={schema|data|stats}" where you specify only what you want,
> and leave out what you don't.

Can you explain the idea in a bit more detail? Does --
include=statistics mean include statistics also or statistics only? Can
you explicitly request that data be included but rely on the default
for statistics? What options would it override or conflict with?

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-07 20:46       ` Robert Treat <[email protected]>
  2025-03-07 21:43         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Robert Treat @ 2025-03-07 20:46 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Haas <[email protected]>; Andres Freund <[email protected]>; Corey Huinker <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Mar 7, 2025 at 1:41 PM Jeff Davis <[email protected]> wrote:
>
> On Fri, 2025-03-07 at 12:41 -0500, Robert Treat wrote:
> > Ugh... this feels like a bit of the combinatorial explosion,
> > especially if we ever need to add another option.
>
> Not quite that bad, because ideally the yes/no/only  would not be
> expanding as well. But I agree that it feels like a lot of options.
>
> > I wonder if it would
> > be possible to do something simple like just providing
> > "--include={schema|data|stats}" where you specify only what you want,
> > and leave out what you don't.
>
> Can you explain the idea in a bit more detail? Does --
> include=statistics mean include statistics also or statistics only? Can
> you explicitly request that data be included but rely on the default
> for statistics? What options would it override or conflict with?
>

There might be some variability depending on the default behavior, but
if we assume that default means "output everything" (which is the v17
behavior), then use of --include would mean to only include items that
are listed, so:

if you want everything --include=schema,data,statistics (presumably
redundant with the default behavior)
if you want schema only --include=schema
if you want "everything except schema" --include=data,statistics

So it's pretty easy to extrapolate data only or statistics only, and
pretty easy to work up any combo of 2 of the 3.

And if someday, for example, there is ever agreement on including role
information with normal pg_dump, you add "roles" as an option to be
parsed via --include without having to create any new flags.


Robert Treat
https://xzilla.net





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
@ 2025-03-07 21:43         ` Jeff Davis <[email protected]>
  2025-03-08 03:43           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-07 21:43 UTC (permalink / raw)
  To: Robert Treat <[email protected]>; +Cc: Robert Haas <[email protected]>; Andres Freund <[email protected]>; Corey Huinker <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, 2025-03-07 at 15:46 -0500, Robert Treat wrote:
> There might be some variability depending on the default behavior,
> but
> if we assume that default means "output everything"

The reason I posted this patch is that, depending on performance
characteristics in v18 and a decision to be made during beta, the
default may not output statistics.

So we need whatever set of options we choose to have the freedom to
change statistics to be either opt-in or opt-out, without needing to
reconsider the overall set of options.

I tried to generalize that requirement to all of
{schema|data|statistics} for consistency, but that resulted in 9
options.

We don't need the options to be perfectly consistent at the expense of
usability, though, so if 9 options is too many we can just have three
new options for stats, for a total of 5 options:

   --data-only
   --schema-only
   --statistics-only
   --statistics (stats also, regardless of default)
   --no-statistics (no stats, regardless of default)

which would allow combinations like "--schema-only --statistics" to
mean "schema and statistics but not data". There would be a bit of
weirdness because --statistics can combine with --data-only and --
schema-only, but nothing can combine with --statistics-only.

> if you want everything --include=schema,data,statistics (presumably
> redundant with the default behavior)
> if you want schema only --include=schema
> if you want "everything except schema" --include=data,statistics

That could work. Comparing to the options above yields:

   --include=statistics <=> --statistics-only
   --include=schema,data,statistics <=> --statistics
   --include=schema,statistics <=> --schema-only --statistics
   --include=data,statistics <=> --data-only --statistics
   --include=schema,data <=> --no-statistics

Not sure which approach is better.

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 21:43         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-08 03:43           ` Corey Huinker <[email protected]>
  2025-03-08 05:51             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-08 03:43 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> I tried to generalize that requirement to all of
> {schema|data|statistics} for consistency, but that resulted in 9
> options.
>

9 options that resolve to 3 boolean variables. It's not that hard.

And if we add a fourth option set, then we have 12 options. So it's O(3N),
not O(N^2).

People have scripts now that rely on the existing -only flags, and nearly
every other potentially optional thing has a -no flag. Let's leverage that.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 21:43         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-08 03:43           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-08 05:51             ` Hari Krishna Sunder <[email protected]>
  2025-03-08 07:51               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-03-08 05:51 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

To improve the performance of pg_dump can we add a new sql function that
can operate more efficiently than the pg_stats view? It could also take in
an optional list of oids to filter on.
This will help speed up the dump and restore within pg18 and future
upgrades to higher pg versions.

Thanks
Hari Krishna Sunder

On Fri, Mar 7, 2025 at 7:43 PM Corey Huinker <[email protected]>
wrote:

> I tried to generalize that requirement to all of
>> {schema|data|statistics} for consistency, but that resulted in 9
>> options.
>>
>
> 9 options that resolve to 3 boolean variables. It's not that hard.
>
> And if we add a fourth option set, then we have 12 options. So it's O(3N),
> not O(N^2).
>
> People have scripts now that rely on the existing -only flags, and nearly
> every other potentially optional thing has a -no flag. Let's leverage that.
>


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 21:43         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-08 03:43           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 05:51             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
@ 2025-03-08 07:51               ` Corey Huinker <[email protected]>
  2025-03-08 07:56                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-08 07:51 UTC (permalink / raw)
  To: Hari Krishna Sunder <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Sat, Mar 8, 2025 at 12:52 AM Hari Krishna Sunder <[email protected]>
wrote:

> To improve the performance of pg_dump can we add a new sql function that
> can operate more efficiently than the pg_stats view? It could also take in
> an optional list of oids to filter on.
> This will help speed up the dump and restore within pg18 and future
> upgrades to higher pg versions.
>
>
We can't install functions on the source database - it might be a read
replica.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 21:43         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-08 03:43           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 05:51             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-03-08 07:51               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-08 07:56                 ` Corey Huinker <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Corey Huinker @ 2025-03-08 07:56 UTC (permalink / raw)
  To: Hari Krishna Sunder <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Updated and rebase patches.

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

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

Spoke with Nathan about how best to batch the pg_stats fetches. I'll be
working on that now. Given that, the patch that optimized out
getAttributeStats() calls on indexes without expressions has been
withdrawn. It's a clear incremental gain, and we're looking for a couple
orders of magnitude gain.


Attachments:

  [text/x-patch] v7-0001-Split-relation-into-schemaname-and-relname.patch (65.0K, ../../CADkLM=fLUHPzvVnM4eC8ZdVH0wewXfZmVoVmn4=8pb12+s1v7Q@mail.gmail.com/3-v7-0001-Split-relation-into-schemaname-and-relname.patch)
  download | inline diff:
From 9cd4b4e0e280d0fd8cb120ac105d6e65a491cd7e Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Tue, 4 Mar 2025 22:16:52 -0500
Subject: [PATCH v7 1/2] Split relation into schemaname and relname.

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

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

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

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

base-commit: 21f653cc0024100f8ecc279162631f2b1ba8c46c
-- 
2.48.1



  [text/x-patch] v7-0002-Downgrade-as-man-pg_restore_-_stats-errors-to-war.patch (29.8K, ../../CADkLM=fLUHPzvVnM4eC8ZdVH0wewXfZmVoVmn4=8pb12+s1v7Q@mail.gmail.com/4-v7-0002-Downgrade-as-man-pg_restore_-_stats-errors-to-war.patch)
  download | inline diff:
From 4d8d76b78b87f53d0adbd6781a2a66beac5bc264 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 8 Mar 2025 00:52:41 -0500
Subject: [PATCH v7 2/2] Downgrade as man pg_restore_*_stats errors to
 warnings.

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

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



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
@ 2025-03-08 03:40         ` Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-08 03:40 UTC (permalink / raw)
  To: Robert Treat <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
>
> if you want everything --include=schema,data,statistics (presumably
> redundant with the default behavior)
> if you want schema only --include=schema
> if you want "everything except schema" --include=data,statistics
>

Until we add a fourth option, and then it becomes completely ambiguous as
to whether you wanted data+statstics, or you not-wanted schema.



And if someday, for example, there is ever agreement on including role
> information with normal pg_dump, you add "roles" as an option to be
> parsed via --include without having to create any new flags.
>

This is pushing a burden onto our customers for a parsing convenience.

-1.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-08 15:56           ` Robert Treat <[email protected]>
  2025-03-08 19:09             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Robert Treat @ 2025-03-08 15:56 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Mar 7, 2025 at 10:40 PM Corey Huinker <[email protected]>
wrote:

>
>> if you want everything --include=schema,data,statistics (presumably
>> redundant with the default behavior)
>> if you want schema only --include=schema
>> if you want "everything except schema" --include=data,statistics
>>
>
> Until we add a fourth option, and then it becomes completely ambiguous as
> to whether you wanted data+statstics, or you not-wanted schema.
>
>
except it is perfectly clear that you *asked for* data and statistics, so
you get what you asked for. however the user conjures in their heads what
they are looking for, the logic is simple, you get what you asked for.


>
>
> And if someday, for example, there is ever agreement on including role
>> information with normal pg_dump, you add "roles" as an option to be
>> parsed via --include without having to create any new flags.
>>
>
> This is pushing a burden onto our customers for a parsing convenience.
>
>
In the UX world, the general pattern is people start to get overwhelmed
once you get over a 1/2 dozen options (I think that's based on Miller's
law, but might be mis-remembering); we are already at 9 for this use case.
So really it is quite the opposite, we'd be reducing the burden on
customers by simplifying the interface rather than just throwing out every
possible combination and saying "you figure it out".


Robert Treat
https://xzilla.net


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
@ 2025-03-08 19:09             ` Corey Huinker <[email protected]>
  2025-03-25 05:32               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-08 19:09 UTC (permalink / raw)
  To: Robert Treat <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> Until we add a fourth option, and then it becomes completely ambiguous as
>> to whether you wanted data+statstics, or you not-wanted schema.
>>
>>
> except it is perfectly clear that you *asked for* data and statistics, so
> you get what you asked for. however the user conjures in their heads what
> they are looking for, the logic is simple, you get what you asked for.
>

They *asked for* that because they didn't have the mechanism to say "hold
the mayo" or "everything except pickles". That's reducing their choice, and
then blaming them for their choice.

In the UX world, the general pattern is people start to get overwhelmed
> once you get over a 1/2 dozen options (I think that's based on Miller's
> law, but might be mis-remembering); we are already at 9 for this use case.
> So really it is quite the opposite, we'd be reducing the burden on
> customers by simplifying the interface rather than just throwing out every
> possible combination and saying "you figure it out".
>

Except that those options are easily grouped into families. I see that
there's a --no-comments flag, so why wouldn't there be a --no-statistics
flag? Lots of $thing have a --no-$thing. That's the established UX pattern
_working_. The user learned that pattern and we shouldn't punish them by
changing it for our own parsing convenience.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 19:09             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-25 05:32               ` Jeff Davis <[email protected]>
  2025-03-25 17:51                 ` Re: Statistics Import and Export Robert Treat <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-25 05:32 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; +Cc: Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Sat, 2025-03-08 at 14:09 -0500, Corey Huinker wrote:
> > 
> > except it is perfectly clear that you *asked for* data and
> > statistics, so you get what you asked for. however the user
> > conjures in their heads what they are looking for, the logic is
> > simple, you get what you asked for. 
> > 
> 
> 
> They *asked for* that because they didn't have the mechanism to say
> "hold the mayo" or "everything except pickles". That's reducing their
> choice, and then blaming them for their choice.

Can we reach a decision here and move forward?

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 19:09             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 05:32               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-25 17:51                 ` Robert Treat <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Robert Treat @ 2025-03-25 17:51 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, Mar 25, 2025 at 1:32 AM Jeff Davis <[email protected]> wrote:

> On Sat, 2025-03-08 at 14:09 -0500, Corey Huinker wrote:
> > >
> > > except it is perfectly clear that you *asked for* data and
> > > statistics, so you get what you asked for. however the user
> > > conjures in their heads what they are looking for, the logic is
> > > simple, you get what you asked for.
> > >
> >
> >
> > They *asked for* that because they didn't have the mechanism to say
> > "hold the mayo" or "everything except pickles". That's reducing their
> > choice, and then blaming them for their choice.
>
> Can we reach a decision here and move forward?
>
>
AFAIK the issue has been settled, or at the least we've agreed to move on.


Robert Treat
https://xzilla.net


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
@ 2025-03-09 17:00             ` Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-09 17:00 UTC (permalink / raw)
  To: Robert Treat <[email protected]>; Corey Huinker <[email protected]>; +Cc: Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Sat, 2025-03-08 at 10:56 -0500, Robert Treat wrote:
> In the UX world, the general pattern is people start to get
> overwhelmed once you get over a 1/2 dozen options (I think that's
> based on Miller's law, but might be mis-remembering); we are already
> at 9 for this use case. So really it is quite the opposite, we'd be
> reducing the burden on customers by simplifying the interface rather
> than just throwing out every possible combination and saying "you
> figure it out". 

To be clear about your proposal:

* --include conflicts with --schema-only and --data-only
* --include overrides any default

is that right?

Thoughts on how we should document when/how to use --section vs --
include? Granted, that might be a point of confusion regardless of the
options we offer.

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-14 20:03               ` Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 20:33                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Corey Huinker @ 2025-03-14 20:03 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

New patches and a rebase.

0001 - no changes, but the longer I go the more I'm certain this is
something we want to do.
0002- same as 0001

0003 -

Storing the restore function calls in the archive entry hogged a lot of
memory and made people nervous. This introduces a new function pointer that
generates those restore SQL calls right before they're written to disk,
thus reducing the memory load from "stats for every object to be dumped" to
just one object. Thanks to Nathan for diagnosing some weird quirks with
various formats.

0004 -

This replaces the query in the prepared statement with one that batches
them 100 relations at a time, and then maintains that result set until it
is consumed. It seems to have obvious speedups.


database pg14, 100k tables x 2 columns each:

0004: 34.5s with statistics, 25.04s without
0003: 42.23s with statistics, 24.29s without
0002: 42.25s with statistics, 23.17s without


Gory details:

PGSERVICE=benchmark14 time /usr/local/pgsql/bin/pg_dump --file=tip.run1.dump
5.45user 2.38system 0:34.50elapsed 22%CPU (0avgtext+0avgdata
912680maxresident)k
0inputs+2105736outputs (0major+245090minor)pagefaults 0swaps

PGSERVICE=benchmark14 time /usr/local/pgsql/bin/pg_dump --no-statistics
--file=tip.nostats.run1.dump
4.36user 2.05system 0:25.04elapsed 25%CPU (0avgtext+0avgdata
702488maxresident)k
0inputs+1643048outputs (0major+192512minor)pagefaults 0swaps

PGSERVICE=benchmark14 time /usr/local/pgsql/bin/pg_dump
--file=nobatch.run1.dump
5.60user 3.95system 0:42.23elapsed 22%CPU (0avgtext+0avgdata
902424maxresident)k
0inputs+2105672outputs (0major+242536minor)pagefaults 0swaps

PGSERVICE=benchmark14 time /usr/local/pgsql/bin/pg_dump --no-statistics
--file=nobatch-nostats.run1.dump
4.38user 2.13system 0:24.29elapsed 26%CPU (0avgtext+0avgdata
702292maxresident)k
48inputs+1642952outputs (0major+192515minor)pagefaults 0swaps

PGSERVICE=benchmark14 time /usr/local/pgsql/bin/pg_dump
--file=nostmtfn.run1.dump
6.01user 4.47system 0:42.25elapsed 24%CPU (0avgtext+0avgdata
1089784maxresident)k
0inputs+2106840outputs (0major+289407minor)pagefaults 0swaps

PGSERVICE=benchmark14 time /usr/local/pgsql/bin/pg_dump --no-statistics
--file=nostmtfn-nostats.run1.dump
4.35user 2.13system 0:23.17elapsed 27%CPU (0avgtext+0avgdata
690000maxresident)k
0inputs+1642952outputs (0major+189383minor)pagefaults 0swaps


Attachments:

  [text/x-patch] v8-0001-Split-relation-into-schemaname-and-relname.patch (65.0K, ../../CADkLM=c+r05srPy9w+-+nbmLEo15dKXYQ03Q_xyK+riJerigLQ@mail.gmail.com/3-v8-0001-Split-relation-into-schemaname-and-relname.patch)
  download | inline diff:
From a2c68b8390cf137323f449a4bc826ad66bada0bb Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Tue, 4 Mar 2025 22:16:52 -0500
Subject: [PATCH v8 1/4] Split relation into schemaname and relname.

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

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

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

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

base-commit: 6d376c3b0d1e79c318d2a1c04097025784e28377
-- 
2.48.1



  [text/x-patch] v8-0002-Downgrade-as-man-pg_restore_-_stats-errors-to-war.patch (29.8K, ../../CADkLM=c+r05srPy9w+-+nbmLEo15dKXYQ03Q_xyK+riJerigLQ@mail.gmail.com/4-v8-0002-Downgrade-as-man-pg_restore_-_stats-errors-to-war.patch)
  download | inline diff:
From 98f1eea90be0804ecdd43a89306aa83a6b9784c5 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 8 Mar 2025 00:52:41 -0500
Subject: [PATCH v8 2/4] Downgrade as man pg_restore_*_stats errors to
 warnings.

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

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



  [text/x-patch] v8-0003-Introduce-CreateStmtPtr.patch (17.2K, ../../CADkLM=c+r05srPy9w+-+nbmLEo15dKXYQ03Q_xyK+riJerigLQ@mail.gmail.com/5-v8-0003-Introduce-CreateStmtPtr.patch)
  download | inline diff:
From e2b00e46afdfa45f263b4666831160bf4d21dd09 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 01:06:19 -0400
Subject: [PATCH v8 3/4] Introduce CreateStmtPtr.

CreateStmtPtr is a function pointer that can replace the createStmt/defn
parameter. This is useful in situations where the amount of text
generated for a definition is so large that it is undesirable to hold
many such objects in memory at the same time.

Using functions of this type, the text created is then immediately
written out to the appropriate file for the given dump format.
---
 src/bin/pg_dump/pg_backup.h          |   2 +
 src/bin/pg_dump/pg_backup_archiver.c |  22 ++-
 src/bin/pg_dump/pg_backup_archiver.h |   7 +
 src/bin/pg_dump/pg_dump.c            | 230 +++++++++++++++------------
 4 files changed, 156 insertions(+), 105 deletions(-)

diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index e783cc68d89..bb175874a5a 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -287,6 +287,8 @@ typedef int (*DataDumperPtr) (Archive *AH, const void *userArg);
 
 typedef void (*SetupWorkerPtrType) (Archive *AH);
 
+typedef char *(*CreateStmtPtr) (Archive *AH, const void *userArg);
+
 /*
  * Main archiver interface.
  */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 7480e122b61..3fcfecf6719 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1263,6 +1263,9 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
 	newToc->dataDumper = opts->dumpFn;
 	newToc->dataDumperArg = opts->dumpArg;
 	newToc->hadDumper = opts->dumpFn ? true : false;
+	newToc->createDumper = opts->createFn;
+	newToc->createDumperArg = opts->createArg;
+	newToc->hadCreateDumper = opts->createFn ? true : false;
 
 	newToc->formatData = NULL;
 	newToc->dataLength = 0;
@@ -2619,7 +2622,17 @@ WriteToc(ArchiveHandle *AH)
 		WriteStr(AH, te->tag);
 		WriteStr(AH, te->desc);
 		WriteInt(AH, te->section);
-		WriteStr(AH, te->defn);
+
+		if (te->hadCreateDumper)
+		{
+			char	   *defn = te->createDumper((Archive *) AH, te->createDumperArg);
+
+			WriteStr(AH, defn);
+			pg_free(defn);
+		}
+		else
+			WriteStr(AH, te->defn);
+
 		WriteStr(AH, te->dropStmt);
 		WriteStr(AH, te->copyStmt);
 		WriteStr(AH, te->namespace);
@@ -3849,6 +3862,13 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
 	{
 		IssueACLPerBlob(AH, te);
 	}
+	else if (te->hadCreateDumper)
+	{
+		char	   *ptr = te->createDumper((Archive *) AH, te->createDumperArg);
+
+		ahwrite(ptr, 1, strlen(ptr), AH);
+		pg_free(ptr);
+	}
 	else if (te->defn && strlen(te->defn) > 0)
 	{
 		ahprintf(AH, "%s\n\n", te->defn);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a2064f471ed..e68db633995 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -368,6 +368,11 @@ struct _tocEntry
 	const void *dataDumperArg;	/* Arg for above routine */
 	void	   *formatData;		/* TOC Entry data specific to file format */
 
+	CreateStmtPtr createDumper; /* Routine for create statement creation */
+	const void *createDumperArg;	/* arg for the above routine */
+	bool		hadCreateDumper;	/* Archiver was passed a create statement
+									 * routine */
+
 	/* working state while dumping/restoring */
 	pgoff_t		dataLength;		/* item's data size; 0 if none or unknown */
 	int			reqs;			/* do we need schema and/or data of object
@@ -407,6 +412,8 @@ typedef struct _archiveOpts
 	int			nDeps;
 	DataDumperPtr dumpFn;
 	const void *dumpArg;
+	CreateStmtPtr createFn;
+	const void *createArg;
 } ArchiveOpts;
 #define ARCHIVE_OPTS(...) &(ArchiveOpts){__VA_ARGS__}
 /* Called to add a TOC entry */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bd857bb076c..38ba6a90106 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10477,51 +10477,44 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
 }
 
 /*
- * dumpRelationStats --
+ * printDumpRelationStats --
  *
- * Dump command to import stats into the relation on the new database.
+ * Generate the SQL statements needed to restore a relation's statistics.
  */
-static void
-dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+static char *
+printRelationStats(Archive *fout, const void *userArg)
 {
+	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
 	const DumpableObject *dobj = &rsinfo->dobj;
+
+	PQExpBufferData query;
+	PQExpBufferData out;
+
 	PGresult   *res;
-	PQExpBuffer query;
-	PQExpBuffer out;
-	DumpId	   *deps = NULL;
-	int			ndeps = 0;
-	int			i_attname;
-	int			i_inherited;
-	int			i_null_frac;
-	int			i_avg_width;
-	int			i_n_distinct;
-	int			i_most_common_vals;
-	int			i_most_common_freqs;
-	int			i_histogram_bounds;
-	int			i_correlation;
-	int			i_most_common_elems;
-	int			i_most_common_elem_freqs;
-	int			i_elem_count_histogram;
-	int			i_range_length_histogram;
-	int			i_range_empty_frac;
-	int			i_range_bounds_histogram;
 
-	/* nothing to do if we are not dumping statistics */
-	if (!fout->dopt->dumpStatistics)
-		return;
+	static bool first_query = true;
+	static int	i_attname;
+	static int	i_inherited;
+	static int	i_null_frac;
+	static int	i_avg_width;
+	static int	i_n_distinct;
+	static int	i_most_common_vals;
+	static int	i_most_common_freqs;
+	static int	i_histogram_bounds;
+	static int	i_correlation;
+	static int	i_most_common_elems;
+	static int	i_most_common_elem_freqs;
+	static int	i_elem_count_histogram;
+	static int	i_range_length_histogram;
+	static int	i_range_empty_frac;
+	static int	i_range_bounds_histogram;
 
-	/* dependent on the relation definition, if doing schema */
-	if (fout->dopt->dumpSchema)
+	initPQExpBuffer(&query);
+
+	if (first_query)
 	{
-		deps = dobj->dependencies;
-		ndeps = dobj->nDeps;
-	}
-
-	query = createPQExpBuffer();
-	if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
-	{
-		appendPQExpBufferStr(query,
-							 "PREPARE getAttributeStats(pg_catalog.name, pg_catalog.name) AS\n"
+		appendPQExpBufferStr(&query,
+							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
 							 "SELECT s.attname, s.inherited, "
 							 "s.null_frac, s.avg_width, s.n_distinct, "
 							 "s.most_common_vals, s.most_common_freqs, "
@@ -10530,82 +10523,85 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 							 "s.elem_count_histogram, ");
 
 		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "s.range_length_histogram, "
 								 "s.range_empty_frac, "
 								 "s.range_bounds_histogram ");
 		else
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "NULL AS range_length_histogram,"
 								 "NULL AS range_empty_frac,"
 								 "NULL AS range_bounds_histogram ");
 
-		appendPQExpBufferStr(query,
+		appendPQExpBufferStr(&query,
 							 "FROM pg_catalog.pg_stats s "
 							 "WHERE s.schemaname = $1 "
 							 "AND s.tablename = $2 "
 							 "ORDER BY s.attname, s.inherited");
 
-		ExecuteSqlStatement(fout, query->data);
+		ExecuteSqlStatement(fout, query.data);
 
-		fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
-		resetPQExpBuffer(query);
+		resetPQExpBuffer(&query);
 	}
 
-	out = createPQExpBuffer();
+	initPQExpBuffer(&out);
 
 	/* restore relation stats */
-	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
-	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
+	appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 					  fout->remoteVersion);
-	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",
+	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",
 					  rsinfo->relallvisible);
 
 	/* fetch attribute stats */
-	appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(query, ", ");
-	appendStringLiteralAH(query, dobj->name, fout);
-	appendPQExpBufferStr(query, ");");
+	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
+	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
+	appendPQExpBufferStr(&query, ", ");
+	appendStringLiteralAH(&query, dobj->name, fout);
+	appendPQExpBufferStr(&query, ")");
 
-	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
 
-	i_attname = PQfnumber(res, "attname");
-	i_inherited = PQfnumber(res, "inherited");
-	i_null_frac = PQfnumber(res, "null_frac");
-	i_avg_width = PQfnumber(res, "avg_width");
-	i_n_distinct = PQfnumber(res, "n_distinct");
-	i_most_common_vals = PQfnumber(res, "most_common_vals");
-	i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-	i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-	i_correlation = PQfnumber(res, "correlation");
-	i_most_common_elems = PQfnumber(res, "most_common_elems");
-	i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-	i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-	i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-	i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-	i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+	if (first_query)
+	{
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		first_query = false;
+	}
 
 	/* restore attribute stats */
 	for (int rownum = 0; rownum < PQntuples(res); rownum++)
 	{
 		const char *attname;
 
-		appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 						  fout->remoteVersion);
-		appendPQExpBufferStr(out, "\t'schemaname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
-		appendPQExpBufferStr(out, ",\n\t'relname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.name, fout);
+		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");
@@ -10618,8 +10614,8 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		 */
 		if (rsinfo->nindAttNames == 0)
 		{
-			appendPQExpBuffer(out, ",\n\t'attname', ");
-			appendStringLiteralAH(out, attname, fout);
+			appendPQExpBuffer(&out, ",\n\t'attname', ");
+			appendStringLiteralAH(&out, attname, fout);
 		}
 		else
 		{
@@ -10629,7 +10625,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 			{
 				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
 				{
-					appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
 									  i + 1);
 					found = true;
 					break;
@@ -10641,67 +10637,93 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		}
 
 		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(out, fout, "inherited", "boolean",
+			appendNamedArgument(&out, fout, "inherited", "boolean",
 								PQgetvalue(res, rownum, i_inherited));
 		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(out, fout, "null_frac", "real",
+			appendNamedArgument(&out, fout, "null_frac", "real",
 								PQgetvalue(res, rownum, i_null_frac));
 		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(out, fout, "avg_width", "integer",
+			appendNamedArgument(&out, fout, "avg_width", "integer",
 								PQgetvalue(res, rownum, i_avg_width));
 		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(out, fout, "n_distinct", "real",
+			appendNamedArgument(&out, fout, "n_distinct", "real",
 								PQgetvalue(res, rownum, i_n_distinct));
 		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(out, fout, "most_common_vals", "text",
+			appendNamedArgument(&out, fout, "most_common_vals", "text",
 								PQgetvalue(res, rownum, i_most_common_vals));
 		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_freqs));
 		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(out, fout, "histogram_bounds", "text",
+			appendNamedArgument(&out, fout, "histogram_bounds", "text",
 								PQgetvalue(res, rownum, i_histogram_bounds));
 		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(out, fout, "correlation", "real",
+			appendNamedArgument(&out, fout, "correlation", "real",
 								PQgetvalue(res, rownum, i_correlation));
 		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(out, fout, "most_common_elems", "text",
+			appendNamedArgument(&out, fout, "most_common_elems", "text",
 								PQgetvalue(res, rownum, i_most_common_elems));
 		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_elem_freqs));
 		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
 								PQgetvalue(res, rownum, i_elem_count_histogram));
 		if (fout->remoteVersion >= 170000)
 		{
 			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(out, fout, "range_length_histogram", "text",
+				appendNamedArgument(&out, fout, "range_length_histogram", "text",
 									PQgetvalue(res, rownum, i_range_length_histogram));
 			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(out, fout, "range_empty_frac", "real",
+				appendNamedArgument(&out, fout, "range_empty_frac", "real",
 									PQgetvalue(res, rownum, i_range_empty_frac));
 			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
 									PQgetvalue(res, rownum, i_range_bounds_histogram));
 		}
-		appendPQExpBufferStr(out, "\n);\n");
+		appendPQExpBufferStr(&out, "\n);\n");
 	}
 
 	PQclear(res);
 
+	termPQExpBuffer(&query);
+	return out.data;
+}
+
+/*
+ * dumpRelationStats --
+ *
+ * Dump command to import stats into the relation on the new database.
+ */
+static void
+dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+{
+	const DumpableObject *dobj = &rsinfo->dobj;
+
+	DumpId	   *deps = NULL;
+	int			ndeps = 0;
+
+	/* nothing to do if we are not dumping statistics */
+	if (!fout->dopt->dumpStatistics)
+		return;
+
+	/* dependent on the relation definition, if doing schema */
+	if (fout->dopt->dumpSchema)
+	{
+		deps = dobj->dependencies;
+		ndeps = dobj->nDeps;
+	}
+
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 ARCHIVE_OPTS(.tag = dobj->name,
 							  .namespace = dobj->namespace->dobj.name,
 							  .description = "STATISTICS DATA",
 							  .section = rsinfo->postponed_def ?
 							  SECTION_POST_DATA : statisticsDumpSection(rsinfo),
-							  .createStmt = out->data,
+							  .createFn = printRelationStats,
+							  .createArg = rsinfo,
 							  .deps = deps,
 							  .nDeps = ndeps));
-
-	destroyPQExpBuffer(out);
-	destroyPQExpBuffer(query);
 }
 
 /*
-- 
2.48.1



  [text/x-patch] v8-0004-Batching-getAttributeStats.patch (21.6K, ../../CADkLM=c+r05srPy9w+-+nbmLEo15dKXYQ03Q_xyK+riJerigLQ@mail.gmail.com/6-v8-0004-Batching-getAttributeStats.patch)
  download | inline diff:
From 737a29c9b8f146eb037630ab183eb8601a76409a Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 03:54:26 -0400
Subject: [PATCH v8 4/4] Batching getAttributeStats().

The prepared statement getAttributeStats() is fairly heavyweight and
could greatly increase pg_dump/pg_upgrade runtime. To alleviate this,
create a result set buffer of all of the attribute stats fetched for a
batch of 100 relations that could potentially have stats.

The query ensures that the order of results exactly matches the needs of
the code walking the TOC to print the stats calls.
---
 src/bin/pg_dump/pg_dump.c | 556 ++++++++++++++++++++++++++------------
 1 file changed, 385 insertions(+), 171 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 38ba6a90106..0c26dc7a1b4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -143,6 +143,25 @@ typedef enum OidOptions
 	zeroAsNone = 4,
 } OidOptions;
 
+typedef enum StatsBufferState
+{
+	STATSBUF_UNINITIALIZED = 0,
+	STATSBUF_ACTIVE,
+	STATSBUF_EXHAUSTED
+}			StatsBufferState;
+
+typedef struct
+{
+	PGresult   *res;			/* results from most recent
+								 * getAttributeStats() */
+	int			idx;			/* first un-consumed row of results */
+	TocEntry   *te;				/* next TOC entry to search for statsitics
+								 * data */
+
+	StatsBufferState state;		/* current state of the buffer */
+}			AttributeStatsBuffer;
+
+
 /* global decls */
 static bool dosync = true;		/* Issue fsync() to make dump durable on disk. */
 
@@ -209,6 +228,18 @@ static int	nbinaryUpgradeClassOids = 0;
 static SequenceItem *sequences = NULL;
 static int	nsequences = 0;
 
+static AttributeStatsBuffer attrstats =
+{
+	NULL, 0, NULL, STATSBUF_UNINITIALIZED
+};
+
+/*
+ * The maximum number of relations that should be fetched in any one
+ * getAttributeStats() call.
+ */
+
+#define MAX_ATTR_STATS_RELS 100
+
 /*
  * The default number of rows per INSERT when
  * --inserts is specified without --rows-per-insert
@@ -222,6 +253,10 @@ static int	nsequences = 0;
  */
 #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
 
+
+
+/* TODO: fmtId(const char *rawid) */
+
 /*
  * Macro for producing quoted, schema-qualified name of a dumpable object.
  */
@@ -399,6 +434,9 @@ static void setupDumpWorker(Archive *AH);
 static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
 static bool forcePartitionRootLoad(const TableInfo *tbinfo);
 static void read_dump_filters(const char *filename, DumpOptions *dopt);
+static void appendNamedArgument(PQExpBuffer out, Archive *fout,
+								const char *argname, const char *argtype,
+								const char *argval);
 
 
 int
@@ -10477,7 +10515,286 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
 }
 
 /*
- * printDumpRelationStats --
+ * Fetch next batch of rows from getAttributeStats()
+ */
+static void
+fetchNextAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData schemas;
+	PQExpBufferData relations;
+	int			numoids = 0;
+
+	Assert(AH != NULL);
+
+	/* free last result set, if any */
+	if (attrstats.state == STATSBUF_ACTIVE)
+		PQclear(attrstats.res);
+
+	/* If we have looped around to the start of the TOC, restart */
+	if (attrstats.te == AH->toc)
+		attrstats.te = AH->toc->next;
+
+	initPQExpBuffer(&schemas);
+	initPQExpBuffer(&relations);
+
+	/*
+	 * Walk ahead looking for relstats entries that are active in this
+	 * section, adding the names to the schemas and relations lists.
+	 */
+	while ((attrstats.te != AH->toc) && (numoids < MAX_ATTR_STATS_RELS))
+	{
+		if (attrstats.te->reqs != 0 &&
+			strcmp(attrstats.te->desc, "STATISTICS DATA") == 0)
+		{
+			RelStatsInfo *rsinfo = (RelStatsInfo *) attrstats.te->createDumperArg;
+
+			Assert(rsinfo != NULL);
+
+			if (numoids > 0)
+			{
+				appendPQExpBufferStr(&schemas, ",");
+				appendPQExpBufferStr(&relations, ",");
+			}
+			appendPQExpBufferStr(&schemas, fmtId(rsinfo->dobj.namespace->dobj.name));
+			appendPQExpBufferStr(&relations, fmtId(rsinfo->dobj.name));
+			numoids++;
+		}
+
+		attrstats.te = attrstats.te->next;
+	}
+
+	if (numoids > 0)
+	{
+		PQExpBufferData query;
+
+		initPQExpBuffer(&query);
+		appendPQExpBuffer(&query,
+						  "EXECUTE getAttributeStats('{%s}'::pg_catalog.text[],'{%s}'::pg_catalog.text[])",
+						  schemas.data, relations.data);
+		attrstats.res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+		attrstats.idx = 0;
+	}
+	else
+	{
+		attrstats.state = STATSBUF_EXHAUSTED;
+		attrstats.res = NULL;
+		attrstats.idx = -1;
+	}
+
+	termPQExpBuffer(&schemas);
+	termPQExpBuffer(&relations);
+}
+
+/*
+ * Prepare the getAttributeStats() statement
+ *
+ * This is done automatically if the user specified dumpStatistics.
+ */
+static void
+initAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData query;
+
+	Assert(AH != NULL);
+	initPQExpBuffer(&query);
+
+	appendPQExpBufferStr(&query,
+						 "PREPARE getAttributeStats(pg_catalog.text[], pg_catalog.text[]) AS\n"
+						 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
+						 "s.null_frac, s.avg_width, s.n_distinct, s.most_common_vals, "
+						 "s.most_common_freqs, s.histogram_bounds, s.correlation, "
+						 "s.most_common_elems, s.most_common_elem_freqs, "
+						 "s.elem_count_histogram, ");
+
+	if (fout->remoteVersion >= 170000)
+		appendPQExpBufferStr(&query,
+							 "s.range_length_histogram, "
+							 "s.range_empty_frac, "
+							 "s.range_bounds_histogram ");
+	else
+		appendPQExpBufferStr(&query,
+							 "NULL AS range_length_histogram, "
+							 "NULL AS range_empty_frac, "
+							 " NULL AS range_bounds_histogram ");
+
+	/*
+	 * The results must be in the order of relations supplied in the
+	 * parameters to ensure that they are in sync with a walk of the TOC.
+	 *
+	 * The redundant (and incomplete) filter clause on s.tablename = ANY(...)
+	 * is a way to lead the query into using the index
+	 * pg_class_relname_nsp_index which in turn allows the planner to avoid an
+	 * expensive full scan of pg_stats.
+	 *
+	 * We may need to adjust this query for versions that are not so easily
+	 * led.
+	 */
+	appendPQExpBufferStr(&query,
+						 "FROM pg_catalog.pg_stats AS s "
+						 "JOIN unnest($1, $2) WITH ORDINALITY AS u(schemaname, tablename, ord) "
+						 "ON s.schemaname = u.schemaname "
+						 "AND s.tablename = u.tablename "
+						 "WHERE s.tablename = ANY($2) "
+						 "ORDER BY u.ord, s.attname, s.inherited");
+
+	ExecuteSqlStatement(fout, query.data);
+
+	termPQExpBuffer(&query);
+
+	attrstats.te = AH->toc->next;
+
+	fetchNextAttributeStats(fout);
+
+	attrstats.state = STATSBUF_ACTIVE;
+}
+
+
+/*
+ * append a single attribute stat to the buffer for this relation.
+ */
+static void
+appendAttributeStats(Archive *fout, PQExpBuffer out,
+					 const RelStatsInfo *rsinfo)
+{
+	PGresult   *res = attrstats.res;
+	int			tup_num = attrstats.idx;
+
+	const char *attname;
+
+	static bool indexes_set = false;
+	static int	i_attname,
+				i_inherited,
+				i_null_frac,
+				i_avg_width,
+				i_n_distinct,
+				i_most_common_vals,
+				i_most_common_freqs,
+				i_histogram_bounds,
+				i_correlation,
+				i_most_common_elems,
+				i_most_common_elem_freqs,
+				i_elem_count_histogram,
+				i_range_length_histogram,
+				i_range_empty_frac,
+				i_range_bounds_histogram;
+
+	if (!indexes_set)
+	{
+		/*
+		 * It's a prepared statement, so the indexes will be the same for all
+		 * result sets, so we only need to set them once.
+		 */
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		indexes_set = true;
+	}
+
+	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+					  fout->remoteVersion);
+	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, tup_num, i_attname))
+		pg_fatal("attname cannot be NULL");
+	attname = PQgetvalue(res, tup_num, i_attname);
+
+	/*
+	 * Indexes look up attname in indAttNames to derive attnum, all others use
+	 * attname directly.  We must specify attnum for indexes, since their
+	 * attnames are not necessarily stable across dump/reload.
+	 */
+	if (rsinfo->nindAttNames == 0)
+	{
+		appendPQExpBuffer(out, ",\n\t'attname', ");
+		appendStringLiteralAH(out, attname, fout);
+	}
+	else
+	{
+		bool		found = false;
+
+		for (int i = 0; i < rsinfo->nindAttNames; i++)
+			if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
+			{
+				appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+								  i + 1);
+				found = true;
+				break;
+			}
+
+		if (!found)
+			pg_fatal("could not find index attname \"%s\"", attname);
+	}
+
+	if (!PQgetisnull(res, tup_num, i_inherited))
+		appendNamedArgument(out, fout, "inherited", "boolean",
+							PQgetvalue(res, tup_num, i_inherited));
+	if (!PQgetisnull(res, tup_num, i_null_frac))
+		appendNamedArgument(out, fout, "null_frac", "real",
+							PQgetvalue(res, tup_num, i_null_frac));
+	if (!PQgetisnull(res, tup_num, i_avg_width))
+		appendNamedArgument(out, fout, "avg_width", "integer",
+							PQgetvalue(res, tup_num, i_avg_width));
+	if (!PQgetisnull(res, tup_num, i_n_distinct))
+		appendNamedArgument(out, fout, "n_distinct", "real",
+							PQgetvalue(res, tup_num, i_n_distinct));
+	if (!PQgetisnull(res, tup_num, i_most_common_vals))
+		appendNamedArgument(out, fout, "most_common_vals", "text",
+							PQgetvalue(res, tup_num, i_most_common_vals));
+	if (!PQgetisnull(res, tup_num, i_most_common_freqs))
+		appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_freqs));
+	if (!PQgetisnull(res, tup_num, i_histogram_bounds))
+		appendNamedArgument(out, fout, "histogram_bounds", "text",
+							PQgetvalue(res, tup_num, i_histogram_bounds));
+	if (!PQgetisnull(res, tup_num, i_correlation))
+		appendNamedArgument(out, fout, "correlation", "real",
+							PQgetvalue(res, tup_num, i_correlation));
+	if (!PQgetisnull(res, tup_num, i_most_common_elems))
+		appendNamedArgument(out, fout, "most_common_elems", "text",
+							PQgetvalue(res, tup_num, i_most_common_elems));
+	if (!PQgetisnull(res, tup_num, i_most_common_elem_freqs))
+		appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_elem_freqs));
+	if (!PQgetisnull(res, tup_num, i_elem_count_histogram))
+		appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+							PQgetvalue(res, tup_num, i_elem_count_histogram));
+	if (fout->remoteVersion >= 170000)
+	{
+		if (!PQgetisnull(res, tup_num, i_range_length_histogram))
+			appendNamedArgument(out, fout, "range_length_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_length_histogram));
+		if (!PQgetisnull(res, tup_num, i_range_empty_frac))
+			appendNamedArgument(out, fout, "range_empty_frac", "real",
+								PQgetvalue(res, tup_num, i_range_empty_frac));
+		if (!PQgetisnull(res, tup_num, i_range_bounds_histogram))
+			appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_bounds_histogram));
+	}
+	appendPQExpBufferStr(out, "\n);\n");
+}
+
+
+
+/*
+ * printRelationStats --
  *
  * Generate the SQL statements needed to restore a relation's statistics.
  */
@@ -10485,64 +10802,21 @@ static char *
 printRelationStats(Archive *fout, const void *userArg)
 {
 	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
-	const DumpableObject *dobj = &rsinfo->dobj;
+	const DumpableObject *dobj;
+	const char *relschema;
+	const char *relname;
+
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
 
-	PQExpBufferData query;
 	PQExpBufferData out;
 
-	PGresult   *res;
-
-	static bool first_query = true;
-	static int	i_attname;
-	static int	i_inherited;
-	static int	i_null_frac;
-	static int	i_avg_width;
-	static int	i_n_distinct;
-	static int	i_most_common_vals;
-	static int	i_most_common_freqs;
-	static int	i_histogram_bounds;
-	static int	i_correlation;
-	static int	i_most_common_elems;
-	static int	i_most_common_elem_freqs;
-	static int	i_elem_count_histogram;
-	static int	i_range_length_histogram;
-	static int	i_range_empty_frac;
-	static int	i_range_bounds_histogram;
-
-	initPQExpBuffer(&query);
-
-	if (first_query)
-	{
-		appendPQExpBufferStr(&query,
-							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
-							 "SELECT s.attname, s.inherited, "
-							 "s.null_frac, s.avg_width, s.n_distinct, "
-							 "s.most_common_vals, s.most_common_freqs, "
-							 "s.histogram_bounds, s.correlation, "
-							 "s.most_common_elems, s.most_common_elem_freqs, "
-							 "s.elem_count_histogram, ");
-
-		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(&query,
-								 "s.range_length_histogram, "
-								 "s.range_empty_frac, "
-								 "s.range_bounds_histogram ");
-		else
-			appendPQExpBufferStr(&query,
-								 "NULL AS range_length_histogram,"
-								 "NULL AS range_empty_frac,"
-								 "NULL AS range_bounds_histogram ");
-
-		appendPQExpBufferStr(&query,
-							 "FROM pg_catalog.pg_stats s "
-							 "WHERE s.schemaname = $1 "
-							 "AND s.tablename = $2 "
-							 "ORDER BY s.attname, s.inherited");
-
-		ExecuteSqlStatement(fout, query.data);
-
-		resetPQExpBuffer(&query);
-	}
+	Assert(rsinfo != NULL);
+	dobj = &rsinfo->dobj;
+	Assert(dobj != NULL);
+	relschema = dobj->namespace->dobj.name;
+	Assert(relschema != NULL);
+	relname = dobj->name;
+	Assert(relname != NULL);
 
 	initPQExpBuffer(&out);
 
@@ -10561,132 +10835,72 @@ printRelationStats(Archive *fout, const void *userArg)
 	appendPQExpBuffer(&out, "\t'relallvisible', '%d'::integer\n);\n",
 					  rsinfo->relallvisible);
 
-	/* fetch attribute stats */
-	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(&query, ", ");
-	appendStringLiteralAH(&query, dobj->name, fout);
-	appendPQExpBufferStr(&query, ")");
+	AH->txnCount++;
 
-	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+	if (attrstats.state == STATSBUF_UNINITIALIZED)
+		initAttributeStats(fout);
 
-	if (first_query)
+	/*
+	 * Because the query returns rows in the same order as the relations
+	 * requested, and because every relation gets at least one row in the
+	 * result set, the first row for this relation must correspond either to
+	 * the current row of this result set (if one exists) or the first row of
+	 * the next result set (if this one is already consumed).
+	 */
+	if (attrstats.state != STATSBUF_ACTIVE)
+		pg_fatal("Exhausted getAttributeStats() before processing %s.%s",
+				 rsinfo->dobj.namespace->dobj.name,
+				 rsinfo->dobj.name);
+
+	/*
+	 * If the current result set has been fully consumed, then the row(s) we
+	 * need (if any) would be found in the next one. This will update
+	 * attrstats.res and attrstats.idx.
+	 */
+	if (PQntuples(attrstats.res) <= attrstats.idx)
+		fetchNextAttributeStats(fout);
+
+	while (true)
 	{
-		i_attname = PQfnumber(res, "attname");
-		i_inherited = PQfnumber(res, "inherited");
-		i_null_frac = PQfnumber(res, "null_frac");
-		i_avg_width = PQfnumber(res, "avg_width");
-		i_n_distinct = PQfnumber(res, "n_distinct");
-		i_most_common_vals = PQfnumber(res, "most_common_vals");
-		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-		i_correlation = PQfnumber(res, "correlation");
-		i_most_common_elems = PQfnumber(res, "most_common_elems");
-		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
-		first_query = false;
-	}
-
-	/* restore attribute stats */
-	for (int rownum = 0; rownum < PQntuples(res); rownum++)
-	{
-		const char *attname;
-
-		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
-						  fout->remoteVersion);
-		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");
-		attname = PQgetvalue(res, rownum, i_attname);
+		int			i_schemaname;
+		int			i_tablename;
+		char	   *schemaname;
+		char	   *tablename;	/* misnomer, following pg_stats naming */
 
 		/*
-		 * Indexes look up attname in indAttNames to derive attnum, all others
-		 * use attname directly.  We must specify attnum for indexes, since
-		 * their attnames are not necessarily stable across dump/reload.
+		 * If we hit the end of the result set, then there are no more records
+		 * for this relation, so we should stop, but first get the next result
+		 * set for the next batch of relations.
 		 */
-		if (rsinfo->nindAttNames == 0)
+		if (PQntuples(attrstats.res) <= attrstats.idx)
 		{
-			appendPQExpBuffer(&out, ",\n\t'attname', ");
-			appendStringLiteralAH(&out, attname, fout);
-		}
-		else
-		{
-			bool		found = false;
-
-			for (int i = 0; i < rsinfo->nindAttNames; i++)
-			{
-				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
-				{
-					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
-									  i + 1);
-					found = true;
-					break;
-				}
-			}
-
-			if (!found)
-				pg_fatal("could not find index attname \"%s\"", attname);
+			fetchNextAttributeStats(fout);
+			break;
 		}
 
-		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(&out, fout, "inherited", "boolean",
-								PQgetvalue(res, rownum, i_inherited));
-		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(&out, fout, "null_frac", "real",
-								PQgetvalue(res, rownum, i_null_frac));
-		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(&out, fout, "avg_width", "integer",
-								PQgetvalue(res, rownum, i_avg_width));
-		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(&out, fout, "n_distinct", "real",
-								PQgetvalue(res, rownum, i_n_distinct));
-		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(&out, fout, "most_common_vals", "text",
-								PQgetvalue(res, rownum, i_most_common_vals));
-		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_freqs));
-		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(&out, fout, "histogram_bounds", "text",
-								PQgetvalue(res, rownum, i_histogram_bounds));
-		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(&out, fout, "correlation", "real",
-								PQgetvalue(res, rownum, i_correlation));
-		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(&out, fout, "most_common_elems", "text",
-								PQgetvalue(res, rownum, i_most_common_elems));
-		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_elem_freqs));
-		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
-								PQgetvalue(res, rownum, i_elem_count_histogram));
-		if (fout->remoteVersion >= 170000)
-		{
-			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(&out, fout, "range_length_histogram", "text",
-									PQgetvalue(res, rownum, i_range_length_histogram));
-			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(&out, fout, "range_empty_frac", "real",
-									PQgetvalue(res, rownum, i_range_empty_frac));
-			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
-									PQgetvalue(res, rownum, i_range_bounds_histogram));
-		}
-		appendPQExpBufferStr(&out, "\n);\n");
+		i_schemaname = PQfnumber(attrstats.res, "schemaname");
+		Assert(i_schemaname >= 0);
+		i_tablename = PQfnumber(attrstats.res, "tablename");
+		Assert(i_tablename >= 0);
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_schemaname))
+			pg_fatal("getAttributeStats() schemaname cannot be NULL");
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_tablename))
+			pg_fatal("getAttributeStats() tablename cannot be NULL");
+
+		schemaname = PQgetvalue(attrstats.res, attrstats.idx, i_schemaname);
+		tablename = PQgetvalue(attrstats.res, attrstats.idx, i_tablename);
+
+		/* stop if current stat row isn't for this relation */
+		if (strcmp(relname, tablename) != 0 || strcmp(relschema, schemaname) != 0)
+			break;
+
+		appendAttributeStats(fout, &out, rsinfo);
+		AH->txnCount++;
+		attrstats.idx++;
 	}
 
-	PQclear(res);
-
-	termPQExpBuffer(&query);
 	return out.data;
 }
 
-- 
2.48.1



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-16 01:37                 ` Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-16 01:37 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Mar 14, 2025 at 4:03 PM Corey Huinker <[email protected]>
wrote:

> New patches and a rebase.
>
> 0001 - no changes, but the longer I go the more I'm certain this is
> something we want to do.
> 0002- same as 0001
>
> 0003 -
>
> Storing the restore function calls in the archive entry hogged a lot of
> memory and made people nervous. This introduces a new function pointer that
> generates those restore SQL calls right before they're written to disk,
> thus reducing the memory load from "stats for every object to be dumped" to
> just one object. Thanks to Nathan for diagnosing some weird quirks with
> various formats.
>
> 0004 -
>
> This replaces the query in the prepared statement with one that batches
> them 100 relations at a time, and then maintains that result set until it
> is consumed. It seems to have obvious speedups.
>

Another rebase, and a new patch 0005 to have pg_dump fetch and restore
relallfrozen for dbs of version 18 and higher. With older versions we omit
relallfrozen and let the import function assign the default.


Attachments:

  [text/x-patch] v9-0005-Add-relallfrozen-to-pg_dump-statistics.patch (7.9K, ../../CADkLM=ceOSsx_=oe73QQ-BxUFR2Cwqum7-UP_fPe22DBY0NerA@mail.gmail.com/3-v9-0005-Add-relallfrozen-to-pg_dump-statistics.patch)
  download | inline diff:
From 79b459706b09458b4c27d3f80a8fab9ec9600ce7 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 15 Mar 2025 17:34:30 -0400
Subject: [PATCH v9 5/5] Add relallfrozen to pg_dump statistics.

The column relallfrozen was recently added to pg_class and it also
represent statistics, so we should add it to the dump/restore/upgrade
operations.

Dumps of databases prior to v18 will not attempt to restore any value to
relallfrozen, allowing pg_restore_relation_stats() to set the default it
deems appropriate.
---
 src/bin/pg_dump/pg_dump.c        | 52 ++++++++++++++++++++++----------
 src/bin/pg_dump/pg_dump.h        |  1 +
 src/bin/pg_dump/t/002_pg_dump.pl |  3 +-
 3 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 0c26dc7a1b4..249bcfb80a1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6856,7 +6856,8 @@ getFuncs(Archive *fout)
  */
 static RelStatsInfo *
 getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
-					  char *reltuples, int32 relallvisible, char relkind,
+					  char *reltuples, int32 relallvisible,
+					  int32 relallfrozen, char relkind,
 					  char **indAttNames, int nindAttNames)
 {
 	if (!fout->dopt->dumpStatistics)
@@ -6885,6 +6886,7 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
 		info->relpages = relpages;
 		info->reltuples = pstrdup(reltuples);
 		info->relallvisible = relallvisible;
+		info->relallfrozen = relallfrozen;
 		info->relkind = relkind;
 		info->indAttNames = indAttNames;
 		info->nindAttNames = nindAttNames;
@@ -6924,6 +6926,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relpages;
 	int			i_reltuples;
 	int			i_relallvisible;
+	int			i_relallfrozen;
 	int			i_toastpages;
 	int			i_owning_tab;
 	int			i_owning_col;
@@ -6974,8 +6977,13 @@ getTables(Archive *fout, int *numTables)
 						 "c.relowner, "
 						 "c.relchecks, "
 						 "c.relhasindex, c.relhasrules, c.relpages, "
-						 "c.reltuples, c.relallvisible, c.relhastriggers, "
-						 "c.relpersistence, "
+						 "c.reltuples, c.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "c.relallfrozen, ");
+
+	appendPQExpBufferStr(query,
+						 "c.relhastriggers, c.relpersistence, "
 						 "c.reloftype, "
 						 "c.relacl, "
 						 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
@@ -7140,6 +7148,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_toastpages = PQfnumber(res, "toastpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
@@ -7187,6 +7196,7 @@ getTables(Archive *fout, int *numTables)
 	for (i = 0; i < ntups; i++)
 	{
 		int32		relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
+		int32		relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
 
 		tblinfo[i].dobj.objType = DO_TABLE;
 		tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
@@ -7289,7 +7299,7 @@ getTables(Archive *fout, int *numTables)
 		if (tblinfo[i].interesting)
 			getRelationStatistics(fout, &tblinfo[i].dobj, tblinfo[i].relpages,
 								  PQgetvalue(res, i, i_reltuples),
-								  relallvisible, tblinfo[i].relkind, NULL, 0);
+								  relallvisible, relallfrozen, tblinfo[i].relkind, NULL, 0);
 
 		/*
 		 * Read-lock target tables to make sure they aren't DROPPED or altered
@@ -7558,6 +7568,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_relpages,
 				i_reltuples,
 				i_relallvisible,
+				i_relallfrozen,
 				i_parentidx,
 				i_indexdef,
 				i_indnkeyatts,
@@ -7612,7 +7623,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	appendPQExpBufferStr(query,
 						 "SELECT t.tableoid, t.oid, i.indrelid, "
 						 "t.relname AS indexname, "
-						 "t.relpages, t.reltuples, t.relallvisible, "
+						 "t.relpages, t.reltuples, t.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "t.relallfrozen, ");
+
+	appendPQExpBufferStr(query,
 						 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
 						 "i.indkey, i.indisclustered, "
 						 "c.contype, c.conname, "
@@ -7728,6 +7744,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_parentidx = PQfnumber(res, "parentidx");
 	i_indexdef = PQfnumber(res, "indexdef");
 	i_indnkeyatts = PQfnumber(res, "indnkeyatts");
@@ -7799,6 +7816,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 			RelStatsInfo *relstats;
 			int32		relpages = atoi(PQgetvalue(res, j, i_relpages));
 			int32		relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
+			int32		relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
 
 			indxinfo[j].dobj.objType = DO_INDEX;
 			indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
@@ -7841,7 +7859,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 
 			relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
 											 PQgetvalue(res, j, i_reltuples),
-											 relallvisible, indexkind,
+											 relallvisible, relallfrozen, indexkind,
 											 indAttNames, nindAttNames);
 
 			contype = *(PQgetvalue(res, j, i_contype));
@@ -10821,19 +10839,21 @@ printRelationStats(Archive *fout, const void *userArg)
 	initPQExpBuffer(&out);
 
 	/* restore relation stats */
-	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
-	appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
+	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(");
+	appendPQExpBuffer(&out, "\n\t'version', '%u'::integer",
 					  fout->remoteVersion);
-	appendPQExpBufferStr(&out, "\t'schemaname', ");
+	appendPQExpBufferStr(&out, ",\n\t'schemaname', ");
 	appendStringLiteralAH(&out, rsinfo->dobj.namespace->dobj.name, fout);
-	appendPQExpBufferStr(&out, ",\n");
-	appendPQExpBufferStr(&out, "\t'relname', ");
+	appendPQExpBufferStr(&out, ",\n\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",
-					  rsinfo->relallvisible);
+	appendPQExpBuffer(&out, ",\n\t'relpages', '%d'::integer", rsinfo->relpages);
+	appendPQExpBuffer(&out, ",\n\t'reltuples', '%s'::real", rsinfo->reltuples);
+	appendPQExpBuffer(&out, ",\n\t'relallvisible', '%d'::integer", rsinfo->relallvisible);
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBuffer(&out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
+
+	appendPQExpBufferStr(&out, "\n);\n");
 
 	AH->txnCount++;
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index bbdb30b5f54..82f1eb3c4b7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -441,6 +441,7 @@ typedef struct _relStatsInfo
 	int32		relpages;
 	char	   *reltuples;
 	int32		relallvisible;
+	int32		relallfrozen;
 	char		relkind;		/* 'r', 'm', 'i', etc */
 
 	/*
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index b037f239136..1d69e55a861 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4729,7 +4729,8 @@ my %tests = (
 			'relname',\s'dup_test_post_data_ix',\s+
 			'relpages',\s'\d+'::integer,\s+
 			'reltuples',\s'\d+'::real,\s+
-			'relallvisible',\s'\d+'::integer\s+
+			'relallvisible',\s'\d+'::integer,\s+
+			'relallfrozen',\s'\d+'::integer\s+
 			\);\s+
 			\QSELECT * FROM pg_catalog.pg_restore_attribute_stats(\E\s+
 			'version',\s'\d+'::integer,\s+
-- 
2.48.1



  [text/x-patch] v9-0003-Introduce-CreateStmtPtr.patch (17.2K, ../../CADkLM=ceOSsx_=oe73QQ-BxUFR2Cwqum7-UP_fPe22DBY0NerA@mail.gmail.com/4-v9-0003-Introduce-CreateStmtPtr.patch)
  download | inline diff:
From 72d0fe4b5de382a2f36151de2b26960d3a85267f Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 01:06:19 -0400
Subject: [PATCH v9 3/5] Introduce CreateStmtPtr.

CreateStmtPtr is a function pointer that can replace the createStmt/defn
parameter. This is useful in situations where the amount of text
generated for a definition is so large that it is undesirable to hold
many such objects in memory at the same time.

Using functions of this type, the text created is then immediately
written out to the appropriate file for the given dump format.
---
 src/bin/pg_dump/pg_backup.h          |   2 +
 src/bin/pg_dump/pg_backup_archiver.c |  22 ++-
 src/bin/pg_dump/pg_backup_archiver.h |   7 +
 src/bin/pg_dump/pg_dump.c            | 230 +++++++++++++++------------
 4 files changed, 156 insertions(+), 105 deletions(-)

diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index e783cc68d89..bb175874a5a 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -287,6 +287,8 @@ typedef int (*DataDumperPtr) (Archive *AH, const void *userArg);
 
 typedef void (*SetupWorkerPtrType) (Archive *AH);
 
+typedef char *(*CreateStmtPtr) (Archive *AH, const void *userArg);
+
 /*
  * Main archiver interface.
  */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 7480e122b61..3fcfecf6719 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1263,6 +1263,9 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
 	newToc->dataDumper = opts->dumpFn;
 	newToc->dataDumperArg = opts->dumpArg;
 	newToc->hadDumper = opts->dumpFn ? true : false;
+	newToc->createDumper = opts->createFn;
+	newToc->createDumperArg = opts->createArg;
+	newToc->hadCreateDumper = opts->createFn ? true : false;
 
 	newToc->formatData = NULL;
 	newToc->dataLength = 0;
@@ -2619,7 +2622,17 @@ WriteToc(ArchiveHandle *AH)
 		WriteStr(AH, te->tag);
 		WriteStr(AH, te->desc);
 		WriteInt(AH, te->section);
-		WriteStr(AH, te->defn);
+
+		if (te->hadCreateDumper)
+		{
+			char	   *defn = te->createDumper((Archive *) AH, te->createDumperArg);
+
+			WriteStr(AH, defn);
+			pg_free(defn);
+		}
+		else
+			WriteStr(AH, te->defn);
+
 		WriteStr(AH, te->dropStmt);
 		WriteStr(AH, te->copyStmt);
 		WriteStr(AH, te->namespace);
@@ -3849,6 +3862,13 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
 	{
 		IssueACLPerBlob(AH, te);
 	}
+	else if (te->hadCreateDumper)
+	{
+		char	   *ptr = te->createDumper((Archive *) AH, te->createDumperArg);
+
+		ahwrite(ptr, 1, strlen(ptr), AH);
+		pg_free(ptr);
+	}
 	else if (te->defn && strlen(te->defn) > 0)
 	{
 		ahprintf(AH, "%s\n\n", te->defn);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a2064f471ed..e68db633995 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -368,6 +368,11 @@ struct _tocEntry
 	const void *dataDumperArg;	/* Arg for above routine */
 	void	   *formatData;		/* TOC Entry data specific to file format */
 
+	CreateStmtPtr createDumper; /* Routine for create statement creation */
+	const void *createDumperArg;	/* arg for the above routine */
+	bool		hadCreateDumper;	/* Archiver was passed a create statement
+									 * routine */
+
 	/* working state while dumping/restoring */
 	pgoff_t		dataLength;		/* item's data size; 0 if none or unknown */
 	int			reqs;			/* do we need schema and/or data of object
@@ -407,6 +412,8 @@ typedef struct _archiveOpts
 	int			nDeps;
 	DataDumperPtr dumpFn;
 	const void *dumpArg;
+	CreateStmtPtr createFn;
+	const void *createArg;
 } ArchiveOpts;
 #define ARCHIVE_OPTS(...) &(ArchiveOpts){__VA_ARGS__}
 /* Called to add a TOC entry */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bd857bb076c..38ba6a90106 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10477,51 +10477,44 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
 }
 
 /*
- * dumpRelationStats --
+ * printDumpRelationStats --
  *
- * Dump command to import stats into the relation on the new database.
+ * Generate the SQL statements needed to restore a relation's statistics.
  */
-static void
-dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+static char *
+printRelationStats(Archive *fout, const void *userArg)
 {
+	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
 	const DumpableObject *dobj = &rsinfo->dobj;
+
+	PQExpBufferData query;
+	PQExpBufferData out;
+
 	PGresult   *res;
-	PQExpBuffer query;
-	PQExpBuffer out;
-	DumpId	   *deps = NULL;
-	int			ndeps = 0;
-	int			i_attname;
-	int			i_inherited;
-	int			i_null_frac;
-	int			i_avg_width;
-	int			i_n_distinct;
-	int			i_most_common_vals;
-	int			i_most_common_freqs;
-	int			i_histogram_bounds;
-	int			i_correlation;
-	int			i_most_common_elems;
-	int			i_most_common_elem_freqs;
-	int			i_elem_count_histogram;
-	int			i_range_length_histogram;
-	int			i_range_empty_frac;
-	int			i_range_bounds_histogram;
 
-	/* nothing to do if we are not dumping statistics */
-	if (!fout->dopt->dumpStatistics)
-		return;
+	static bool first_query = true;
+	static int	i_attname;
+	static int	i_inherited;
+	static int	i_null_frac;
+	static int	i_avg_width;
+	static int	i_n_distinct;
+	static int	i_most_common_vals;
+	static int	i_most_common_freqs;
+	static int	i_histogram_bounds;
+	static int	i_correlation;
+	static int	i_most_common_elems;
+	static int	i_most_common_elem_freqs;
+	static int	i_elem_count_histogram;
+	static int	i_range_length_histogram;
+	static int	i_range_empty_frac;
+	static int	i_range_bounds_histogram;
 
-	/* dependent on the relation definition, if doing schema */
-	if (fout->dopt->dumpSchema)
+	initPQExpBuffer(&query);
+
+	if (first_query)
 	{
-		deps = dobj->dependencies;
-		ndeps = dobj->nDeps;
-	}
-
-	query = createPQExpBuffer();
-	if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
-	{
-		appendPQExpBufferStr(query,
-							 "PREPARE getAttributeStats(pg_catalog.name, pg_catalog.name) AS\n"
+		appendPQExpBufferStr(&query,
+							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
 							 "SELECT s.attname, s.inherited, "
 							 "s.null_frac, s.avg_width, s.n_distinct, "
 							 "s.most_common_vals, s.most_common_freqs, "
@@ -10530,82 +10523,85 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 							 "s.elem_count_histogram, ");
 
 		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "s.range_length_histogram, "
 								 "s.range_empty_frac, "
 								 "s.range_bounds_histogram ");
 		else
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "NULL AS range_length_histogram,"
 								 "NULL AS range_empty_frac,"
 								 "NULL AS range_bounds_histogram ");
 
-		appendPQExpBufferStr(query,
+		appendPQExpBufferStr(&query,
 							 "FROM pg_catalog.pg_stats s "
 							 "WHERE s.schemaname = $1 "
 							 "AND s.tablename = $2 "
 							 "ORDER BY s.attname, s.inherited");
 
-		ExecuteSqlStatement(fout, query->data);
+		ExecuteSqlStatement(fout, query.data);
 
-		fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
-		resetPQExpBuffer(query);
+		resetPQExpBuffer(&query);
 	}
 
-	out = createPQExpBuffer();
+	initPQExpBuffer(&out);
 
 	/* restore relation stats */
-	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
-	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
+	appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 					  fout->remoteVersion);
-	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",
+	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",
 					  rsinfo->relallvisible);
 
 	/* fetch attribute stats */
-	appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(query, ", ");
-	appendStringLiteralAH(query, dobj->name, fout);
-	appendPQExpBufferStr(query, ");");
+	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
+	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
+	appendPQExpBufferStr(&query, ", ");
+	appendStringLiteralAH(&query, dobj->name, fout);
+	appendPQExpBufferStr(&query, ")");
 
-	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
 
-	i_attname = PQfnumber(res, "attname");
-	i_inherited = PQfnumber(res, "inherited");
-	i_null_frac = PQfnumber(res, "null_frac");
-	i_avg_width = PQfnumber(res, "avg_width");
-	i_n_distinct = PQfnumber(res, "n_distinct");
-	i_most_common_vals = PQfnumber(res, "most_common_vals");
-	i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-	i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-	i_correlation = PQfnumber(res, "correlation");
-	i_most_common_elems = PQfnumber(res, "most_common_elems");
-	i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-	i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-	i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-	i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-	i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+	if (first_query)
+	{
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		first_query = false;
+	}
 
 	/* restore attribute stats */
 	for (int rownum = 0; rownum < PQntuples(res); rownum++)
 	{
 		const char *attname;
 
-		appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 						  fout->remoteVersion);
-		appendPQExpBufferStr(out, "\t'schemaname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
-		appendPQExpBufferStr(out, ",\n\t'relname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.name, fout);
+		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");
@@ -10618,8 +10614,8 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		 */
 		if (rsinfo->nindAttNames == 0)
 		{
-			appendPQExpBuffer(out, ",\n\t'attname', ");
-			appendStringLiteralAH(out, attname, fout);
+			appendPQExpBuffer(&out, ",\n\t'attname', ");
+			appendStringLiteralAH(&out, attname, fout);
 		}
 		else
 		{
@@ -10629,7 +10625,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 			{
 				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
 				{
-					appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
 									  i + 1);
 					found = true;
 					break;
@@ -10641,67 +10637,93 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		}
 
 		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(out, fout, "inherited", "boolean",
+			appendNamedArgument(&out, fout, "inherited", "boolean",
 								PQgetvalue(res, rownum, i_inherited));
 		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(out, fout, "null_frac", "real",
+			appendNamedArgument(&out, fout, "null_frac", "real",
 								PQgetvalue(res, rownum, i_null_frac));
 		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(out, fout, "avg_width", "integer",
+			appendNamedArgument(&out, fout, "avg_width", "integer",
 								PQgetvalue(res, rownum, i_avg_width));
 		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(out, fout, "n_distinct", "real",
+			appendNamedArgument(&out, fout, "n_distinct", "real",
 								PQgetvalue(res, rownum, i_n_distinct));
 		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(out, fout, "most_common_vals", "text",
+			appendNamedArgument(&out, fout, "most_common_vals", "text",
 								PQgetvalue(res, rownum, i_most_common_vals));
 		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_freqs));
 		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(out, fout, "histogram_bounds", "text",
+			appendNamedArgument(&out, fout, "histogram_bounds", "text",
 								PQgetvalue(res, rownum, i_histogram_bounds));
 		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(out, fout, "correlation", "real",
+			appendNamedArgument(&out, fout, "correlation", "real",
 								PQgetvalue(res, rownum, i_correlation));
 		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(out, fout, "most_common_elems", "text",
+			appendNamedArgument(&out, fout, "most_common_elems", "text",
 								PQgetvalue(res, rownum, i_most_common_elems));
 		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_elem_freqs));
 		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
 								PQgetvalue(res, rownum, i_elem_count_histogram));
 		if (fout->remoteVersion >= 170000)
 		{
 			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(out, fout, "range_length_histogram", "text",
+				appendNamedArgument(&out, fout, "range_length_histogram", "text",
 									PQgetvalue(res, rownum, i_range_length_histogram));
 			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(out, fout, "range_empty_frac", "real",
+				appendNamedArgument(&out, fout, "range_empty_frac", "real",
 									PQgetvalue(res, rownum, i_range_empty_frac));
 			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
 									PQgetvalue(res, rownum, i_range_bounds_histogram));
 		}
-		appendPQExpBufferStr(out, "\n);\n");
+		appendPQExpBufferStr(&out, "\n);\n");
 	}
 
 	PQclear(res);
 
+	termPQExpBuffer(&query);
+	return out.data;
+}
+
+/*
+ * dumpRelationStats --
+ *
+ * Dump command to import stats into the relation on the new database.
+ */
+static void
+dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+{
+	const DumpableObject *dobj = &rsinfo->dobj;
+
+	DumpId	   *deps = NULL;
+	int			ndeps = 0;
+
+	/* nothing to do if we are not dumping statistics */
+	if (!fout->dopt->dumpStatistics)
+		return;
+
+	/* dependent on the relation definition, if doing schema */
+	if (fout->dopt->dumpSchema)
+	{
+		deps = dobj->dependencies;
+		ndeps = dobj->nDeps;
+	}
+
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 ARCHIVE_OPTS(.tag = dobj->name,
 							  .namespace = dobj->namespace->dobj.name,
 							  .description = "STATISTICS DATA",
 							  .section = rsinfo->postponed_def ?
 							  SECTION_POST_DATA : statisticsDumpSection(rsinfo),
-							  .createStmt = out->data,
+							  .createFn = printRelationStats,
+							  .createArg = rsinfo,
 							  .deps = deps,
 							  .nDeps = ndeps));
-
-	destroyPQExpBuffer(out);
-	destroyPQExpBuffer(query);
 }
 
 /*
-- 
2.48.1



  [text/x-patch] v9-0001-Split-relation-into-schemaname-and-relname.patch (65.0K, ../../CADkLM=ceOSsx_=oe73QQ-BxUFR2Cwqum7-UP_fPe22DBY0NerA@mail.gmail.com/5-v9-0001-Split-relation-into-schemaname-and-relname.patch)
  download | inline diff:
From fd2cced89c11353e909e6ef9bb824a3a7536e6cb Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Tue, 4 Mar 2025 22:16:52 -0500
Subject: [PATCH v9 1/5] Split relation into schemaname and relname.

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

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

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

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

base-commit: 5eabd91a83adae75f53b61857343660919fef4c7
-- 
2.48.1



  [text/x-patch] v9-0002-Downgrade-as-man-pg_restore_-_stats-errors-to-war.patch (29.8K, ../../CADkLM=ceOSsx_=oe73QQ-BxUFR2Cwqum7-UP_fPe22DBY0NerA@mail.gmail.com/6-v9-0002-Downgrade-as-man-pg_restore_-_stats-errors-to-war.patch)
  download | inline diff:
From b5b96d4a2fe4119ef26689cc8f3e1a5b8d24bdda Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 8 Mar 2025 00:52:41 -0500
Subject: [PATCH v9 2/5] Downgrade as man pg_restore_*_stats errors to
 warnings.

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

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



  [text/x-patch] v9-0004-Batching-getAttributeStats.patch (21.6K, ../../CADkLM=ceOSsx_=oe73QQ-BxUFR2Cwqum7-UP_fPe22DBY0NerA@mail.gmail.com/7-v9-0004-Batching-getAttributeStats.patch)
  download | inline diff:
From d9ab97b8ca00d03ab370de5b99546d01e0480bd5 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 03:54:26 -0400
Subject: [PATCH v9 4/5] Batching getAttributeStats().

The prepared statement getAttributeStats() is fairly heavyweight and
could greatly increase pg_dump/pg_upgrade runtime. To alleviate this,
create a result set buffer of all of the attribute stats fetched for a
batch of 100 relations that could potentially have stats.

The query ensures that the order of results exactly matches the needs of
the code walking the TOC to print the stats calls.
---
 src/bin/pg_dump/pg_dump.c | 556 ++++++++++++++++++++++++++------------
 1 file changed, 385 insertions(+), 171 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 38ba6a90106..0c26dc7a1b4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -143,6 +143,25 @@ typedef enum OidOptions
 	zeroAsNone = 4,
 } OidOptions;
 
+typedef enum StatsBufferState
+{
+	STATSBUF_UNINITIALIZED = 0,
+	STATSBUF_ACTIVE,
+	STATSBUF_EXHAUSTED
+}			StatsBufferState;
+
+typedef struct
+{
+	PGresult   *res;			/* results from most recent
+								 * getAttributeStats() */
+	int			idx;			/* first un-consumed row of results */
+	TocEntry   *te;				/* next TOC entry to search for statsitics
+								 * data */
+
+	StatsBufferState state;		/* current state of the buffer */
+}			AttributeStatsBuffer;
+
+
 /* global decls */
 static bool dosync = true;		/* Issue fsync() to make dump durable on disk. */
 
@@ -209,6 +228,18 @@ static int	nbinaryUpgradeClassOids = 0;
 static SequenceItem *sequences = NULL;
 static int	nsequences = 0;
 
+static AttributeStatsBuffer attrstats =
+{
+	NULL, 0, NULL, STATSBUF_UNINITIALIZED
+};
+
+/*
+ * The maximum number of relations that should be fetched in any one
+ * getAttributeStats() call.
+ */
+
+#define MAX_ATTR_STATS_RELS 100
+
 /*
  * The default number of rows per INSERT when
  * --inserts is specified without --rows-per-insert
@@ -222,6 +253,10 @@ static int	nsequences = 0;
  */
 #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
 
+
+
+/* TODO: fmtId(const char *rawid) */
+
 /*
  * Macro for producing quoted, schema-qualified name of a dumpable object.
  */
@@ -399,6 +434,9 @@ static void setupDumpWorker(Archive *AH);
 static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
 static bool forcePartitionRootLoad(const TableInfo *tbinfo);
 static void read_dump_filters(const char *filename, DumpOptions *dopt);
+static void appendNamedArgument(PQExpBuffer out, Archive *fout,
+								const char *argname, const char *argtype,
+								const char *argval);
 
 
 int
@@ -10477,7 +10515,286 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
 }
 
 /*
- * printDumpRelationStats --
+ * Fetch next batch of rows from getAttributeStats()
+ */
+static void
+fetchNextAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData schemas;
+	PQExpBufferData relations;
+	int			numoids = 0;
+
+	Assert(AH != NULL);
+
+	/* free last result set, if any */
+	if (attrstats.state == STATSBUF_ACTIVE)
+		PQclear(attrstats.res);
+
+	/* If we have looped around to the start of the TOC, restart */
+	if (attrstats.te == AH->toc)
+		attrstats.te = AH->toc->next;
+
+	initPQExpBuffer(&schemas);
+	initPQExpBuffer(&relations);
+
+	/*
+	 * Walk ahead looking for relstats entries that are active in this
+	 * section, adding the names to the schemas and relations lists.
+	 */
+	while ((attrstats.te != AH->toc) && (numoids < MAX_ATTR_STATS_RELS))
+	{
+		if (attrstats.te->reqs != 0 &&
+			strcmp(attrstats.te->desc, "STATISTICS DATA") == 0)
+		{
+			RelStatsInfo *rsinfo = (RelStatsInfo *) attrstats.te->createDumperArg;
+
+			Assert(rsinfo != NULL);
+
+			if (numoids > 0)
+			{
+				appendPQExpBufferStr(&schemas, ",");
+				appendPQExpBufferStr(&relations, ",");
+			}
+			appendPQExpBufferStr(&schemas, fmtId(rsinfo->dobj.namespace->dobj.name));
+			appendPQExpBufferStr(&relations, fmtId(rsinfo->dobj.name));
+			numoids++;
+		}
+
+		attrstats.te = attrstats.te->next;
+	}
+
+	if (numoids > 0)
+	{
+		PQExpBufferData query;
+
+		initPQExpBuffer(&query);
+		appendPQExpBuffer(&query,
+						  "EXECUTE getAttributeStats('{%s}'::pg_catalog.text[],'{%s}'::pg_catalog.text[])",
+						  schemas.data, relations.data);
+		attrstats.res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+		attrstats.idx = 0;
+	}
+	else
+	{
+		attrstats.state = STATSBUF_EXHAUSTED;
+		attrstats.res = NULL;
+		attrstats.idx = -1;
+	}
+
+	termPQExpBuffer(&schemas);
+	termPQExpBuffer(&relations);
+}
+
+/*
+ * Prepare the getAttributeStats() statement
+ *
+ * This is done automatically if the user specified dumpStatistics.
+ */
+static void
+initAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData query;
+
+	Assert(AH != NULL);
+	initPQExpBuffer(&query);
+
+	appendPQExpBufferStr(&query,
+						 "PREPARE getAttributeStats(pg_catalog.text[], pg_catalog.text[]) AS\n"
+						 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
+						 "s.null_frac, s.avg_width, s.n_distinct, s.most_common_vals, "
+						 "s.most_common_freqs, s.histogram_bounds, s.correlation, "
+						 "s.most_common_elems, s.most_common_elem_freqs, "
+						 "s.elem_count_histogram, ");
+
+	if (fout->remoteVersion >= 170000)
+		appendPQExpBufferStr(&query,
+							 "s.range_length_histogram, "
+							 "s.range_empty_frac, "
+							 "s.range_bounds_histogram ");
+	else
+		appendPQExpBufferStr(&query,
+							 "NULL AS range_length_histogram, "
+							 "NULL AS range_empty_frac, "
+							 " NULL AS range_bounds_histogram ");
+
+	/*
+	 * The results must be in the order of relations supplied in the
+	 * parameters to ensure that they are in sync with a walk of the TOC.
+	 *
+	 * The redundant (and incomplete) filter clause on s.tablename = ANY(...)
+	 * is a way to lead the query into using the index
+	 * pg_class_relname_nsp_index which in turn allows the planner to avoid an
+	 * expensive full scan of pg_stats.
+	 *
+	 * We may need to adjust this query for versions that are not so easily
+	 * led.
+	 */
+	appendPQExpBufferStr(&query,
+						 "FROM pg_catalog.pg_stats AS s "
+						 "JOIN unnest($1, $2) WITH ORDINALITY AS u(schemaname, tablename, ord) "
+						 "ON s.schemaname = u.schemaname "
+						 "AND s.tablename = u.tablename "
+						 "WHERE s.tablename = ANY($2) "
+						 "ORDER BY u.ord, s.attname, s.inherited");
+
+	ExecuteSqlStatement(fout, query.data);
+
+	termPQExpBuffer(&query);
+
+	attrstats.te = AH->toc->next;
+
+	fetchNextAttributeStats(fout);
+
+	attrstats.state = STATSBUF_ACTIVE;
+}
+
+
+/*
+ * append a single attribute stat to the buffer for this relation.
+ */
+static void
+appendAttributeStats(Archive *fout, PQExpBuffer out,
+					 const RelStatsInfo *rsinfo)
+{
+	PGresult   *res = attrstats.res;
+	int			tup_num = attrstats.idx;
+
+	const char *attname;
+
+	static bool indexes_set = false;
+	static int	i_attname,
+				i_inherited,
+				i_null_frac,
+				i_avg_width,
+				i_n_distinct,
+				i_most_common_vals,
+				i_most_common_freqs,
+				i_histogram_bounds,
+				i_correlation,
+				i_most_common_elems,
+				i_most_common_elem_freqs,
+				i_elem_count_histogram,
+				i_range_length_histogram,
+				i_range_empty_frac,
+				i_range_bounds_histogram;
+
+	if (!indexes_set)
+	{
+		/*
+		 * It's a prepared statement, so the indexes will be the same for all
+		 * result sets, so we only need to set them once.
+		 */
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		indexes_set = true;
+	}
+
+	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+					  fout->remoteVersion);
+	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, tup_num, i_attname))
+		pg_fatal("attname cannot be NULL");
+	attname = PQgetvalue(res, tup_num, i_attname);
+
+	/*
+	 * Indexes look up attname in indAttNames to derive attnum, all others use
+	 * attname directly.  We must specify attnum for indexes, since their
+	 * attnames are not necessarily stable across dump/reload.
+	 */
+	if (rsinfo->nindAttNames == 0)
+	{
+		appendPQExpBuffer(out, ",\n\t'attname', ");
+		appendStringLiteralAH(out, attname, fout);
+	}
+	else
+	{
+		bool		found = false;
+
+		for (int i = 0; i < rsinfo->nindAttNames; i++)
+			if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
+			{
+				appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+								  i + 1);
+				found = true;
+				break;
+			}
+
+		if (!found)
+			pg_fatal("could not find index attname \"%s\"", attname);
+	}
+
+	if (!PQgetisnull(res, tup_num, i_inherited))
+		appendNamedArgument(out, fout, "inherited", "boolean",
+							PQgetvalue(res, tup_num, i_inherited));
+	if (!PQgetisnull(res, tup_num, i_null_frac))
+		appendNamedArgument(out, fout, "null_frac", "real",
+							PQgetvalue(res, tup_num, i_null_frac));
+	if (!PQgetisnull(res, tup_num, i_avg_width))
+		appendNamedArgument(out, fout, "avg_width", "integer",
+							PQgetvalue(res, tup_num, i_avg_width));
+	if (!PQgetisnull(res, tup_num, i_n_distinct))
+		appendNamedArgument(out, fout, "n_distinct", "real",
+							PQgetvalue(res, tup_num, i_n_distinct));
+	if (!PQgetisnull(res, tup_num, i_most_common_vals))
+		appendNamedArgument(out, fout, "most_common_vals", "text",
+							PQgetvalue(res, tup_num, i_most_common_vals));
+	if (!PQgetisnull(res, tup_num, i_most_common_freqs))
+		appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_freqs));
+	if (!PQgetisnull(res, tup_num, i_histogram_bounds))
+		appendNamedArgument(out, fout, "histogram_bounds", "text",
+							PQgetvalue(res, tup_num, i_histogram_bounds));
+	if (!PQgetisnull(res, tup_num, i_correlation))
+		appendNamedArgument(out, fout, "correlation", "real",
+							PQgetvalue(res, tup_num, i_correlation));
+	if (!PQgetisnull(res, tup_num, i_most_common_elems))
+		appendNamedArgument(out, fout, "most_common_elems", "text",
+							PQgetvalue(res, tup_num, i_most_common_elems));
+	if (!PQgetisnull(res, tup_num, i_most_common_elem_freqs))
+		appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_elem_freqs));
+	if (!PQgetisnull(res, tup_num, i_elem_count_histogram))
+		appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+							PQgetvalue(res, tup_num, i_elem_count_histogram));
+	if (fout->remoteVersion >= 170000)
+	{
+		if (!PQgetisnull(res, tup_num, i_range_length_histogram))
+			appendNamedArgument(out, fout, "range_length_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_length_histogram));
+		if (!PQgetisnull(res, tup_num, i_range_empty_frac))
+			appendNamedArgument(out, fout, "range_empty_frac", "real",
+								PQgetvalue(res, tup_num, i_range_empty_frac));
+		if (!PQgetisnull(res, tup_num, i_range_bounds_histogram))
+			appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_bounds_histogram));
+	}
+	appendPQExpBufferStr(out, "\n);\n");
+}
+
+
+
+/*
+ * printRelationStats --
  *
  * Generate the SQL statements needed to restore a relation's statistics.
  */
@@ -10485,64 +10802,21 @@ static char *
 printRelationStats(Archive *fout, const void *userArg)
 {
 	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
-	const DumpableObject *dobj = &rsinfo->dobj;
+	const DumpableObject *dobj;
+	const char *relschema;
+	const char *relname;
+
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
 
-	PQExpBufferData query;
 	PQExpBufferData out;
 
-	PGresult   *res;
-
-	static bool first_query = true;
-	static int	i_attname;
-	static int	i_inherited;
-	static int	i_null_frac;
-	static int	i_avg_width;
-	static int	i_n_distinct;
-	static int	i_most_common_vals;
-	static int	i_most_common_freqs;
-	static int	i_histogram_bounds;
-	static int	i_correlation;
-	static int	i_most_common_elems;
-	static int	i_most_common_elem_freqs;
-	static int	i_elem_count_histogram;
-	static int	i_range_length_histogram;
-	static int	i_range_empty_frac;
-	static int	i_range_bounds_histogram;
-
-	initPQExpBuffer(&query);
-
-	if (first_query)
-	{
-		appendPQExpBufferStr(&query,
-							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
-							 "SELECT s.attname, s.inherited, "
-							 "s.null_frac, s.avg_width, s.n_distinct, "
-							 "s.most_common_vals, s.most_common_freqs, "
-							 "s.histogram_bounds, s.correlation, "
-							 "s.most_common_elems, s.most_common_elem_freqs, "
-							 "s.elem_count_histogram, ");
-
-		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(&query,
-								 "s.range_length_histogram, "
-								 "s.range_empty_frac, "
-								 "s.range_bounds_histogram ");
-		else
-			appendPQExpBufferStr(&query,
-								 "NULL AS range_length_histogram,"
-								 "NULL AS range_empty_frac,"
-								 "NULL AS range_bounds_histogram ");
-
-		appendPQExpBufferStr(&query,
-							 "FROM pg_catalog.pg_stats s "
-							 "WHERE s.schemaname = $1 "
-							 "AND s.tablename = $2 "
-							 "ORDER BY s.attname, s.inherited");
-
-		ExecuteSqlStatement(fout, query.data);
-
-		resetPQExpBuffer(&query);
-	}
+	Assert(rsinfo != NULL);
+	dobj = &rsinfo->dobj;
+	Assert(dobj != NULL);
+	relschema = dobj->namespace->dobj.name;
+	Assert(relschema != NULL);
+	relname = dobj->name;
+	Assert(relname != NULL);
 
 	initPQExpBuffer(&out);
 
@@ -10561,132 +10835,72 @@ printRelationStats(Archive *fout, const void *userArg)
 	appendPQExpBuffer(&out, "\t'relallvisible', '%d'::integer\n);\n",
 					  rsinfo->relallvisible);
 
-	/* fetch attribute stats */
-	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(&query, ", ");
-	appendStringLiteralAH(&query, dobj->name, fout);
-	appendPQExpBufferStr(&query, ")");
+	AH->txnCount++;
 
-	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+	if (attrstats.state == STATSBUF_UNINITIALIZED)
+		initAttributeStats(fout);
 
-	if (first_query)
+	/*
+	 * Because the query returns rows in the same order as the relations
+	 * requested, and because every relation gets at least one row in the
+	 * result set, the first row for this relation must correspond either to
+	 * the current row of this result set (if one exists) or the first row of
+	 * the next result set (if this one is already consumed).
+	 */
+	if (attrstats.state != STATSBUF_ACTIVE)
+		pg_fatal("Exhausted getAttributeStats() before processing %s.%s",
+				 rsinfo->dobj.namespace->dobj.name,
+				 rsinfo->dobj.name);
+
+	/*
+	 * If the current result set has been fully consumed, then the row(s) we
+	 * need (if any) would be found in the next one. This will update
+	 * attrstats.res and attrstats.idx.
+	 */
+	if (PQntuples(attrstats.res) <= attrstats.idx)
+		fetchNextAttributeStats(fout);
+
+	while (true)
 	{
-		i_attname = PQfnumber(res, "attname");
-		i_inherited = PQfnumber(res, "inherited");
-		i_null_frac = PQfnumber(res, "null_frac");
-		i_avg_width = PQfnumber(res, "avg_width");
-		i_n_distinct = PQfnumber(res, "n_distinct");
-		i_most_common_vals = PQfnumber(res, "most_common_vals");
-		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-		i_correlation = PQfnumber(res, "correlation");
-		i_most_common_elems = PQfnumber(res, "most_common_elems");
-		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
-		first_query = false;
-	}
-
-	/* restore attribute stats */
-	for (int rownum = 0; rownum < PQntuples(res); rownum++)
-	{
-		const char *attname;
-
-		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
-						  fout->remoteVersion);
-		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");
-		attname = PQgetvalue(res, rownum, i_attname);
+		int			i_schemaname;
+		int			i_tablename;
+		char	   *schemaname;
+		char	   *tablename;	/* misnomer, following pg_stats naming */
 
 		/*
-		 * Indexes look up attname in indAttNames to derive attnum, all others
-		 * use attname directly.  We must specify attnum for indexes, since
-		 * their attnames are not necessarily stable across dump/reload.
+		 * If we hit the end of the result set, then there are no more records
+		 * for this relation, so we should stop, but first get the next result
+		 * set for the next batch of relations.
 		 */
-		if (rsinfo->nindAttNames == 0)
+		if (PQntuples(attrstats.res) <= attrstats.idx)
 		{
-			appendPQExpBuffer(&out, ",\n\t'attname', ");
-			appendStringLiteralAH(&out, attname, fout);
-		}
-		else
-		{
-			bool		found = false;
-
-			for (int i = 0; i < rsinfo->nindAttNames; i++)
-			{
-				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
-				{
-					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
-									  i + 1);
-					found = true;
-					break;
-				}
-			}
-
-			if (!found)
-				pg_fatal("could not find index attname \"%s\"", attname);
+			fetchNextAttributeStats(fout);
+			break;
 		}
 
-		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(&out, fout, "inherited", "boolean",
-								PQgetvalue(res, rownum, i_inherited));
-		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(&out, fout, "null_frac", "real",
-								PQgetvalue(res, rownum, i_null_frac));
-		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(&out, fout, "avg_width", "integer",
-								PQgetvalue(res, rownum, i_avg_width));
-		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(&out, fout, "n_distinct", "real",
-								PQgetvalue(res, rownum, i_n_distinct));
-		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(&out, fout, "most_common_vals", "text",
-								PQgetvalue(res, rownum, i_most_common_vals));
-		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_freqs));
-		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(&out, fout, "histogram_bounds", "text",
-								PQgetvalue(res, rownum, i_histogram_bounds));
-		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(&out, fout, "correlation", "real",
-								PQgetvalue(res, rownum, i_correlation));
-		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(&out, fout, "most_common_elems", "text",
-								PQgetvalue(res, rownum, i_most_common_elems));
-		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_elem_freqs));
-		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
-								PQgetvalue(res, rownum, i_elem_count_histogram));
-		if (fout->remoteVersion >= 170000)
-		{
-			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(&out, fout, "range_length_histogram", "text",
-									PQgetvalue(res, rownum, i_range_length_histogram));
-			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(&out, fout, "range_empty_frac", "real",
-									PQgetvalue(res, rownum, i_range_empty_frac));
-			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
-									PQgetvalue(res, rownum, i_range_bounds_histogram));
-		}
-		appendPQExpBufferStr(&out, "\n);\n");
+		i_schemaname = PQfnumber(attrstats.res, "schemaname");
+		Assert(i_schemaname >= 0);
+		i_tablename = PQfnumber(attrstats.res, "tablename");
+		Assert(i_tablename >= 0);
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_schemaname))
+			pg_fatal("getAttributeStats() schemaname cannot be NULL");
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_tablename))
+			pg_fatal("getAttributeStats() tablename cannot be NULL");
+
+		schemaname = PQgetvalue(attrstats.res, attrstats.idx, i_schemaname);
+		tablename = PQgetvalue(attrstats.res, attrstats.idx, i_tablename);
+
+		/* stop if current stat row isn't for this relation */
+		if (strcmp(relname, tablename) != 0 || strcmp(relschema, schemaname) != 0)
+			break;
+
+		appendAttributeStats(fout, &out, rsinfo);
+		AH->txnCount++;
+		attrstats.idx++;
 	}
 
-	PQclear(res);
-
-	termPQExpBuffer(&query);
 	return out.data;
 }
 
-- 
2.48.1



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-19 22:17                   ` Jeff Davis <[email protected]>
  2025-03-19 22:35                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Jeff Davis @ 2025-03-19 22:17 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Sat, 2025-03-15 at 21:37 -0400, Corey Huinker wrote:
> > 0001 - no changes, but the longer I go the more I'm certain this is
> > something we want to do.

This replaces regclassin with custom lookups of the namespace and
relname, but misses some of the complexities that regclassin is
handling. For instance, it calls RangeVarGetRelid(), which calls
LookupExplicitNamespace(), which handles temp tables and
InvokeNamespaceSearchHook().

At first it looked like a bit too much code to copy, but regclassin()
passes NoLock, which means we basically just have to call
LookupExplicitNamespace().

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-19 22:35                     ` Corey Huinker <[email protected]>
  1 sibling, 0 replies; 88+ messages in thread

From: Corey Huinker @ 2025-03-19 22:35 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> This replaces regclassin with custom lookups of the namespace and
> relname, but misses some of the complexities that regclassin is
> handling. For instance, it calls RangeVarGetRelid(), which calls
> LookupExplicitNamespace(), which handles temp tables and
> InvokeNamespaceSearchHook().
>
> At first it looked like a bit too much code to copy, but regclassin()
> passes NoLock, which means we basically just have to call
> LookupExplicitNamespace().


To be clear, LookupExplicitNamespace() can call aclcheck_error(), which is
something we cannot presently step-down into a WARNING, so an aclcheck
failure inside a restore/upgrade would fail the upgrade. I want to make
sure we can live with that because it might be hard to explain what's an
error we can nerf and what isn't.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-25 06:53                     ` Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-25 06:53 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Wed, 2025-03-19 at 15:17 -0700, Jeff Davis wrote:
> On Sat, 2025-03-15 at 21:37 -0400, Corey Huinker wrote:
> > > 0001 - no changes, but the longer I go the more I'm certain this
> > > is
> > > something we want to do.
> 
> This replaces regclassin with custom lookups of the namespace and
> relname, but misses some of the complexities that regclassin is
> handling. For instance, it calls RangeVarGetRelid(), which calls
> LookupExplicitNamespace(), which handles temp tables and
> InvokeNamespaceSearchHook().
> 
> At first it looked like a bit too much code to copy, but regclassin()
> passes NoLock, which means we basically just have to call
> LookupExplicitNamespace().

Attached new version 9j:

* Changed to use LookupExplicitNamespace()
* Added test for temp tables
* Doc fixes

Regards,
	Jeff Davis



Attachments:

  [text/x-patch] v9j-0001-Stats-use-schemaname-relname-instead-of-regclass.patch (68.4K, ../../[email protected]/2-v9j-0001-Stats-use-schemaname-relname-instead-of-regclass.patch)
  download | inline diff:
From 72d4b9fc128e6d4ef73bb24ebba41797d06a7d9e Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
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 <[email protected]>
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



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-25 14:53                       ` Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-25 14:53 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> * Changed to use LookupExplicitNamespace()
>

Seems good.


> * Added test for temp tables
>

+1


> * Doc fixes


So this patch swings the pendulum a bit back towards accepting some things
as errors. That's understandable, as we're never going to have a situation
where we can guarantee that the restore functions never generate an error,
so the best we can do is to draw the error-versus-warning line at a place
that:

* doesn't mess up flawed restores that we would otherwise expect to
complete at least partially
* is easy for us to understand
* is easy for us to explain
* we can live with for the next couple of decades

I don't know where that line should be drawn, so if people are happy with
Jeff's demarcation, then less roll with it.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-25 18:42                         ` Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-25 18:42 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, 2025-03-25 at 10:53 -0400, Corey Huinker wrote:
> 
> So this patch swings the pendulum a bit back towards accepting some
> things as errors.

Not exactly. I see patch 0001 as a change to the function signatures
from regclass to schemaname/relname, both for usability as well as
control over ERROR vs WARNING.

There's agreement to do so, so I went ahead and committed that part.

> the best we can do is to draw the error-versus-warning line at a
> place that:
> 
> * doesn't mess up flawed restores that we would otherwise expect to
> complete at least partially
> * is easy for us to understand
> * is easy for us to explain
> * we can live with for the next couple of decades

The original reason we wanted to issue warnings was to allow ourselves
a chance to change the meaning of parameters, add new parameters, or
even remove parameters without causing restore failures. If there are
any ERRORs that might limit our flexibility I think we should downgrade
those to WARNINGs.

Also, out of a sense of paranoia, it might be good to downgrade some
other ERRORs to WARNINGs, like in 0002. I don't think it's quite as
important as you seem to think, however. It doesn't make a lot of
difference unless the user is running restore with --single-transaction
or --exit-on-error, in which case they probably don't want the restore
to continue if something unexpected happens. I'm fine having the
discussion, though, or we can wait until beta to see what kinds of
problems people encounter.

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-25 19:59                           ` Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-25 19:59 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> The original reason we wanted to issue warnings was to allow ourselves
> a chance to change the meaning of parameters, add new parameters, or
> even remove parameters without causing restore failures. If there are
> any ERRORs that might limit our flexibility I think we should downgrade
> those to WARNINGs.
>

+1


> Also, out of a sense of paranoia, it might be good to downgrade some
> other ERRORs to WARNINGs, like in 0002. I don't think it's quite as
> important as you seem to think, however. It doesn't make a lot of
> difference unless the user is running restore with --single-transaction
> or --exit-on-error, in which case they probably don't want the restore
> to continue if something unexpected happens. I'm fine having the
> discussion, though, or we can wait until beta to see what kinds of
> problems people encounter.
>

At this point, I feel I've demonstrated the limit of what can be made into
WARNINGs, giving us a range of options for now and into the beta. I'll
rebase and move the 0002 patch to be in last position so as to tee up
0003-0004 for consideration.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-26 01:41                             ` Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-26 01:41 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> At this point, I feel I've demonstrated the limit of what can be made into
> WARNINGs, giving us a range of options for now and into the beta. I'll
> rebase and move the 0002 patch to be in last position so as to tee up
> 0003-0004 for consideration.
>

And here's the rebase (after bde2fb797aaebcbe06bf60f330ba5a068f17dda7).

The order of the patches is different, but the purpose of each is the same
as before.


Attachments:

  [text/x-patch] v10-0002-Batching-getAttributeStats.patch (21.5K, ../../CADkLM=ftC94muGKH+z1irdBUXO2+tmBMLdqDAAxcct=H+LE1Eg@mail.gmail.com/3-v10-0002-Batching-getAttributeStats.patch)
  download | inline diff:
From 1f9b2578f55fa1233121bcf5949a6f69d6cf8cee Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 03:54:26 -0400
Subject: [PATCH v10 2/4] Batching getAttributeStats().

The prepared statement getAttributeStats() is fairly heavyweight and
could greatly increase pg_dump/pg_upgrade runtime. To alleviate this,
create a result set buffer of all of the attribute stats fetched for a
batch of 100 relations that could potentially have stats.

The query ensures that the order of results exactly matches the needs of
the code walking the TOC to print the stats calls.
---
 src/bin/pg_dump/pg_dump.c | 554 ++++++++++++++++++++++++++------------
 1 file changed, 383 insertions(+), 171 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 224dc8c9330..e3f2dac33ec 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -143,6 +143,25 @@ typedef enum OidOptions
 	zeroAsNone = 4,
 } OidOptions;
 
+typedef enum StatsBufferState
+{
+	STATSBUF_UNINITIALIZED = 0,
+	STATSBUF_ACTIVE,
+	STATSBUF_EXHAUSTED
+}			StatsBufferState;
+
+typedef struct
+{
+	PGresult   *res;			/* results from most recent
+								 * getAttributeStats() */
+	int			idx;			/* first un-consumed row of results */
+	TocEntry   *te;				/* next TOC entry to search for statsitics
+								 * data */
+
+	StatsBufferState state;		/* current state of the buffer */
+}			AttributeStatsBuffer;
+
+
 /* global decls */
 static bool dosync = true;		/* Issue fsync() to make dump durable on disk. */
 
@@ -209,6 +228,18 @@ static int	nbinaryUpgradeClassOids = 0;
 static SequenceItem *sequences = NULL;
 static int	nsequences = 0;
 
+static AttributeStatsBuffer attrstats =
+{
+	NULL, 0, NULL, STATSBUF_UNINITIALIZED
+};
+
+/*
+ * The maximum number of relations that should be fetched in any one
+ * getAttributeStats() call.
+ */
+
+#define MAX_ATTR_STATS_RELS 100
+
 /*
  * The default number of rows per INSERT when
  * --inserts is specified without --rows-per-insert
@@ -222,6 +253,8 @@ static int	nsequences = 0;
  */
 #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
 
+
+
 /*
  * Macro for producing quoted, schema-qualified name of a dumpable object.
  */
@@ -399,6 +432,9 @@ static void setupDumpWorker(Archive *AH);
 static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
 static bool forcePartitionRootLoad(const TableInfo *tbinfo);
 static void read_dump_filters(const char *filename, DumpOptions *dopt);
+static void appendNamedArgument(PQExpBuffer out, Archive *fout,
+								const char *argname, const char *argtype,
+								const char *argval);
 
 
 int
@@ -10520,7 +10556,286 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
 }
 
 /*
- * printDumpRelationStats --
+ * Fetch next batch of rows from getAttributeStats()
+ */
+static void
+fetchNextAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData schemas;
+	PQExpBufferData relations;
+	int			numoids = 0;
+
+	Assert(AH != NULL);
+
+	/* free last result set, if any */
+	if (attrstats.state == STATSBUF_ACTIVE)
+		PQclear(attrstats.res);
+
+	/* If we have looped around to the start of the TOC, restart */
+	if (attrstats.te == AH->toc)
+		attrstats.te = AH->toc->next;
+
+	initPQExpBuffer(&schemas);
+	initPQExpBuffer(&relations);
+
+	/*
+	 * Walk ahead looking for relstats entries that are active in this
+	 * section, adding the names to the schemas and relations lists.
+	 */
+	while ((attrstats.te != AH->toc) && (numoids < MAX_ATTR_STATS_RELS))
+	{
+		if (attrstats.te->reqs != 0 &&
+			strcmp(attrstats.te->desc, "STATISTICS DATA") == 0)
+		{
+			RelStatsInfo *rsinfo = (RelStatsInfo *) attrstats.te->createDumperArg;
+
+			Assert(rsinfo != NULL);
+
+			if (numoids > 0)
+			{
+				appendPQExpBufferStr(&schemas, ",");
+				appendPQExpBufferStr(&relations, ",");
+			}
+			appendPQExpBufferStr(&schemas, fmtId(rsinfo->dobj.namespace->dobj.name));
+			appendPQExpBufferStr(&relations, fmtId(rsinfo->dobj.name));
+			numoids++;
+		}
+
+		attrstats.te = attrstats.te->next;
+	}
+
+	if (numoids > 0)
+	{
+		PQExpBufferData query;
+
+		initPQExpBuffer(&query);
+		appendPQExpBuffer(&query,
+						  "EXECUTE getAttributeStats('{%s}'::pg_catalog.text[],'{%s}'::pg_catalog.text[])",
+						  schemas.data, relations.data);
+		attrstats.res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+		attrstats.idx = 0;
+	}
+	else
+	{
+		attrstats.state = STATSBUF_EXHAUSTED;
+		attrstats.res = NULL;
+		attrstats.idx = -1;
+	}
+
+	termPQExpBuffer(&schemas);
+	termPQExpBuffer(&relations);
+}
+
+/*
+ * Prepare the getAttributeStats() statement
+ *
+ * This is done automatically if the user specified dumpStatistics.
+ */
+static void
+initAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData query;
+
+	Assert(AH != NULL);
+	initPQExpBuffer(&query);
+
+	appendPQExpBufferStr(&query,
+						 "PREPARE getAttributeStats(pg_catalog.text[], pg_catalog.text[]) AS\n"
+						 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
+						 "s.null_frac, s.avg_width, s.n_distinct, s.most_common_vals, "
+						 "s.most_common_freqs, s.histogram_bounds, s.correlation, "
+						 "s.most_common_elems, s.most_common_elem_freqs, "
+						 "s.elem_count_histogram, ");
+
+	if (fout->remoteVersion >= 170000)
+		appendPQExpBufferStr(&query,
+							 "s.range_length_histogram, "
+							 "s.range_empty_frac, "
+							 "s.range_bounds_histogram ");
+	else
+		appendPQExpBufferStr(&query,
+							 "NULL AS range_length_histogram, "
+							 "NULL AS range_empty_frac, "
+							 " NULL AS range_bounds_histogram ");
+
+	/*
+	 * The results must be in the order of relations supplied in the
+	 * parameters to ensure that they are in sync with a walk of the TOC.
+	 *
+	 * The redundant (and incomplete) filter clause on s.tablename = ANY(...)
+	 * is a way to lead the query into using the index
+	 * pg_class_relname_nsp_index which in turn allows the planner to avoid an
+	 * expensive full scan of pg_stats.
+	 *
+	 * We may need to adjust this query for versions that are not so easily
+	 * led.
+	 */
+	appendPQExpBufferStr(&query,
+						 "FROM pg_catalog.pg_stats AS s "
+						 "JOIN unnest($1, $2) WITH ORDINALITY AS u(schemaname, tablename, ord) "
+						 "ON s.schemaname = u.schemaname "
+						 "AND s.tablename = u.tablename "
+						 "WHERE s.tablename = ANY($2) "
+						 "ORDER BY u.ord, s.attname, s.inherited");
+
+	ExecuteSqlStatement(fout, query.data);
+
+	termPQExpBuffer(&query);
+
+	attrstats.te = AH->toc->next;
+
+	fetchNextAttributeStats(fout);
+
+	attrstats.state = STATSBUF_ACTIVE;
+}
+
+
+/*
+ * append a single attribute stat to the buffer for this relation.
+ */
+static void
+appendAttributeStats(Archive *fout, PQExpBuffer out,
+					 const RelStatsInfo *rsinfo)
+{
+	PGresult   *res = attrstats.res;
+	int			tup_num = attrstats.idx;
+
+	const char *attname;
+
+	static bool indexes_set = false;
+	static int	i_attname,
+				i_inherited,
+				i_null_frac,
+				i_avg_width,
+				i_n_distinct,
+				i_most_common_vals,
+				i_most_common_freqs,
+				i_histogram_bounds,
+				i_correlation,
+				i_most_common_elems,
+				i_most_common_elem_freqs,
+				i_elem_count_histogram,
+				i_range_length_histogram,
+				i_range_empty_frac,
+				i_range_bounds_histogram;
+
+	if (!indexes_set)
+	{
+		/*
+		 * It's a prepared statement, so the indexes will be the same for all
+		 * result sets, so we only need to set them once.
+		 */
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		indexes_set = true;
+	}
+
+	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+					  fout->remoteVersion);
+	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, tup_num, i_attname))
+		pg_fatal("attname cannot be NULL");
+	attname = PQgetvalue(res, tup_num, i_attname);
+
+	/*
+	 * Indexes look up attname in indAttNames to derive attnum, all others use
+	 * attname directly.  We must specify attnum for indexes, since their
+	 * attnames are not necessarily stable across dump/reload.
+	 */
+	if (rsinfo->nindAttNames == 0)
+	{
+		appendPQExpBuffer(out, ",\n\t'attname', ");
+		appendStringLiteralAH(out, attname, fout);
+	}
+	else
+	{
+		bool		found = false;
+
+		for (int i = 0; i < rsinfo->nindAttNames; i++)
+			if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
+			{
+				appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+								  i + 1);
+				found = true;
+				break;
+			}
+
+		if (!found)
+			pg_fatal("could not find index attname \"%s\"", attname);
+	}
+
+	if (!PQgetisnull(res, tup_num, i_inherited))
+		appendNamedArgument(out, fout, "inherited", "boolean",
+							PQgetvalue(res, tup_num, i_inherited));
+	if (!PQgetisnull(res, tup_num, i_null_frac))
+		appendNamedArgument(out, fout, "null_frac", "real",
+							PQgetvalue(res, tup_num, i_null_frac));
+	if (!PQgetisnull(res, tup_num, i_avg_width))
+		appendNamedArgument(out, fout, "avg_width", "integer",
+							PQgetvalue(res, tup_num, i_avg_width));
+	if (!PQgetisnull(res, tup_num, i_n_distinct))
+		appendNamedArgument(out, fout, "n_distinct", "real",
+							PQgetvalue(res, tup_num, i_n_distinct));
+	if (!PQgetisnull(res, tup_num, i_most_common_vals))
+		appendNamedArgument(out, fout, "most_common_vals", "text",
+							PQgetvalue(res, tup_num, i_most_common_vals));
+	if (!PQgetisnull(res, tup_num, i_most_common_freqs))
+		appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_freqs));
+	if (!PQgetisnull(res, tup_num, i_histogram_bounds))
+		appendNamedArgument(out, fout, "histogram_bounds", "text",
+							PQgetvalue(res, tup_num, i_histogram_bounds));
+	if (!PQgetisnull(res, tup_num, i_correlation))
+		appendNamedArgument(out, fout, "correlation", "real",
+							PQgetvalue(res, tup_num, i_correlation));
+	if (!PQgetisnull(res, tup_num, i_most_common_elems))
+		appendNamedArgument(out, fout, "most_common_elems", "text",
+							PQgetvalue(res, tup_num, i_most_common_elems));
+	if (!PQgetisnull(res, tup_num, i_most_common_elem_freqs))
+		appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_elem_freqs));
+	if (!PQgetisnull(res, tup_num, i_elem_count_histogram))
+		appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+							PQgetvalue(res, tup_num, i_elem_count_histogram));
+	if (fout->remoteVersion >= 170000)
+	{
+		if (!PQgetisnull(res, tup_num, i_range_length_histogram))
+			appendNamedArgument(out, fout, "range_length_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_length_histogram));
+		if (!PQgetisnull(res, tup_num, i_range_empty_frac))
+			appendNamedArgument(out, fout, "range_empty_frac", "real",
+								PQgetvalue(res, tup_num, i_range_empty_frac));
+		if (!PQgetisnull(res, tup_num, i_range_bounds_histogram))
+			appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_bounds_histogram));
+	}
+	appendPQExpBufferStr(out, "\n);\n");
+}
+
+
+
+/*
+ * printRelationStats --
  *
  * Generate the SQL statements needed to restore a relation's statistics.
  */
@@ -10528,64 +10843,21 @@ static char *
 printRelationStats(Archive *fout, const void *userArg)
 {
 	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
-	const DumpableObject *dobj = &rsinfo->dobj;
+	const DumpableObject *dobj;
+	const char *relschema;
+	const char *relname;
+
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
 
-	PQExpBufferData query;
 	PQExpBufferData out;
 
-	PGresult   *res;
-
-	static bool first_query = true;
-	static int	i_attname;
-	static int	i_inherited;
-	static int	i_null_frac;
-	static int	i_avg_width;
-	static int	i_n_distinct;
-	static int	i_most_common_vals;
-	static int	i_most_common_freqs;
-	static int	i_histogram_bounds;
-	static int	i_correlation;
-	static int	i_most_common_elems;
-	static int	i_most_common_elem_freqs;
-	static int	i_elem_count_histogram;
-	static int	i_range_length_histogram;
-	static int	i_range_empty_frac;
-	static int	i_range_bounds_histogram;
-
-	initPQExpBuffer(&query);
-
-	if (first_query)
-	{
-		appendPQExpBufferStr(&query,
-							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
-							 "SELECT s.attname, s.inherited, "
-							 "s.null_frac, s.avg_width, s.n_distinct, "
-							 "s.most_common_vals, s.most_common_freqs, "
-							 "s.histogram_bounds, s.correlation, "
-							 "s.most_common_elems, s.most_common_elem_freqs, "
-							 "s.elem_count_histogram, ");
-
-		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(&query,
-								 "s.range_length_histogram, "
-								 "s.range_empty_frac, "
-								 "s.range_bounds_histogram ");
-		else
-			appendPQExpBufferStr(&query,
-								 "NULL AS range_length_histogram,"
-								 "NULL AS range_empty_frac,"
-								 "NULL AS range_bounds_histogram ");
-
-		appendPQExpBufferStr(&query,
-							 "FROM pg_catalog.pg_stats s "
-							 "WHERE s.schemaname = $1 "
-							 "AND s.tablename = $2 "
-							 "ORDER BY s.attname, s.inherited");
-
-		ExecuteSqlStatement(fout, query.data);
-
-		resetPQExpBuffer(&query);
-	}
+	Assert(rsinfo != NULL);
+	dobj = &rsinfo->dobj;
+	Assert(dobj != NULL);
+	relschema = dobj->namespace->dobj.name;
+	Assert(relschema != NULL);
+	relname = dobj->name;
+	Assert(relname != NULL);
 
 	initPQExpBuffer(&out);
 
@@ -10604,132 +10876,72 @@ printRelationStats(Archive *fout, const void *userArg)
 	appendPQExpBuffer(&out, "\t'relallvisible', '%d'::integer\n);\n",
 					  rsinfo->relallvisible);
 
-	/* fetch attribute stats */
-	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(&query, ", ");
-	appendStringLiteralAH(&query, dobj->name, fout);
-	appendPQExpBufferStr(&query, ")");
+	AH->txnCount++;
 
-	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+	if (attrstats.state == STATSBUF_UNINITIALIZED)
+		initAttributeStats(fout);
 
-	if (first_query)
+	/*
+	 * Because the query returns rows in the same order as the relations
+	 * requested, and because every relation gets at least one row in the
+	 * result set, the first row for this relation must correspond either to
+	 * the current row of this result set (if one exists) or the first row of
+	 * the next result set (if this one is already consumed).
+	 */
+	if (attrstats.state != STATSBUF_ACTIVE)
+		pg_fatal("Exhausted getAttributeStats() before processing %s.%s",
+				 rsinfo->dobj.namespace->dobj.name,
+				 rsinfo->dobj.name);
+
+	/*
+	 * If the current result set has been fully consumed, then the row(s) we
+	 * need (if any) would be found in the next one. This will update
+	 * attrstats.res and attrstats.idx.
+	 */
+	if (PQntuples(attrstats.res) <= attrstats.idx)
+		fetchNextAttributeStats(fout);
+
+	while (true)
 	{
-		i_attname = PQfnumber(res, "attname");
-		i_inherited = PQfnumber(res, "inherited");
-		i_null_frac = PQfnumber(res, "null_frac");
-		i_avg_width = PQfnumber(res, "avg_width");
-		i_n_distinct = PQfnumber(res, "n_distinct");
-		i_most_common_vals = PQfnumber(res, "most_common_vals");
-		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-		i_correlation = PQfnumber(res, "correlation");
-		i_most_common_elems = PQfnumber(res, "most_common_elems");
-		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
-		first_query = false;
-	}
-
-	/* restore attribute stats */
-	for (int rownum = 0; rownum < PQntuples(res); rownum++)
-	{
-		const char *attname;
-
-		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
-						  fout->remoteVersion);
-		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");
-		attname = PQgetvalue(res, rownum, i_attname);
+		int			i_schemaname;
+		int			i_tablename;
+		char	   *schemaname;
+		char	   *tablename;	/* misnomer, following pg_stats naming */
 
 		/*
-		 * Indexes look up attname in indAttNames to derive attnum, all others
-		 * use attname directly.  We must specify attnum for indexes, since
-		 * their attnames are not necessarily stable across dump/reload.
+		 * If we hit the end of the result set, then there are no more records
+		 * for this relation, so we should stop, but first get the next result
+		 * set for the next batch of relations.
 		 */
-		if (rsinfo->nindAttNames == 0)
+		if (PQntuples(attrstats.res) <= attrstats.idx)
 		{
-			appendPQExpBuffer(&out, ",\n\t'attname', ");
-			appendStringLiteralAH(&out, attname, fout);
-		}
-		else
-		{
-			bool		found = false;
-
-			for (int i = 0; i < rsinfo->nindAttNames; i++)
-			{
-				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
-				{
-					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
-									  i + 1);
-					found = true;
-					break;
-				}
-			}
-
-			if (!found)
-				pg_fatal("could not find index attname \"%s\"", attname);
+			fetchNextAttributeStats(fout);
+			break;
 		}
 
-		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(&out, fout, "inherited", "boolean",
-								PQgetvalue(res, rownum, i_inherited));
-		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(&out, fout, "null_frac", "real",
-								PQgetvalue(res, rownum, i_null_frac));
-		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(&out, fout, "avg_width", "integer",
-								PQgetvalue(res, rownum, i_avg_width));
-		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(&out, fout, "n_distinct", "real",
-								PQgetvalue(res, rownum, i_n_distinct));
-		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(&out, fout, "most_common_vals", "text",
-								PQgetvalue(res, rownum, i_most_common_vals));
-		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_freqs));
-		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(&out, fout, "histogram_bounds", "text",
-								PQgetvalue(res, rownum, i_histogram_bounds));
-		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(&out, fout, "correlation", "real",
-								PQgetvalue(res, rownum, i_correlation));
-		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(&out, fout, "most_common_elems", "text",
-								PQgetvalue(res, rownum, i_most_common_elems));
-		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_elem_freqs));
-		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
-								PQgetvalue(res, rownum, i_elem_count_histogram));
-		if (fout->remoteVersion >= 170000)
-		{
-			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(&out, fout, "range_length_histogram", "text",
-									PQgetvalue(res, rownum, i_range_length_histogram));
-			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(&out, fout, "range_empty_frac", "real",
-									PQgetvalue(res, rownum, i_range_empty_frac));
-			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
-									PQgetvalue(res, rownum, i_range_bounds_histogram));
-		}
-		appendPQExpBufferStr(&out, "\n);\n");
+		i_schemaname = PQfnumber(attrstats.res, "schemaname");
+		Assert(i_schemaname >= 0);
+		i_tablename = PQfnumber(attrstats.res, "tablename");
+		Assert(i_tablename >= 0);
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_schemaname))
+			pg_fatal("getAttributeStats() schemaname cannot be NULL");
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_tablename))
+			pg_fatal("getAttributeStats() tablename cannot be NULL");
+
+		schemaname = PQgetvalue(attrstats.res, attrstats.idx, i_schemaname);
+		tablename = PQgetvalue(attrstats.res, attrstats.idx, i_tablename);
+
+		/* stop if current stat row isn't for this relation */
+		if (strcmp(relname, tablename) != 0 || strcmp(relschema, schemaname) != 0)
+			break;
+
+		appendAttributeStats(fout, &out, rsinfo);
+		AH->txnCount++;
+		attrstats.idx++;
 	}
 
-	PQclear(res);
-
-	termPQExpBuffer(&query);
 	return out.data;
 }
 
-- 
2.49.0



  [text/x-patch] v10-0004-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch (30.4K, ../../CADkLM=ftC94muGKH+z1irdBUXO2+tmBMLdqDAAxcct=H+LE1Eg@mail.gmail.com/4-v10-0004-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch)
  download | inline diff:
From 651e70ae705d5a4f081509e66a743422d2e86ae4 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 8 Mar 2025 00:52:41 -0500
Subject: [PATCH v10 4/4] 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



  [text/x-patch] v10-0001-Introduce-CreateStmtPtr.patch (17.2K, ../../CADkLM=ftC94muGKH+z1irdBUXO2+tmBMLdqDAAxcct=H+LE1Eg@mail.gmail.com/5-v10-0001-Introduce-CreateStmtPtr.patch)
  download | inline diff:
From 8611beb5a7906d0f7e93fb68aa41dd58bc7ab80f Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 01:06:19 -0400
Subject: [PATCH v10 1/4] Introduce CreateStmtPtr.

CreateStmtPtr is a function pointer that can replace the createStmt/defn
parameter. This is useful in situations where the amount of text
generated for a definition is so large that it is undesirable to hold
many such objects in memory at the same time.

Using functions of this type, the text created is then immediately
written out to the appropriate file for the given dump format.
---
 src/bin/pg_dump/pg_backup.h          |   2 +
 src/bin/pg_dump/pg_backup_archiver.c |  22 ++-
 src/bin/pg_dump/pg_backup_archiver.h |   7 +
 src/bin/pg_dump/pg_dump.c            | 230 +++++++++++++++------------
 4 files changed, 156 insertions(+), 105 deletions(-)

diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 658986de6f8..fdcccd64a70 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -289,6 +289,8 @@ typedef int (*DataDumperPtr) (Archive *AH, const void *userArg);
 
 typedef void (*SetupWorkerPtrType) (Archive *AH);
 
+typedef char *(*CreateStmtPtr) (Archive *AH, const void *userArg);
+
 /*
  * Main archiver interface.
  */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 82d51c89ac6..e512201ed58 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1264,6 +1264,9 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
 	newToc->dataDumper = opts->dumpFn;
 	newToc->dataDumperArg = opts->dumpArg;
 	newToc->hadDumper = opts->dumpFn ? true : false;
+	newToc->createDumper = opts->createFn;
+	newToc->createDumperArg = opts->createArg;
+	newToc->hadCreateDumper = opts->createFn ? true : false;
 
 	newToc->formatData = NULL;
 	newToc->dataLength = 0;
@@ -2620,7 +2623,17 @@ WriteToc(ArchiveHandle *AH)
 		WriteStr(AH, te->tag);
 		WriteStr(AH, te->desc);
 		WriteInt(AH, te->section);
-		WriteStr(AH, te->defn);
+
+		if (te->hadCreateDumper)
+		{
+			char	   *defn = te->createDumper((Archive *) AH, te->createDumperArg);
+
+			WriteStr(AH, defn);
+			pg_free(defn);
+		}
+		else
+			WriteStr(AH, te->defn);
+
 		WriteStr(AH, te->dropStmt);
 		WriteStr(AH, te->copyStmt);
 		WriteStr(AH, te->namespace);
@@ -3856,6 +3869,13 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
 	{
 		IssueACLPerBlob(AH, te);
 	}
+	else if (te->hadCreateDumper)
+	{
+		char	   *ptr = te->createDumper((Archive *) AH, te->createDumperArg);
+
+		ahwrite(ptr, 1, strlen(ptr), AH);
+		pg_free(ptr);
+	}
 	else if (te->defn && strlen(te->defn) > 0)
 	{
 		ahprintf(AH, "%s\n\n", te->defn);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a2064f471ed..e68db633995 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -368,6 +368,11 @@ struct _tocEntry
 	const void *dataDumperArg;	/* Arg for above routine */
 	void	   *formatData;		/* TOC Entry data specific to file format */
 
+	CreateStmtPtr createDumper; /* Routine for create statement creation */
+	const void *createDumperArg;	/* arg for the above routine */
+	bool		hadCreateDumper;	/* Archiver was passed a create statement
+									 * routine */
+
 	/* working state while dumping/restoring */
 	pgoff_t		dataLength;		/* item's data size; 0 if none or unknown */
 	int			reqs;			/* do we need schema and/or data of object
@@ -407,6 +412,8 @@ typedef struct _archiveOpts
 	int			nDeps;
 	DataDumperPtr dumpFn;
 	const void *dumpArg;
+	CreateStmtPtr createFn;
+	const void *createArg;
 } ArchiveOpts;
 #define ARCHIVE_OPTS(...) &(ArchiveOpts){__VA_ARGS__}
 /* Called to add a TOC entry */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e41e645f649..224dc8c9330 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10520,51 +10520,44 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
 }
 
 /*
- * dumpRelationStats --
+ * printDumpRelationStats --
  *
- * Dump command to import stats into the relation on the new database.
+ * Generate the SQL statements needed to restore a relation's statistics.
  */
-static void
-dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+static char *
+printRelationStats(Archive *fout, const void *userArg)
 {
+	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
 	const DumpableObject *dobj = &rsinfo->dobj;
+
+	PQExpBufferData query;
+	PQExpBufferData out;
+
 	PGresult   *res;
-	PQExpBuffer query;
-	PQExpBuffer out;
-	DumpId	   *deps = NULL;
-	int			ndeps = 0;
-	int			i_attname;
-	int			i_inherited;
-	int			i_null_frac;
-	int			i_avg_width;
-	int			i_n_distinct;
-	int			i_most_common_vals;
-	int			i_most_common_freqs;
-	int			i_histogram_bounds;
-	int			i_correlation;
-	int			i_most_common_elems;
-	int			i_most_common_elem_freqs;
-	int			i_elem_count_histogram;
-	int			i_range_length_histogram;
-	int			i_range_empty_frac;
-	int			i_range_bounds_histogram;
 
-	/* nothing to do if we are not dumping statistics */
-	if (!fout->dopt->dumpStatistics)
-		return;
+	static bool first_query = true;
+	static int	i_attname;
+	static int	i_inherited;
+	static int	i_null_frac;
+	static int	i_avg_width;
+	static int	i_n_distinct;
+	static int	i_most_common_vals;
+	static int	i_most_common_freqs;
+	static int	i_histogram_bounds;
+	static int	i_correlation;
+	static int	i_most_common_elems;
+	static int	i_most_common_elem_freqs;
+	static int	i_elem_count_histogram;
+	static int	i_range_length_histogram;
+	static int	i_range_empty_frac;
+	static int	i_range_bounds_histogram;
 
-	/* dependent on the relation definition, if doing schema */
-	if (fout->dopt->dumpSchema)
+	initPQExpBuffer(&query);
+
+	if (first_query)
 	{
-		deps = dobj->dependencies;
-		ndeps = dobj->nDeps;
-	}
-
-	query = createPQExpBuffer();
-	if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
-	{
-		appendPQExpBufferStr(query,
-							 "PREPARE getAttributeStats(pg_catalog.name, pg_catalog.name) AS\n"
+		appendPQExpBufferStr(&query,
+							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
 							 "SELECT s.attname, s.inherited, "
 							 "s.null_frac, s.avg_width, s.n_distinct, "
 							 "s.most_common_vals, s.most_common_freqs, "
@@ -10573,82 +10566,85 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 							 "s.elem_count_histogram, ");
 
 		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "s.range_length_histogram, "
 								 "s.range_empty_frac, "
 								 "s.range_bounds_histogram ");
 		else
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "NULL AS range_length_histogram,"
 								 "NULL AS range_empty_frac,"
 								 "NULL AS range_bounds_histogram ");
 
-		appendPQExpBufferStr(query,
+		appendPQExpBufferStr(&query,
 							 "FROM pg_catalog.pg_stats s "
 							 "WHERE s.schemaname = $1 "
 							 "AND s.tablename = $2 "
 							 "ORDER BY s.attname, s.inherited");
 
-		ExecuteSqlStatement(fout, query->data);
+		ExecuteSqlStatement(fout, query.data);
 
-		fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
-		resetPQExpBuffer(query);
+		resetPQExpBuffer(&query);
 	}
 
-	out = createPQExpBuffer();
+	initPQExpBuffer(&out);
 
 	/* restore relation stats */
-	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
-	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
+	appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 					  fout->remoteVersion);
-	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",
+	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",
 					  rsinfo->relallvisible);
 
 	/* fetch attribute stats */
-	appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(query, ", ");
-	appendStringLiteralAH(query, dobj->name, fout);
-	appendPQExpBufferStr(query, ");");
+	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
+	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
+	appendPQExpBufferStr(&query, ", ");
+	appendStringLiteralAH(&query, dobj->name, fout);
+	appendPQExpBufferStr(&query, ")");
 
-	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
 
-	i_attname = PQfnumber(res, "attname");
-	i_inherited = PQfnumber(res, "inherited");
-	i_null_frac = PQfnumber(res, "null_frac");
-	i_avg_width = PQfnumber(res, "avg_width");
-	i_n_distinct = PQfnumber(res, "n_distinct");
-	i_most_common_vals = PQfnumber(res, "most_common_vals");
-	i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-	i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-	i_correlation = PQfnumber(res, "correlation");
-	i_most_common_elems = PQfnumber(res, "most_common_elems");
-	i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-	i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-	i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-	i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-	i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+	if (first_query)
+	{
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		first_query = false;
+	}
 
 	/* restore attribute stats */
 	for (int rownum = 0; rownum < PQntuples(res); rownum++)
 	{
 		const char *attname;
 
-		appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 						  fout->remoteVersion);
-		appendPQExpBufferStr(out, "\t'schemaname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
-		appendPQExpBufferStr(out, ",\n\t'relname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.name, fout);
+		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");
@@ -10661,8 +10657,8 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		 */
 		if (rsinfo->nindAttNames == 0)
 		{
-			appendPQExpBuffer(out, ",\n\t'attname', ");
-			appendStringLiteralAH(out, attname, fout);
+			appendPQExpBuffer(&out, ",\n\t'attname', ");
+			appendStringLiteralAH(&out, attname, fout);
 		}
 		else
 		{
@@ -10672,7 +10668,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 			{
 				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
 				{
-					appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
 									  i + 1);
 					found = true;
 					break;
@@ -10684,67 +10680,93 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		}
 
 		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(out, fout, "inherited", "boolean",
+			appendNamedArgument(&out, fout, "inherited", "boolean",
 								PQgetvalue(res, rownum, i_inherited));
 		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(out, fout, "null_frac", "real",
+			appendNamedArgument(&out, fout, "null_frac", "real",
 								PQgetvalue(res, rownum, i_null_frac));
 		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(out, fout, "avg_width", "integer",
+			appendNamedArgument(&out, fout, "avg_width", "integer",
 								PQgetvalue(res, rownum, i_avg_width));
 		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(out, fout, "n_distinct", "real",
+			appendNamedArgument(&out, fout, "n_distinct", "real",
 								PQgetvalue(res, rownum, i_n_distinct));
 		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(out, fout, "most_common_vals", "text",
+			appendNamedArgument(&out, fout, "most_common_vals", "text",
 								PQgetvalue(res, rownum, i_most_common_vals));
 		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_freqs));
 		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(out, fout, "histogram_bounds", "text",
+			appendNamedArgument(&out, fout, "histogram_bounds", "text",
 								PQgetvalue(res, rownum, i_histogram_bounds));
 		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(out, fout, "correlation", "real",
+			appendNamedArgument(&out, fout, "correlation", "real",
 								PQgetvalue(res, rownum, i_correlation));
 		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(out, fout, "most_common_elems", "text",
+			appendNamedArgument(&out, fout, "most_common_elems", "text",
 								PQgetvalue(res, rownum, i_most_common_elems));
 		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_elem_freqs));
 		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
 								PQgetvalue(res, rownum, i_elem_count_histogram));
 		if (fout->remoteVersion >= 170000)
 		{
 			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(out, fout, "range_length_histogram", "text",
+				appendNamedArgument(&out, fout, "range_length_histogram", "text",
 									PQgetvalue(res, rownum, i_range_length_histogram));
 			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(out, fout, "range_empty_frac", "real",
+				appendNamedArgument(&out, fout, "range_empty_frac", "real",
 									PQgetvalue(res, rownum, i_range_empty_frac));
 			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
 									PQgetvalue(res, rownum, i_range_bounds_histogram));
 		}
-		appendPQExpBufferStr(out, "\n);\n");
+		appendPQExpBufferStr(&out, "\n);\n");
 	}
 
 	PQclear(res);
 
+	termPQExpBuffer(&query);
+	return out.data;
+}
+
+/*
+ * dumpRelationStats --
+ *
+ * Dump command to import stats into the relation on the new database.
+ */
+static void
+dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+{
+	const DumpableObject *dobj = &rsinfo->dobj;
+
+	DumpId	   *deps = NULL;
+	int			ndeps = 0;
+
+	/* nothing to do if we are not dumping statistics */
+	if (!fout->dopt->dumpStatistics)
+		return;
+
+	/* dependent on the relation definition, if doing schema */
+	if (fout->dopt->dumpSchema)
+	{
+		deps = dobj->dependencies;
+		ndeps = dobj->nDeps;
+	}
+
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 ARCHIVE_OPTS(.tag = dobj->name,
 							  .namespace = dobj->namespace->dobj.name,
 							  .description = "STATISTICS DATA",
 							  .section = rsinfo->postponed_def ?
 							  SECTION_POST_DATA : statisticsDumpSection(rsinfo),
-							  .createStmt = out->data,
+							  .createFn = printRelationStats,
+							  .createArg = rsinfo,
 							  .deps = deps,
 							  .nDeps = ndeps));
-
-	destroyPQExpBuffer(out);
-	destroyPQExpBuffer(query);
 }
 
 /*

base-commit: bde2fb797aaebcbe06bf60f330ba5a068f17dda7
-- 
2.49.0



  [text/x-patch] v10-0003-Add-relallfrozen-to-pg_dump-statistics.patch (7.9K, ../../CADkLM=ftC94muGKH+z1irdBUXO2+tmBMLdqDAAxcct=H+LE1Eg@mail.gmail.com/6-v10-0003-Add-relallfrozen-to-pg_dump-statistics.patch)
  download | inline diff:
From 87da7d4c517c3b2e63666892e64b9de2a8dbbe44 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 15 Mar 2025 17:34:30 -0400
Subject: [PATCH v10 3/4] Add relallfrozen to pg_dump statistics.

The column relallfrozen was recently added to pg_class and it also
represent statistics, so we should add it to the dump/restore/upgrade
operations.

Dumps of databases prior to v18 will not attempt to restore any value to
relallfrozen, allowing pg_restore_relation_stats() to set the default it
deems appropriate.
---
 src/bin/pg_dump/pg_dump.c        | 52 ++++++++++++++++++++++----------
 src/bin/pg_dump/pg_dump.h        |  1 +
 src/bin/pg_dump/t/002_pg_dump.pl |  3 +-
 3 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e3f2dac33ec..6c366fd55d3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6897,7 +6897,8 @@ getFuncs(Archive *fout)
  */
 static RelStatsInfo *
 getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
-					  char *reltuples, int32 relallvisible, char relkind,
+					  char *reltuples, int32 relallvisible,
+					  int32 relallfrozen, char relkind,
 					  char **indAttNames, int nindAttNames)
 {
 	if (!fout->dopt->dumpStatistics)
@@ -6926,6 +6927,7 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
 		info->relpages = relpages;
 		info->reltuples = pstrdup(reltuples);
 		info->relallvisible = relallvisible;
+		info->relallfrozen = relallfrozen;
 		info->relkind = relkind;
 		info->indAttNames = indAttNames;
 		info->nindAttNames = nindAttNames;
@@ -6965,6 +6967,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relpages;
 	int			i_reltuples;
 	int			i_relallvisible;
+	int			i_relallfrozen;
 	int			i_toastpages;
 	int			i_owning_tab;
 	int			i_owning_col;
@@ -7015,8 +7018,13 @@ getTables(Archive *fout, int *numTables)
 						 "c.relowner, "
 						 "c.relchecks, "
 						 "c.relhasindex, c.relhasrules, c.relpages, "
-						 "c.reltuples, c.relallvisible, c.relhastriggers, "
-						 "c.relpersistence, "
+						 "c.reltuples, c.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "c.relallfrozen, ");
+
+	appendPQExpBufferStr(query,
+						 "c.relhastriggers, c.relpersistence, "
 						 "c.reloftype, "
 						 "c.relacl, "
 						 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
@@ -7181,6 +7189,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_toastpages = PQfnumber(res, "toastpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
@@ -7228,6 +7237,7 @@ getTables(Archive *fout, int *numTables)
 	for (i = 0; i < ntups; i++)
 	{
 		int32		relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
+		int32		relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
 
 		tblinfo[i].dobj.objType = DO_TABLE;
 		tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
@@ -7330,7 +7340,7 @@ getTables(Archive *fout, int *numTables)
 		if (tblinfo[i].interesting)
 			getRelationStatistics(fout, &tblinfo[i].dobj, tblinfo[i].relpages,
 								  PQgetvalue(res, i, i_reltuples),
-								  relallvisible, tblinfo[i].relkind, NULL, 0);
+								  relallvisible, relallfrozen, tblinfo[i].relkind, NULL, 0);
 
 		/*
 		 * Read-lock target tables to make sure they aren't DROPPED or altered
@@ -7599,6 +7609,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_relpages,
 				i_reltuples,
 				i_relallvisible,
+				i_relallfrozen,
 				i_parentidx,
 				i_indexdef,
 				i_indnkeyatts,
@@ -7653,7 +7664,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	appendPQExpBufferStr(query,
 						 "SELECT t.tableoid, t.oid, i.indrelid, "
 						 "t.relname AS indexname, "
-						 "t.relpages, t.reltuples, t.relallvisible, "
+						 "t.relpages, t.reltuples, t.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "t.relallfrozen, ");
+
+	appendPQExpBufferStr(query,
 						 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
 						 "i.indkey, i.indisclustered, "
 						 "c.contype, c.conname, "
@@ -7769,6 +7785,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_parentidx = PQfnumber(res, "parentidx");
 	i_indexdef = PQfnumber(res, "indexdef");
 	i_indnkeyatts = PQfnumber(res, "indnkeyatts");
@@ -7840,6 +7857,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 			RelStatsInfo *relstats;
 			int32		relpages = atoi(PQgetvalue(res, j, i_relpages));
 			int32		relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
+			int32		relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
 
 			indxinfo[j].dobj.objType = DO_INDEX;
 			indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
@@ -7882,7 +7900,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 
 			relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
 											 PQgetvalue(res, j, i_reltuples),
-											 relallvisible, indexkind,
+											 relallvisible, relallfrozen, indexkind,
 											 indAttNames, nindAttNames);
 
 			contype = *(PQgetvalue(res, j, i_contype));
@@ -10862,19 +10880,21 @@ printRelationStats(Archive *fout, const void *userArg)
 	initPQExpBuffer(&out);
 
 	/* restore relation stats */
-	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
-	appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
+	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(");
+	appendPQExpBuffer(&out, "\n\t'version', '%u'::integer",
 					  fout->remoteVersion);
-	appendPQExpBufferStr(&out, "\t'schemaname', ");
+	appendPQExpBufferStr(&out, ",\n\t'schemaname', ");
 	appendStringLiteralAH(&out, rsinfo->dobj.namespace->dobj.name, fout);
-	appendPQExpBufferStr(&out, ",\n");
-	appendPQExpBufferStr(&out, "\t'relname', ");
+	appendPQExpBufferStr(&out, ",\n\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",
-					  rsinfo->relallvisible);
+	appendPQExpBuffer(&out, ",\n\t'relpages', '%d'::integer", rsinfo->relpages);
+	appendPQExpBuffer(&out, ",\n\t'reltuples', '%s'::real", rsinfo->reltuples);
+	appendPQExpBuffer(&out, ",\n\t'relallvisible', '%d'::integer", rsinfo->relallvisible);
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBuffer(&out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
+
+	appendPQExpBufferStr(&out, "\n);\n");
 
 	AH->txnCount++;
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index bbdb30b5f54..82f1eb3c4b7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -441,6 +441,7 @@ typedef struct _relStatsInfo
 	int32		relpages;
 	char	   *reltuples;
 	int32		relallvisible;
+	int32		relallfrozen;
 	char		relkind;		/* 'r', 'm', 'i', etc */
 
 	/*
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 51ebf8ad13c..576326daec7 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4771,7 +4771,8 @@ my %tests = (
 			'relname',\s'dup_test_post_data_ix',\s+
 			'relpages',\s'\d+'::integer,\s+
 			'reltuples',\s'\d+'::real,\s+
-			'relallvisible',\s'\d+'::integer\s+
+			'relallvisible',\s'\d+'::integer,\s+
+			'relallfrozen',\s'\d+'::integer\s+
 			\);\s+
 			\QSELECT * FROM pg_catalog.pg_restore_attribute_stats(\E\s+
 			'version',\s'\d+'::integer,\s+
-- 
2.49.0



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-29 01:11                               ` Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-29 01:11 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

A rebase and a reordering of the commits to put the really-really-must-have
relallfrozen ahead of the really-must-have stats batching and both of them
head of the error->warning step-downs.


Attachments:

  [text/x-patch] v11-0001-Add-relallfrozen-to-pg_dump-statistics.patch (7.2K, ../../CADkLM=desCuf3dVHasADvdUVRmb-5gO0mhMO5u9nzgv6i7U86Q@mail.gmail.com/3-v11-0001-Add-relallfrozen-to-pg_dump-statistics.patch)
  download | inline diff:
From 96b10b1eb955c5619d23cadf7de8b12d2db638a9 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 15 Mar 2025 17:34:30 -0400
Subject: [PATCH v11 1/4] Add relallfrozen to pg_dump statistics.

The column relallfrozen was recently added to pg_class and it also
represent statistics, so we should add it to the dump/restore/upgrade
operations.

Dumps of databases prior to v18 will not attempt to restore any value to
relallfrozen, allowing pg_restore_relation_stats() to set the default it
deems appropriate.
---
 src/bin/pg_dump/pg_dump.c        | 38 ++++++++++++++++++++++++++------
 src/bin/pg_dump/pg_dump.h        |  1 +
 src/bin/pg_dump/t/002_pg_dump.pl |  3 ++-
 3 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 84a78625820..211cf10dbd6 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6874,7 +6874,8 @@ getFuncs(Archive *fout)
  */
 static RelStatsInfo *
 getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
-					  char *reltuples, int32 relallvisible, char relkind,
+					  char *reltuples, int32 relallvisible,
+					  int32 relallfrozen, char relkind,
 					  char **indAttNames, int nindAttNames)
 {
 	if (!fout->dopt->dumpStatistics)
@@ -6903,6 +6904,7 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
 		info->relpages = relpages;
 		info->reltuples = pstrdup(reltuples);
 		info->relallvisible = relallvisible;
+		info->relallfrozen = relallfrozen;
 		info->relkind = relkind;
 		info->indAttNames = indAttNames;
 		info->nindAttNames = nindAttNames;
@@ -6967,6 +6969,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relpages;
 	int			i_reltuples;
 	int			i_relallvisible;
+	int			i_relallfrozen;
 	int			i_toastpages;
 	int			i_owning_tab;
 	int			i_owning_col;
@@ -7017,8 +7020,13 @@ getTables(Archive *fout, int *numTables)
 						 "c.relowner, "
 						 "c.relchecks, "
 						 "c.relhasindex, c.relhasrules, c.relpages, "
-						 "c.reltuples, c.relallvisible, c.relhastriggers, "
-						 "c.relpersistence, "
+						 "c.reltuples, c.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "c.relallfrozen, ");
+
+	appendPQExpBufferStr(query,
+						 "c.relhastriggers, c.relpersistence, "
 						 "c.reloftype, "
 						 "c.relacl, "
 						 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
@@ -7183,6 +7191,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_toastpages = PQfnumber(res, "toastpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
@@ -7230,6 +7239,7 @@ getTables(Archive *fout, int *numTables)
 	for (i = 0; i < ntups; i++)
 	{
 		int32		relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
+		int32		relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
 
 		tblinfo[i].dobj.objType = DO_TABLE;
 		tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
@@ -7336,7 +7346,7 @@ getTables(Archive *fout, int *numTables)
 			stats = getRelationStatistics(fout, &tblinfo[i].dobj,
 										  tblinfo[i].relpages,
 										  PQgetvalue(res, i, i_reltuples),
-										  relallvisible,
+										  relallvisible, relallfrozen,
 										  tblinfo[i].relkind, NULL, 0);
 			if (tblinfo[i].relkind == RELKIND_MATVIEW)
 				tblinfo[i].stats = stats;
@@ -7609,6 +7619,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_relpages,
 				i_reltuples,
 				i_relallvisible,
+				i_relallfrozen,
 				i_parentidx,
 				i_indexdef,
 				i_indnkeyatts,
@@ -7663,7 +7674,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	appendPQExpBufferStr(query,
 						 "SELECT t.tableoid, t.oid, i.indrelid, "
 						 "t.relname AS indexname, "
-						 "t.relpages, t.reltuples, t.relallvisible, "
+						 "t.relpages, t.reltuples, t.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "t.relallfrozen, ");
+
+	appendPQExpBufferStr(query,
 						 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
 						 "i.indkey, i.indisclustered, "
 						 "c.contype, c.conname, "
@@ -7779,6 +7795,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_parentidx = PQfnumber(res, "parentidx");
 	i_indexdef = PQfnumber(res, "indexdef");
 	i_indnkeyatts = PQfnumber(res, "indnkeyatts");
@@ -7850,6 +7867,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 			RelStatsInfo *relstats;
 			int32		relpages = atoi(PQgetvalue(res, j, i_relpages));
 			int32		relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
+			int32		relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
 
 			indxinfo[j].dobj.objType = DO_INDEX;
 			indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
@@ -7892,7 +7910,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 
 			relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
 											 PQgetvalue(res, j, i_reltuples),
-											 relallvisible, indexkind,
+											 relallvisible, relallfrozen, indexkind,
 											 indAttNames, nindAttNames);
 
 			contype = *(PQgetvalue(res, j, i_contype));
@@ -10618,9 +10636,15 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 	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",
+	appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
 					  rsinfo->relallvisible);
 
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
+
+	appendPQExpBufferStr(out, "\n);\n");
+
+
 	/* fetch attribute stats */
 	appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
 	appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 70f7a369e4a..e6f0f86a459 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -442,6 +442,7 @@ typedef struct _relStatsInfo
 	int32		relpages;
 	char	   *reltuples;
 	int32		relallvisible;
+	int32		relallfrozen;
 	char		relkind;		/* 'r', 'm', 'i', etc */
 
 	/*
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 51ebf8ad13c..576326daec7 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4771,7 +4771,8 @@ my %tests = (
 			'relname',\s'dup_test_post_data_ix',\s+
 			'relpages',\s'\d+'::integer,\s+
 			'reltuples',\s'\d+'::real,\s+
-			'relallvisible',\s'\d+'::integer\s+
+			'relallvisible',\s'\d+'::integer,\s+
+			'relallfrozen',\s'\d+'::integer\s+
 			\);\s+
 			\QSELECT * FROM pg_catalog.pg_restore_attribute_stats(\E\s+
 			'version',\s'\d+'::integer,\s+

base-commit: a0a4601765b896079eb82a9d5cfa1f41154fcfdb
-- 
2.49.0



  [text/x-patch] v11-0004-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch (30.4K, ../../CADkLM=desCuf3dVHasADvdUVRmb-5gO0mhMO5u9nzgv6i7U86Q@mail.gmail.com/4-v11-0004-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch)
  download | inline diff:
From fd6cd3691e21b807a299749631dc0bdddc886853 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 8 Mar 2025 00:52:41 -0500
Subject: [PATCH v11 4/4] 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



  [text/x-patch] v11-0003-Batching-getAttributeStats.patch (21.5K, ../../CADkLM=desCuf3dVHasADvdUVRmb-5gO0mhMO5u9nzgv6i7U86Q@mail.gmail.com/5-v11-0003-Batching-getAttributeStats.patch)
  download | inline diff:
From 7b226732d1e68b5899c3dc8fbc6eb940c0f884ad Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 03:54:26 -0400
Subject: [PATCH v11 3/4] Batching getAttributeStats().

The prepared statement getAttributeStats() is fairly heavyweight and
could greatly increase pg_dump/pg_upgrade runtime. To alleviate this,
create a result set buffer of all of the attribute stats fetched for a
batch of 100 relations that could potentially have stats.

The query ensures that the order of results exactly matches the needs of
the code walking the TOC to print the stats calls.
---
 src/bin/pg_dump/pg_dump.c | 554 ++++++++++++++++++++++++++------------
 1 file changed, 383 insertions(+), 171 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index b7571ea15eb..bcf9dd1eb47 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -143,6 +143,25 @@ typedef enum OidOptions
 	zeroAsNone = 4,
 } OidOptions;
 
+typedef enum StatsBufferState
+{
+	STATSBUF_UNINITIALIZED = 0,
+	STATSBUF_ACTIVE,
+	STATSBUF_EXHAUSTED
+}			StatsBufferState;
+
+typedef struct
+{
+	PGresult   *res;			/* results from most recent
+								 * getAttributeStats() */
+	int			idx;			/* first un-consumed row of results */
+	TocEntry   *te;				/* next TOC entry to search for statsitics
+								 * data */
+
+	StatsBufferState state;		/* current state of the buffer */
+}			AttributeStatsBuffer;
+
+
 /* global decls */
 static bool dosync = true;		/* Issue fsync() to make dump durable on disk. */
 
@@ -209,6 +228,18 @@ static int	nbinaryUpgradeClassOids = 0;
 static SequenceItem *sequences = NULL;
 static int	nsequences = 0;
 
+static AttributeStatsBuffer attrstats =
+{
+	NULL, 0, NULL, STATSBUF_UNINITIALIZED
+};
+
+/*
+ * The maximum number of relations that should be fetched in any one
+ * getAttributeStats() call.
+ */
+
+#define MAX_ATTR_STATS_RELS 100
+
 /*
  * The default number of rows per INSERT when
  * --inserts is specified without --rows-per-insert
@@ -222,6 +253,8 @@ static int	nsequences = 0;
  */
 #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
 
+
+
 /*
  * Macro for producing quoted, schema-qualified name of a dumpable object.
  */
@@ -399,6 +432,9 @@ static void setupDumpWorker(Archive *AH);
 static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
 static bool forcePartitionRootLoad(const TableInfo *tbinfo);
 static void read_dump_filters(const char *filename, DumpOptions *dopt);
+static void appendNamedArgument(PQExpBuffer out, Archive *fout,
+								const char *argname, const char *argtype,
+								const char *argval);
 
 
 int
@@ -10556,7 +10592,286 @@ appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
 }
 
 /*
- * printDumpRelationStats --
+ * Fetch next batch of rows from getAttributeStats()
+ */
+static void
+fetchNextAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData schemas;
+	PQExpBufferData relations;
+	int			numoids = 0;
+
+	Assert(AH != NULL);
+
+	/* free last result set, if any */
+	if (attrstats.state == STATSBUF_ACTIVE)
+		PQclear(attrstats.res);
+
+	/* If we have looped around to the start of the TOC, restart */
+	if (attrstats.te == AH->toc)
+		attrstats.te = AH->toc->next;
+
+	initPQExpBuffer(&schemas);
+	initPQExpBuffer(&relations);
+
+	/*
+	 * Walk ahead looking for relstats entries that are active in this
+	 * section, adding the names to the schemas and relations lists.
+	 */
+	while ((attrstats.te != AH->toc) && (numoids < MAX_ATTR_STATS_RELS))
+	{
+		if (attrstats.te->reqs != 0 &&
+			strcmp(attrstats.te->desc, "STATISTICS DATA") == 0)
+		{
+			RelStatsInfo *rsinfo = (RelStatsInfo *) attrstats.te->createDumperArg;
+
+			Assert(rsinfo != NULL);
+
+			if (numoids > 0)
+			{
+				appendPQExpBufferStr(&schemas, ",");
+				appendPQExpBufferStr(&relations, ",");
+			}
+			appendPQExpBufferStr(&schemas, fmtId(rsinfo->dobj.namespace->dobj.name));
+			appendPQExpBufferStr(&relations, fmtId(rsinfo->dobj.name));
+			numoids++;
+		}
+
+		attrstats.te = attrstats.te->next;
+	}
+
+	if (numoids > 0)
+	{
+		PQExpBufferData query;
+
+		initPQExpBuffer(&query);
+		appendPQExpBuffer(&query,
+						  "EXECUTE getAttributeStats('{%s}'::pg_catalog.text[],'{%s}'::pg_catalog.text[])",
+						  schemas.data, relations.data);
+		attrstats.res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+		attrstats.idx = 0;
+	}
+	else
+	{
+		attrstats.state = STATSBUF_EXHAUSTED;
+		attrstats.res = NULL;
+		attrstats.idx = -1;
+	}
+
+	termPQExpBuffer(&schemas);
+	termPQExpBuffer(&relations);
+}
+
+/*
+ * Prepare the getAttributeStats() statement
+ *
+ * This is done automatically if the user specified dumpStatistics.
+ */
+static void
+initAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData query;
+
+	Assert(AH != NULL);
+	initPQExpBuffer(&query);
+
+	appendPQExpBufferStr(&query,
+						 "PREPARE getAttributeStats(pg_catalog.text[], pg_catalog.text[]) AS\n"
+						 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
+						 "s.null_frac, s.avg_width, s.n_distinct, s.most_common_vals, "
+						 "s.most_common_freqs, s.histogram_bounds, s.correlation, "
+						 "s.most_common_elems, s.most_common_elem_freqs, "
+						 "s.elem_count_histogram, ");
+
+	if (fout->remoteVersion >= 170000)
+		appendPQExpBufferStr(&query,
+							 "s.range_length_histogram, "
+							 "s.range_empty_frac, "
+							 "s.range_bounds_histogram ");
+	else
+		appendPQExpBufferStr(&query,
+							 "NULL AS range_length_histogram, "
+							 "NULL AS range_empty_frac, "
+							 " NULL AS range_bounds_histogram ");
+
+	/*
+	 * The results must be in the order of relations supplied in the
+	 * parameters to ensure that they are in sync with a walk of the TOC.
+	 *
+	 * The redundant (and incomplete) filter clause on s.tablename = ANY(...)
+	 * is a way to lead the query into using the index
+	 * pg_class_relname_nsp_index which in turn allows the planner to avoid an
+	 * expensive full scan of pg_stats.
+	 *
+	 * We may need to adjust this query for versions that are not so easily
+	 * led.
+	 */
+	appendPQExpBufferStr(&query,
+						 "FROM pg_catalog.pg_stats AS s "
+						 "JOIN unnest($1, $2) WITH ORDINALITY AS u(schemaname, tablename, ord) "
+						 "ON s.schemaname = u.schemaname "
+						 "AND s.tablename = u.tablename "
+						 "WHERE s.tablename = ANY($2) "
+						 "ORDER BY u.ord, s.attname, s.inherited");
+
+	ExecuteSqlStatement(fout, query.data);
+
+	termPQExpBuffer(&query);
+
+	attrstats.te = AH->toc->next;
+
+	fetchNextAttributeStats(fout);
+
+	attrstats.state = STATSBUF_ACTIVE;
+}
+
+
+/*
+ * append a single attribute stat to the buffer for this relation.
+ */
+static void
+appendAttributeStats(Archive *fout, PQExpBuffer out,
+					 const RelStatsInfo *rsinfo)
+{
+	PGresult   *res = attrstats.res;
+	int			tup_num = attrstats.idx;
+
+	const char *attname;
+
+	static bool indexes_set = false;
+	static int	i_attname,
+				i_inherited,
+				i_null_frac,
+				i_avg_width,
+				i_n_distinct,
+				i_most_common_vals,
+				i_most_common_freqs,
+				i_histogram_bounds,
+				i_correlation,
+				i_most_common_elems,
+				i_most_common_elem_freqs,
+				i_elem_count_histogram,
+				i_range_length_histogram,
+				i_range_empty_frac,
+				i_range_bounds_histogram;
+
+	if (!indexes_set)
+	{
+		/*
+		 * It's a prepared statement, so the indexes will be the same for all
+		 * result sets, so we only need to set them once.
+		 */
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		indexes_set = true;
+	}
+
+	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+					  fout->remoteVersion);
+	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, tup_num, i_attname))
+		pg_fatal("attname cannot be NULL");
+	attname = PQgetvalue(res, tup_num, i_attname);
+
+	/*
+	 * Indexes look up attname in indAttNames to derive attnum, all others use
+	 * attname directly.  We must specify attnum for indexes, since their
+	 * attnames are not necessarily stable across dump/reload.
+	 */
+	if (rsinfo->nindAttNames == 0)
+	{
+		appendPQExpBuffer(out, ",\n\t'attname', ");
+		appendStringLiteralAH(out, attname, fout);
+	}
+	else
+	{
+		bool		found = false;
+
+		for (int i = 0; i < rsinfo->nindAttNames; i++)
+			if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
+			{
+				appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+								  i + 1);
+				found = true;
+				break;
+			}
+
+		if (!found)
+			pg_fatal("could not find index attname \"%s\"", attname);
+	}
+
+	if (!PQgetisnull(res, tup_num, i_inherited))
+		appendNamedArgument(out, fout, "inherited", "boolean",
+							PQgetvalue(res, tup_num, i_inherited));
+	if (!PQgetisnull(res, tup_num, i_null_frac))
+		appendNamedArgument(out, fout, "null_frac", "real",
+							PQgetvalue(res, tup_num, i_null_frac));
+	if (!PQgetisnull(res, tup_num, i_avg_width))
+		appendNamedArgument(out, fout, "avg_width", "integer",
+							PQgetvalue(res, tup_num, i_avg_width));
+	if (!PQgetisnull(res, tup_num, i_n_distinct))
+		appendNamedArgument(out, fout, "n_distinct", "real",
+							PQgetvalue(res, tup_num, i_n_distinct));
+	if (!PQgetisnull(res, tup_num, i_most_common_vals))
+		appendNamedArgument(out, fout, "most_common_vals", "text",
+							PQgetvalue(res, tup_num, i_most_common_vals));
+	if (!PQgetisnull(res, tup_num, i_most_common_freqs))
+		appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_freqs));
+	if (!PQgetisnull(res, tup_num, i_histogram_bounds))
+		appendNamedArgument(out, fout, "histogram_bounds", "text",
+							PQgetvalue(res, tup_num, i_histogram_bounds));
+	if (!PQgetisnull(res, tup_num, i_correlation))
+		appendNamedArgument(out, fout, "correlation", "real",
+							PQgetvalue(res, tup_num, i_correlation));
+	if (!PQgetisnull(res, tup_num, i_most_common_elems))
+		appendNamedArgument(out, fout, "most_common_elems", "text",
+							PQgetvalue(res, tup_num, i_most_common_elems));
+	if (!PQgetisnull(res, tup_num, i_most_common_elem_freqs))
+		appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_elem_freqs));
+	if (!PQgetisnull(res, tup_num, i_elem_count_histogram))
+		appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+							PQgetvalue(res, tup_num, i_elem_count_histogram));
+	if (fout->remoteVersion >= 170000)
+	{
+		if (!PQgetisnull(res, tup_num, i_range_length_histogram))
+			appendNamedArgument(out, fout, "range_length_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_length_histogram));
+		if (!PQgetisnull(res, tup_num, i_range_empty_frac))
+			appendNamedArgument(out, fout, "range_empty_frac", "real",
+								PQgetvalue(res, tup_num, i_range_empty_frac));
+		if (!PQgetisnull(res, tup_num, i_range_bounds_histogram))
+			appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_bounds_histogram));
+	}
+	appendPQExpBufferStr(out, "\n);\n");
+}
+
+
+
+/*
+ * printRelationStats --
  *
  * Generate the SQL statements needed to restore a relation's statistics.
  */
@@ -10564,64 +10879,21 @@ static char *
 printRelationStats(Archive *fout, const void *userArg)
 {
 	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
-	const DumpableObject *dobj = &rsinfo->dobj;
+	const DumpableObject *dobj;
+	const char *relschema;
+	const char *relname;
+
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
 
-	PQExpBufferData query;
 	PQExpBufferData out;
 
-	PGresult   *res;
-
-	static bool first_query = true;
-	static int	i_attname;
-	static int	i_inherited;
-	static int	i_null_frac;
-	static int	i_avg_width;
-	static int	i_n_distinct;
-	static int	i_most_common_vals;
-	static int	i_most_common_freqs;
-	static int	i_histogram_bounds;
-	static int	i_correlation;
-	static int	i_most_common_elems;
-	static int	i_most_common_elem_freqs;
-	static int	i_elem_count_histogram;
-	static int	i_range_length_histogram;
-	static int	i_range_empty_frac;
-	static int	i_range_bounds_histogram;
-
-	initPQExpBuffer(&query);
-
-	if (first_query)
-	{
-		appendPQExpBufferStr(&query,
-							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
-							 "SELECT s.attname, s.inherited, "
-							 "s.null_frac, s.avg_width, s.n_distinct, "
-							 "s.most_common_vals, s.most_common_freqs, "
-							 "s.histogram_bounds, s.correlation, "
-							 "s.most_common_elems, s.most_common_elem_freqs, "
-							 "s.elem_count_histogram, ");
-
-		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(&query,
-								 "s.range_length_histogram, "
-								 "s.range_empty_frac, "
-								 "s.range_bounds_histogram ");
-		else
-			appendPQExpBufferStr(&query,
-								 "NULL AS range_length_histogram,"
-								 "NULL AS range_empty_frac,"
-								 "NULL AS range_bounds_histogram ");
-
-		appendPQExpBufferStr(&query,
-							 "FROM pg_catalog.pg_stats s "
-							 "WHERE s.schemaname = $1 "
-							 "AND s.tablename = $2 "
-							 "ORDER BY s.attname, s.inherited");
-
-		ExecuteSqlStatement(fout, query.data);
-
-		resetPQExpBuffer(&query);
-	}
+	Assert(rsinfo != NULL);
+	dobj = &rsinfo->dobj;
+	Assert(dobj != NULL);
+	relschema = dobj->namespace->dobj.name;
+	Assert(relschema != NULL);
+	relname = dobj->name;
+	Assert(relname != NULL);
 
 	initPQExpBuffer(&out);
 
@@ -10642,132 +10914,72 @@ printRelationStats(Archive *fout, const void *userArg)
 	appendPQExpBufferStr(&out, "\n);\n");
 
 
-	/* fetch attribute stats */
-	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(&query, ", ");
-	appendStringLiteralAH(&query, dobj->name, fout);
-	appendPQExpBufferStr(&query, ")");
+	AH->txnCount++;
 
-	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+	if (attrstats.state == STATSBUF_UNINITIALIZED)
+		initAttributeStats(fout);
 
-	if (first_query)
+	/*
+	 * Because the query returns rows in the same order as the relations
+	 * requested, and because every relation gets at least one row in the
+	 * result set, the first row for this relation must correspond either to
+	 * the current row of this result set (if one exists) or the first row of
+	 * the next result set (if this one is already consumed).
+	 */
+	if (attrstats.state != STATSBUF_ACTIVE)
+		pg_fatal("Exhausted getAttributeStats() before processing %s.%s",
+				 rsinfo->dobj.namespace->dobj.name,
+				 rsinfo->dobj.name);
+
+	/*
+	 * If the current result set has been fully consumed, then the row(s) we
+	 * need (if any) would be found in the next one. This will update
+	 * attrstats.res and attrstats.idx.
+	 */
+	if (PQntuples(attrstats.res) <= attrstats.idx)
+		fetchNextAttributeStats(fout);
+
+	while (true)
 	{
-		i_attname = PQfnumber(res, "attname");
-		i_inherited = PQfnumber(res, "inherited");
-		i_null_frac = PQfnumber(res, "null_frac");
-		i_avg_width = PQfnumber(res, "avg_width");
-		i_n_distinct = PQfnumber(res, "n_distinct");
-		i_most_common_vals = PQfnumber(res, "most_common_vals");
-		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-		i_correlation = PQfnumber(res, "correlation");
-		i_most_common_elems = PQfnumber(res, "most_common_elems");
-		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
-		first_query = false;
-	}
-
-	/* restore attribute stats */
-	for (int rownum = 0; rownum < PQntuples(res); rownum++)
-	{
-		const char *attname;
-
-		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
-						  fout->remoteVersion);
-		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");
-		attname = PQgetvalue(res, rownum, i_attname);
+		int			i_schemaname;
+		int			i_tablename;
+		char	   *schemaname;
+		char	   *tablename;	/* misnomer, following pg_stats naming */
 
 		/*
-		 * Indexes look up attname in indAttNames to derive attnum, all others
-		 * use attname directly.  We must specify attnum for indexes, since
-		 * their attnames are not necessarily stable across dump/reload.
+		 * If we hit the end of the result set, then there are no more records
+		 * for this relation, so we should stop, but first get the next result
+		 * set for the next batch of relations.
 		 */
-		if (rsinfo->nindAttNames == 0)
+		if (PQntuples(attrstats.res) <= attrstats.idx)
 		{
-			appendPQExpBuffer(&out, ",\n\t'attname', ");
-			appendStringLiteralAH(&out, attname, fout);
-		}
-		else
-		{
-			bool		found = false;
-
-			for (int i = 0; i < rsinfo->nindAttNames; i++)
-			{
-				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
-				{
-					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
-									  i + 1);
-					found = true;
-					break;
-				}
-			}
-
-			if (!found)
-				pg_fatal("could not find index attname \"%s\"", attname);
+			fetchNextAttributeStats(fout);
+			break;
 		}
 
-		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(&out, fout, "inherited", "boolean",
-								PQgetvalue(res, rownum, i_inherited));
-		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(&out, fout, "null_frac", "real",
-								PQgetvalue(res, rownum, i_null_frac));
-		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(&out, fout, "avg_width", "integer",
-								PQgetvalue(res, rownum, i_avg_width));
-		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(&out, fout, "n_distinct", "real",
-								PQgetvalue(res, rownum, i_n_distinct));
-		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(&out, fout, "most_common_vals", "text",
-								PQgetvalue(res, rownum, i_most_common_vals));
-		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_freqs));
-		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(&out, fout, "histogram_bounds", "text",
-								PQgetvalue(res, rownum, i_histogram_bounds));
-		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(&out, fout, "correlation", "real",
-								PQgetvalue(res, rownum, i_correlation));
-		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(&out, fout, "most_common_elems", "text",
-								PQgetvalue(res, rownum, i_most_common_elems));
-		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_elem_freqs));
-		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
-								PQgetvalue(res, rownum, i_elem_count_histogram));
-		if (fout->remoteVersion >= 170000)
-		{
-			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(&out, fout, "range_length_histogram", "text",
-									PQgetvalue(res, rownum, i_range_length_histogram));
-			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(&out, fout, "range_empty_frac", "real",
-									PQgetvalue(res, rownum, i_range_empty_frac));
-			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
-									PQgetvalue(res, rownum, i_range_bounds_histogram));
-		}
-		appendPQExpBufferStr(&out, "\n);\n");
+		i_schemaname = PQfnumber(attrstats.res, "schemaname");
+		Assert(i_schemaname >= 0);
+		i_tablename = PQfnumber(attrstats.res, "tablename");
+		Assert(i_tablename >= 0);
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_schemaname))
+			pg_fatal("getAttributeStats() schemaname cannot be NULL");
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_tablename))
+			pg_fatal("getAttributeStats() tablename cannot be NULL");
+
+		schemaname = PQgetvalue(attrstats.res, attrstats.idx, i_schemaname);
+		tablename = PQgetvalue(attrstats.res, attrstats.idx, i_tablename);
+
+		/* stop if current stat row isn't for this relation */
+		if (strcmp(relname, tablename) != 0 || strcmp(relschema, schemaname) != 0)
+			break;
+
+		appendAttributeStats(fout, &out, rsinfo);
+		AH->txnCount++;
+		attrstats.idx++;
 	}
 
-	PQclear(res);
-
-	termPQExpBuffer(&query);
 	return out.data;
 }
 
-- 
2.49.0



  [text/x-patch] v11-0002-Introduce-CreateStmtPtr.patch (17.2K, ../../CADkLM=desCuf3dVHasADvdUVRmb-5gO0mhMO5u9nzgv6i7U86Q@mail.gmail.com/6-v11-0002-Introduce-CreateStmtPtr.patch)
  download | inline diff:
From c71426ee2068d37237b79a65d1a0764b7cd3d60f Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 01:06:19 -0400
Subject: [PATCH v11 2/4] Introduce CreateStmtPtr.

CreateStmtPtr is a function pointer that can replace the createStmt/defn
parameter. This is useful in situations where the amount of text
generated for a definition is so large that it is undesirable to hold
many such objects in memory at the same time.

Using functions of this type, the text created is then immediately
written out to the appropriate file for the given dump format.
---
 src/bin/pg_dump/pg_backup.h          |   2 +
 src/bin/pg_dump/pg_backup_archiver.c |  22 ++-
 src/bin/pg_dump/pg_backup_archiver.h |   7 +
 src/bin/pg_dump/pg_dump.c            | 229 +++++++++++++++------------
 4 files changed, 158 insertions(+), 102 deletions(-)

diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 658986de6f8..fdcccd64a70 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -289,6 +289,8 @@ typedef int (*DataDumperPtr) (Archive *AH, const void *userArg);
 
 typedef void (*SetupWorkerPtrType) (Archive *AH);
 
+typedef char *(*CreateStmtPtr) (Archive *AH, const void *userArg);
+
 /*
  * Main archiver interface.
  */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 1d131e5a57d..1b4c62fd7d7 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1265,6 +1265,9 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
 	newToc->dataDumper = opts->dumpFn;
 	newToc->dataDumperArg = opts->dumpArg;
 	newToc->hadDumper = opts->dumpFn ? true : false;
+	newToc->createDumper = opts->createFn;
+	newToc->createDumperArg = opts->createArg;
+	newToc->hadCreateDumper = opts->createFn ? true : false;
 
 	newToc->formatData = NULL;
 	newToc->dataLength = 0;
@@ -2621,7 +2624,17 @@ WriteToc(ArchiveHandle *AH)
 		WriteStr(AH, te->tag);
 		WriteStr(AH, te->desc);
 		WriteInt(AH, te->section);
-		WriteStr(AH, te->defn);
+
+		if (te->hadCreateDumper)
+		{
+			char	   *defn = te->createDumper((Archive *) AH, te->createDumperArg);
+
+			WriteStr(AH, defn);
+			pg_free(defn);
+		}
+		else
+			WriteStr(AH, te->defn);
+
 		WriteStr(AH, te->dropStmt);
 		WriteStr(AH, te->copyStmt);
 		WriteStr(AH, te->namespace);
@@ -3877,6 +3890,13 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
 	{
 		IssueACLPerBlob(AH, te);
 	}
+	else if (te->hadCreateDumper)
+	{
+		char	   *ptr = te->createDumper((Archive *) AH, te->createDumperArg);
+
+		ahwrite(ptr, 1, strlen(ptr), AH);
+		pg_free(ptr);
+	}
 	else if (te->defn && strlen(te->defn) > 0)
 	{
 		ahprintf(AH, "%s\n\n", te->defn);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a2064f471ed..e68db633995 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -368,6 +368,11 @@ struct _tocEntry
 	const void *dataDumperArg;	/* Arg for above routine */
 	void	   *formatData;		/* TOC Entry data specific to file format */
 
+	CreateStmtPtr createDumper; /* Routine for create statement creation */
+	const void *createDumperArg;	/* arg for the above routine */
+	bool		hadCreateDumper;	/* Archiver was passed a create statement
+									 * routine */
+
 	/* working state while dumping/restoring */
 	pgoff_t		dataLength;		/* item's data size; 0 if none or unknown */
 	int			reqs;			/* do we need schema and/or data of object
@@ -407,6 +412,8 @@ typedef struct _archiveOpts
 	int			nDeps;
 	DataDumperPtr dumpFn;
 	const void *dumpArg;
+	CreateStmtPtr createFn;
+	const void *createArg;
 } ArchiveOpts;
 #define ARCHIVE_OPTS(...) &(ArchiveOpts){__VA_ARGS__}
 /* Called to add a TOC entry */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 211cf10dbd6..b7571ea15eb 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10556,42 +10556,44 @@ appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
 }
 
 /*
- * dumpRelationStats --
+ * printDumpRelationStats --
  *
- * Dump command to import stats into the relation on the new database.
+ * Generate the SQL statements needed to restore a relation's statistics.
  */
-static void
-dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+static char *
+printRelationStats(Archive *fout, const void *userArg)
 {
+	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
 	const DumpableObject *dobj = &rsinfo->dobj;
+
+	PQExpBufferData query;
+	PQExpBufferData out;
+
 	PGresult   *res;
-	PQExpBuffer query;
-	PQExpBuffer out;
-	int			i_attname;
-	int			i_inherited;
-	int			i_null_frac;
-	int			i_avg_width;
-	int			i_n_distinct;
-	int			i_most_common_vals;
-	int			i_most_common_freqs;
-	int			i_histogram_bounds;
-	int			i_correlation;
-	int			i_most_common_elems;
-	int			i_most_common_elem_freqs;
-	int			i_elem_count_histogram;
-	int			i_range_length_histogram;
-	int			i_range_empty_frac;
-	int			i_range_bounds_histogram;
 
-	/* nothing to do if we are not dumping statistics */
-	if (!fout->dopt->dumpStatistics)
-		return;
+	static bool first_query = true;
+	static int	i_attname;
+	static int	i_inherited;
+	static int	i_null_frac;
+	static int	i_avg_width;
+	static int	i_n_distinct;
+	static int	i_most_common_vals;
+	static int	i_most_common_freqs;
+	static int	i_histogram_bounds;
+	static int	i_correlation;
+	static int	i_most_common_elems;
+	static int	i_most_common_elem_freqs;
+	static int	i_elem_count_histogram;
+	static int	i_range_length_histogram;
+	static int	i_range_empty_frac;
+	static int	i_range_bounds_histogram;
 
-	query = createPQExpBuffer();
-	if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
+	initPQExpBuffer(&query);
+
+	if (first_query)
 	{
-		appendPQExpBufferStr(query,
-							 "PREPARE getAttributeStats(pg_catalog.name, pg_catalog.name) AS\n"
+		appendPQExpBufferStr(&query,
+							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
 							 "SELECT s.attname, s.inherited, "
 							 "s.null_frac, s.avg_width, s.n_distinct, "
 							 "s.most_common_vals, s.most_common_freqs, "
@@ -10600,88 +10602,87 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 							 "s.elem_count_histogram, ");
 
 		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "s.range_length_histogram, "
 								 "s.range_empty_frac, "
 								 "s.range_bounds_histogram ");
 		else
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "NULL AS range_length_histogram,"
 								 "NULL AS range_empty_frac,"
 								 "NULL AS range_bounds_histogram ");
 
-		appendPQExpBufferStr(query,
+		appendPQExpBufferStr(&query,
 							 "FROM pg_catalog.pg_stats s "
 							 "WHERE s.schemaname = $1 "
 							 "AND s.tablename = $2 "
 							 "ORDER BY s.attname, s.inherited");
 
-		ExecuteSqlStatement(fout, query->data);
+		ExecuteSqlStatement(fout, query.data);
 
-		fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
-		resetPQExpBuffer(query);
+		resetPQExpBuffer(&query);
 	}
 
-	out = createPQExpBuffer();
+	initPQExpBuffer(&out);
 
 	/* 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'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",
-					  rsinfo->relallvisible);
+	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(");
+	appendPQExpBuffer(&out, "\n\t'version', '%u'::integer", fout->remoteVersion);
+	appendPQExpBufferStr(&out, ",\n\t'schemaname', ");
+	appendStringLiteralAH(&out, rsinfo->dobj.namespace->dobj.name, fout);
+	appendPQExpBufferStr(&out, ",\n\t'relname', ");
+	appendStringLiteralAH(&out, rsinfo->dobj.name, fout);
+	appendPQExpBuffer(&out, ",\n\t'relpages', '%d'::integer", rsinfo->relpages);
+	appendPQExpBuffer(&out, ",\n\t'reltuples', '%s'::real", rsinfo->reltuples);
+	appendPQExpBuffer(&out, ",\n\t'relallvisible', '%d'::integer", rsinfo->relallvisible);
 
 	if (fout->remoteVersion >= 180000)
-		appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
+		appendPQExpBuffer(&out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
 
-	appendPQExpBufferStr(out, "\n);\n");
+	appendPQExpBufferStr(&out, "\n);\n");
 
 
 	/* fetch attribute stats */
-	appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(query, ", ");
-	appendStringLiteralAH(query, dobj->name, fout);
-	appendPQExpBufferStr(query, ");");
+	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
+	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
+	appendPQExpBufferStr(&query, ", ");
+	appendStringLiteralAH(&query, dobj->name, fout);
+	appendPQExpBufferStr(&query, ")");
 
-	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
 
-	i_attname = PQfnumber(res, "attname");
-	i_inherited = PQfnumber(res, "inherited");
-	i_null_frac = PQfnumber(res, "null_frac");
-	i_avg_width = PQfnumber(res, "avg_width");
-	i_n_distinct = PQfnumber(res, "n_distinct");
-	i_most_common_vals = PQfnumber(res, "most_common_vals");
-	i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-	i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-	i_correlation = PQfnumber(res, "correlation");
-	i_most_common_elems = PQfnumber(res, "most_common_elems");
-	i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-	i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-	i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-	i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-	i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+	if (first_query)
+	{
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		first_query = false;
+	}
 
 	/* restore attribute stats */
 	for (int rownum = 0; rownum < PQntuples(res); rownum++)
 	{
 		const char *attname;
 
-		appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 						  fout->remoteVersion);
-		appendPQExpBufferStr(out, "\t'schemaname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
-		appendPQExpBufferStr(out, ",\n\t'relname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.name, fout);
+		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");
@@ -10694,8 +10695,8 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		 */
 		if (rsinfo->nindAttNames == 0)
 		{
-			appendPQExpBuffer(out, ",\n\t'attname', ");
-			appendStringLiteralAH(out, attname, fout);
+			appendPQExpBuffer(&out, ",\n\t'attname', ");
+			appendStringLiteralAH(&out, attname, fout);
 		}
 		else
 		{
@@ -10705,7 +10706,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 			{
 				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
 				{
-					appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
 									  i + 1);
 					found = true;
 					break;
@@ -10717,66 +10718,92 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		}
 
 		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(out, fout, "inherited", "boolean",
+			appendNamedArgument(&out, fout, "inherited", "boolean",
 								PQgetvalue(res, rownum, i_inherited));
 		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(out, fout, "null_frac", "real",
+			appendNamedArgument(&out, fout, "null_frac", "real",
 								PQgetvalue(res, rownum, i_null_frac));
 		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(out, fout, "avg_width", "integer",
+			appendNamedArgument(&out, fout, "avg_width", "integer",
 								PQgetvalue(res, rownum, i_avg_width));
 		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(out, fout, "n_distinct", "real",
+			appendNamedArgument(&out, fout, "n_distinct", "real",
 								PQgetvalue(res, rownum, i_n_distinct));
 		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(out, fout, "most_common_vals", "text",
+			appendNamedArgument(&out, fout, "most_common_vals", "text",
 								PQgetvalue(res, rownum, i_most_common_vals));
 		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_freqs));
 		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(out, fout, "histogram_bounds", "text",
+			appendNamedArgument(&out, fout, "histogram_bounds", "text",
 								PQgetvalue(res, rownum, i_histogram_bounds));
 		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(out, fout, "correlation", "real",
+			appendNamedArgument(&out, fout, "correlation", "real",
 								PQgetvalue(res, rownum, i_correlation));
 		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(out, fout, "most_common_elems", "text",
+			appendNamedArgument(&out, fout, "most_common_elems", "text",
 								PQgetvalue(res, rownum, i_most_common_elems));
 		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_elem_freqs));
 		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
 								PQgetvalue(res, rownum, i_elem_count_histogram));
 		if (fout->remoteVersion >= 170000)
 		{
 			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(out, fout, "range_length_histogram", "text",
+				appendNamedArgument(&out, fout, "range_length_histogram", "text",
 									PQgetvalue(res, rownum, i_range_length_histogram));
 			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(out, fout, "range_empty_frac", "real",
+				appendNamedArgument(&out, fout, "range_empty_frac", "real",
 									PQgetvalue(res, rownum, i_range_empty_frac));
 			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
 									PQgetvalue(res, rownum, i_range_bounds_histogram));
 		}
-		appendPQExpBufferStr(out, "\n);\n");
+		appendPQExpBufferStr(&out, "\n);\n");
 	}
 
 	PQclear(res);
 
+	termPQExpBuffer(&query);
+	return out.data;
+}
+
+/*
+ * dumpRelationStats --
+ *
+ * Dump command to import stats into the relation on the new database.
+ */
+static void
+dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+{
+	const DumpableObject *dobj = &rsinfo->dobj;
+
+	DumpId	   *deps = NULL;
+	int			ndeps = 0;
+
+	/* nothing to do if we are not dumping statistics */
+	if (!fout->dopt->dumpStatistics)
+		return;
+
+	/* dependent on the relation definition, if doing schema */
+	if (fout->dopt->dumpSchema)
+	{
+		deps = dobj->dependencies;
+		ndeps = dobj->nDeps;
+	}
+
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 ARCHIVE_OPTS(.tag = dobj->name,
 							  .namespace = dobj->namespace->dobj.name,
 							  .description = "STATISTICS DATA",
 							  .section = rsinfo->section,
-							  .createStmt = out->data,
-							  .deps = dobj->dependencies,
-							  .nDeps = dobj->nDeps));
-
-	destroyPQExpBuffer(out);
-	destroyPQExpBuffer(query);
+							  .createFn = printRelationStats,
+							  .createArg = rsinfo,
+							  .deps = deps,
+							  .nDeps = ndeps));
 }
 
 /*
-- 
2.49.0



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-29 05:29                                 ` Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-29 05:29 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, 2025-03-28 at 21:11 -0400, Corey Huinker wrote:
> A rebase and a reordering of the commits to put the really-really-
> must-have relallfrozen ahead of the really-must-have stats batching
> and both of them head of the error->warning step-downs.

v11-0001 has a couple issues:

The first is that i_relallfrozen is undefined in versions earlier than
18. That's trivial to fix, we just add "0 AS relallfrozen," in the
earlier versions, but still refrain from outputting it.

The second is that the pg_upgrade test (when run with
olddump/oldinstall) compares the before and after dumps, and if the
"before" version is 17, then it will not have the relallfrozen argument
to pg_restore_relation_stats. We might need a filtering step in
adjust_new_dumpfile?

Attached new v11j-0001

Regards,
	Jeff Davis



Attachments:

  [text/x-patch] v11j-0001-Add-relallfrozen-to-pg_dump-statistics.patch (7.7K, ../../[email protected]/2-v11j-0001-Add-relallfrozen-to-pg_dump-statistics.patch)
  download | inline diff:
From 154b8b5c10ec330c26ccd9006c434a7db1feef04 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Sat, 15 Mar 2025 17:34:30 -0400
Subject: [PATCH v11j] Add relallfrozen to pg_dump statistics.

Author: Corey Huinker <[email protected]>
Discussion: https://postgr.es/m/CADkLM=desCuf3dVHasADvdUVRmb-5gO0mhMO5u9nzgv6i7U86Q@mail.gmail.com
---
 src/bin/pg_dump/pg_dump.c                     | 42 +++++++++++++++----
 src/bin/pg_dump/pg_dump.h                     |  1 +
 src/bin/pg_dump/t/002_pg_dump.pl              |  3 +-
 .../perl/PostgreSQL/Test/AdjustUpgrade.pm     |  5 +++
 4 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 84a78625820..4ca34be230c 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6874,7 +6874,8 @@ getFuncs(Archive *fout)
  */
 static RelStatsInfo *
 getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
-					  char *reltuples, int32 relallvisible, char relkind,
+					  char *reltuples, int32 relallvisible,
+					  int32 relallfrozen, char relkind,
 					  char **indAttNames, int nindAttNames)
 {
 	if (!fout->dopt->dumpStatistics)
@@ -6903,6 +6904,7 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
 		info->relpages = relpages;
 		info->reltuples = pstrdup(reltuples);
 		info->relallvisible = relallvisible;
+		info->relallfrozen = relallfrozen;
 		info->relkind = relkind;
 		info->indAttNames = indAttNames;
 		info->nindAttNames = nindAttNames;
@@ -6967,6 +6969,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relpages;
 	int			i_reltuples;
 	int			i_relallvisible;
+	int			i_relallfrozen;
 	int			i_toastpages;
 	int			i_owning_tab;
 	int			i_owning_col;
@@ -7017,8 +7020,15 @@ getTables(Archive *fout, int *numTables)
 						 "c.relowner, "
 						 "c.relchecks, "
 						 "c.relhasindex, c.relhasrules, c.relpages, "
-						 "c.reltuples, c.relallvisible, c.relhastriggers, "
-						 "c.relpersistence, "
+						 "c.reltuples, c.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "c.relallfrozen, ");
+	else
+		appendPQExpBufferStr(query, "0 AS relallfrozen, ");
+
+	appendPQExpBufferStr(query,
+						 "c.relhastriggers, c.relpersistence, "
 						 "c.reloftype, "
 						 "c.relacl, "
 						 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
@@ -7183,6 +7193,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_toastpages = PQfnumber(res, "toastpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
@@ -7230,6 +7241,7 @@ getTables(Archive *fout, int *numTables)
 	for (i = 0; i < ntups; i++)
 	{
 		int32		relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
+		int32		relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
 
 		tblinfo[i].dobj.objType = DO_TABLE;
 		tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
@@ -7336,7 +7348,7 @@ getTables(Archive *fout, int *numTables)
 			stats = getRelationStatistics(fout, &tblinfo[i].dobj,
 										  tblinfo[i].relpages,
 										  PQgetvalue(res, i, i_reltuples),
-										  relallvisible,
+										  relallvisible, relallfrozen,
 										  tblinfo[i].relkind, NULL, 0);
 			if (tblinfo[i].relkind == RELKIND_MATVIEW)
 				tblinfo[i].stats = stats;
@@ -7609,6 +7621,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_relpages,
 				i_reltuples,
 				i_relallvisible,
+				i_relallfrozen,
 				i_parentidx,
 				i_indexdef,
 				i_indnkeyatts,
@@ -7663,7 +7676,14 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	appendPQExpBufferStr(query,
 						 "SELECT t.tableoid, t.oid, i.indrelid, "
 						 "t.relname AS indexname, "
-						 "t.relpages, t.reltuples, t.relallvisible, "
+						 "t.relpages, t.reltuples, t.relallvisible, ");
+
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBufferStr(query, "t.relallfrozen, ");
+	else
+		appendPQExpBufferStr(query, "0 AS relallfrozen, ");
+
+	appendPQExpBufferStr(query,
 						 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
 						 "i.indkey, i.indisclustered, "
 						 "c.contype, c.conname, "
@@ -7779,6 +7799,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_reltuples = PQfnumber(res, "reltuples");
 	i_relallvisible = PQfnumber(res, "relallvisible");
+	i_relallfrozen = PQfnumber(res, "relallfrozen");
 	i_parentidx = PQfnumber(res, "parentidx");
 	i_indexdef = PQfnumber(res, "indexdef");
 	i_indnkeyatts = PQfnumber(res, "indnkeyatts");
@@ -7850,6 +7871,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 			RelStatsInfo *relstats;
 			int32		relpages = atoi(PQgetvalue(res, j, i_relpages));
 			int32		relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
+			int32		relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
 
 			indxinfo[j].dobj.objType = DO_INDEX;
 			indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
@@ -7892,7 +7914,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 
 			relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
 											 PQgetvalue(res, j, i_reltuples),
-											 relallvisible, indexkind,
+											 relallvisible, relallfrozen, indexkind,
 											 indAttNames, nindAttNames);
 
 			contype = *(PQgetvalue(res, j, i_contype));
@@ -10618,9 +10640,15 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 	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",
+	appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
 					  rsinfo->relallvisible);
 
+	if (fout->remoteVersion >= 180000)
+		appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
+
+	appendPQExpBufferStr(out, "\n);\n");
+
+
 	/* fetch attribute stats */
 	appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
 	appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 70f7a369e4a..e6f0f86a459 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -442,6 +442,7 @@ typedef struct _relStatsInfo
 	int32		relpages;
 	char	   *reltuples;
 	int32		relallvisible;
+	int32		relallfrozen;
 	char		relkind;		/* 'r', 'm', 'i', etc */
 
 	/*
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 51ebf8ad13c..576326daec7 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4771,7 +4771,8 @@ my %tests = (
 			'relname',\s'dup_test_post_data_ix',\s+
 			'relpages',\s'\d+'::integer,\s+
 			'reltuples',\s'\d+'::real,\s+
-			'relallvisible',\s'\d+'::integer\s+
+			'relallvisible',\s'\d+'::integer,\s+
+			'relallfrozen',\s'\d+'::integer\s+
 			\);\s+
 			\QSELECT * FROM pg_catalog.pg_restore_attribute_stats(\E\s+
 			'version',\s'\d+'::integer,\s+
diff --git a/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm b/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
index 81a8f44aa9f..07550295a82 100644
--- a/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
+++ b/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
@@ -648,6 +648,11 @@ sub adjust_new_dumpfile
 	$dump =~ s {\n(\s+'version',) '\d+'::integer,$}
 		{$1 '000000'::integer,}mg;
 
+	if ($old_version < 18)
+	{
+		$dump =~ s {,\n(\s+'relallfrozen',) '\d+'::integer$}{}mg;
+	}
+
 	# pre-v16 dumps do not know about XMLSERIALIZE(NO INDENT).
 	if ($old_version < 16)
 	{
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-03-29 05:44                                   ` Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-29 05:44 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> The first is that i_relallfrozen is undefined in versions earlier than
> 18. That's trivial to fix, we just add "0 AS relallfrozen," in the
> earlier versions, but still refrain from outputting it.
>

Ok, so long as we refrain from outputting it, I'm cool with whatever we
store internally.



> The second is that the pg_upgrade test (when run with
> olddump/oldinstall) compares the before and after dumps, and if the
> "before" version is 17, then it will not have the relallfrozen argument
> to pg_restore_relation_stats. We might need a filtering step in
> adjust_new_dumpfile?
>

That sounds trickier. Do we already have filtering steps that are sensitive
to the "before" version dump?


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-31 15:11                                     ` Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-31 15:11 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> The second is that the pg_upgrade test (when run with
>> olddump/oldinstall) compares the before and after dumps, and if the
>> "before" version is 17, then it will not have the relallfrozen argument
>> to pg_restore_relation_stats. We might need a filtering step in
>> adjust_new_dumpfile?
>>
>
> That sounds trickier.
>

Narrator: It was not trickier.

In light of v11-0001 being committed as 4694aedf63bf, I've rebased the
remaining patches.


Attachments:

  [text/x-patch] v12-0001-Introduce-CreateStmtPtr.patch (17.3K, ../../CADkLM=domd5+CvjKMHGbOfvSuY6J8G-x+9M2D6Ss2HamYefE9w@mail.gmail.com/3-v12-0001-Introduce-CreateStmtPtr.patch)
  download | inline diff:
From 607984bdcc91fa31fb7a12e9b24fb8704aa14975 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 01:06:19 -0400
Subject: [PATCH v12 1/3] Introduce CreateStmtPtr.

CreateStmtPtr is a function pointer that can replace the createStmt/defn
parameter. This is useful in situations where the amount of text
generated for a definition is so large that it is undesirable to hold
many such objects in memory at the same time.

Using functions of this type, the text created is then immediately
written out to the appropriate file for the given dump format.
---
 src/bin/pg_dump/pg_backup.h          |   2 +
 src/bin/pg_dump/pg_backup_archiver.c |  22 ++-
 src/bin/pg_dump/pg_backup_archiver.h |   7 +
 src/bin/pg_dump/pg_dump.c            | 229 +++++++++++++++------------
 4 files changed, 158 insertions(+), 102 deletions(-)

diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 658986de6f8..fdcccd64a70 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -289,6 +289,8 @@ typedef int (*DataDumperPtr) (Archive *AH, const void *userArg);
 
 typedef void (*SetupWorkerPtrType) (Archive *AH);
 
+typedef char *(*CreateStmtPtr) (Archive *AH, const void *userArg);
+
 /*
  * Main archiver interface.
  */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 1d131e5a57d..1b4c62fd7d7 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1265,6 +1265,9 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
 	newToc->dataDumper = opts->dumpFn;
 	newToc->dataDumperArg = opts->dumpArg;
 	newToc->hadDumper = opts->dumpFn ? true : false;
+	newToc->createDumper = opts->createFn;
+	newToc->createDumperArg = opts->createArg;
+	newToc->hadCreateDumper = opts->createFn ? true : false;
 
 	newToc->formatData = NULL;
 	newToc->dataLength = 0;
@@ -2621,7 +2624,17 @@ WriteToc(ArchiveHandle *AH)
 		WriteStr(AH, te->tag);
 		WriteStr(AH, te->desc);
 		WriteInt(AH, te->section);
-		WriteStr(AH, te->defn);
+
+		if (te->hadCreateDumper)
+		{
+			char	   *defn = te->createDumper((Archive *) AH, te->createDumperArg);
+
+			WriteStr(AH, defn);
+			pg_free(defn);
+		}
+		else
+			WriteStr(AH, te->defn);
+
 		WriteStr(AH, te->dropStmt);
 		WriteStr(AH, te->copyStmt);
 		WriteStr(AH, te->namespace);
@@ -3877,6 +3890,13 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
 	{
 		IssueACLPerBlob(AH, te);
 	}
+	else if (te->hadCreateDumper)
+	{
+		char	   *ptr = te->createDumper((Archive *) AH, te->createDumperArg);
+
+		ahwrite(ptr, 1, strlen(ptr), AH);
+		pg_free(ptr);
+	}
 	else if (te->defn && strlen(te->defn) > 0)
 	{
 		ahprintf(AH, "%s\n\n", te->defn);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a2064f471ed..e68db633995 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -368,6 +368,11 @@ struct _tocEntry
 	const void *dataDumperArg;	/* Arg for above routine */
 	void	   *formatData;		/* TOC Entry data specific to file format */
 
+	CreateStmtPtr createDumper; /* Routine for create statement creation */
+	const void *createDumperArg;	/* arg for the above routine */
+	bool		hadCreateDumper;	/* Archiver was passed a create statement
+									 * routine */
+
 	/* working state while dumping/restoring */
 	pgoff_t		dataLength;		/* item's data size; 0 if none or unknown */
 	int			reqs;			/* do we need schema and/or data of object
@@ -407,6 +412,8 @@ typedef struct _archiveOpts
 	int			nDeps;
 	DataDumperPtr dumpFn;
 	const void *dumpArg;
+	CreateStmtPtr createFn;
+	const void *createArg;
 } ArchiveOpts;
 #define ARCHIVE_OPTS(...) &(ArchiveOpts){__VA_ARGS__}
 /* Called to add a TOC entry */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4ca34be230c..cc195d6cd9e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10560,42 +10560,44 @@ appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
 }
 
 /*
- * dumpRelationStats --
+ * printDumpRelationStats --
  *
- * Dump command to import stats into the relation on the new database.
+ * Generate the SQL statements needed to restore a relation's statistics.
  */
-static void
-dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+static char *
+printRelationStats(Archive *fout, const void *userArg)
 {
+	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
 	const DumpableObject *dobj = &rsinfo->dobj;
+
+	PQExpBufferData query;
+	PQExpBufferData out;
+
 	PGresult   *res;
-	PQExpBuffer query;
-	PQExpBuffer out;
-	int			i_attname;
-	int			i_inherited;
-	int			i_null_frac;
-	int			i_avg_width;
-	int			i_n_distinct;
-	int			i_most_common_vals;
-	int			i_most_common_freqs;
-	int			i_histogram_bounds;
-	int			i_correlation;
-	int			i_most_common_elems;
-	int			i_most_common_elem_freqs;
-	int			i_elem_count_histogram;
-	int			i_range_length_histogram;
-	int			i_range_empty_frac;
-	int			i_range_bounds_histogram;
 
-	/* nothing to do if we are not dumping statistics */
-	if (!fout->dopt->dumpStatistics)
-		return;
+	static bool first_query = true;
+	static int	i_attname;
+	static int	i_inherited;
+	static int	i_null_frac;
+	static int	i_avg_width;
+	static int	i_n_distinct;
+	static int	i_most_common_vals;
+	static int	i_most_common_freqs;
+	static int	i_histogram_bounds;
+	static int	i_correlation;
+	static int	i_most_common_elems;
+	static int	i_most_common_elem_freqs;
+	static int	i_elem_count_histogram;
+	static int	i_range_length_histogram;
+	static int	i_range_empty_frac;
+	static int	i_range_bounds_histogram;
 
-	query = createPQExpBuffer();
-	if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
+	initPQExpBuffer(&query);
+
+	if (first_query)
 	{
-		appendPQExpBufferStr(query,
-							 "PREPARE getAttributeStats(pg_catalog.name, pg_catalog.name) AS\n"
+		appendPQExpBufferStr(&query,
+							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
 							 "SELECT s.attname, s.inherited, "
 							 "s.null_frac, s.avg_width, s.n_distinct, "
 							 "s.most_common_vals, s.most_common_freqs, "
@@ -10604,88 +10606,87 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 							 "s.elem_count_histogram, ");
 
 		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "s.range_length_histogram, "
 								 "s.range_empty_frac, "
 								 "s.range_bounds_histogram ");
 		else
-			appendPQExpBufferStr(query,
+			appendPQExpBufferStr(&query,
 								 "NULL AS range_length_histogram,"
 								 "NULL AS range_empty_frac,"
 								 "NULL AS range_bounds_histogram ");
 
-		appendPQExpBufferStr(query,
+		appendPQExpBufferStr(&query,
 							 "FROM pg_catalog.pg_stats s "
 							 "WHERE s.schemaname = $1 "
 							 "AND s.tablename = $2 "
 							 "ORDER BY s.attname, s.inherited");
 
-		ExecuteSqlStatement(fout, query->data);
+		ExecuteSqlStatement(fout, query.data);
 
-		fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
-		resetPQExpBuffer(query);
+		resetPQExpBuffer(&query);
 	}
 
-	out = createPQExpBuffer();
+	initPQExpBuffer(&out);
 
 	/* 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'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",
-					  rsinfo->relallvisible);
+	appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(");
+	appendPQExpBuffer(&out, "\n\t'version', '%u'::integer", fout->remoteVersion);
+	appendPQExpBufferStr(&out, ",\n\t'schemaname', ");
+	appendStringLiteralAH(&out, rsinfo->dobj.namespace->dobj.name, fout);
+	appendPQExpBufferStr(&out, ",\n\t'relname', ");
+	appendStringLiteralAH(&out, rsinfo->dobj.name, fout);
+	appendPQExpBuffer(&out, ",\n\t'relpages', '%d'::integer", rsinfo->relpages);
+	appendPQExpBuffer(&out, ",\n\t'reltuples', '%s'::real", rsinfo->reltuples);
+	appendPQExpBuffer(&out, ",\n\t'relallvisible', '%d'::integer", rsinfo->relallvisible);
 
 	if (fout->remoteVersion >= 180000)
-		appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
+		appendPQExpBuffer(&out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
 
-	appendPQExpBufferStr(out, "\n);\n");
+	appendPQExpBufferStr(&out, "\n);\n");
 
 
 	/* fetch attribute stats */
-	appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(query, ", ");
-	appendStringLiteralAH(query, dobj->name, fout);
-	appendPQExpBufferStr(query, ");");
+	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
+	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
+	appendPQExpBufferStr(&query, ", ");
+	appendStringLiteralAH(&query, dobj->name, fout);
+	appendPQExpBufferStr(&query, ")");
 
-	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
 
-	i_attname = PQfnumber(res, "attname");
-	i_inherited = PQfnumber(res, "inherited");
-	i_null_frac = PQfnumber(res, "null_frac");
-	i_avg_width = PQfnumber(res, "avg_width");
-	i_n_distinct = PQfnumber(res, "n_distinct");
-	i_most_common_vals = PQfnumber(res, "most_common_vals");
-	i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-	i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-	i_correlation = PQfnumber(res, "correlation");
-	i_most_common_elems = PQfnumber(res, "most_common_elems");
-	i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-	i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-	i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-	i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-	i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+	if (first_query)
+	{
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		first_query = false;
+	}
 
 	/* restore attribute stats */
 	for (int rownum = 0; rownum < PQntuples(res); rownum++)
 	{
 		const char *attname;
 
-		appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
 						  fout->remoteVersion);
-		appendPQExpBufferStr(out, "\t'schemaname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
-		appendPQExpBufferStr(out, ",\n\t'relname', ");
-		appendStringLiteralAH(out, rsinfo->dobj.name, fout);
+		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");
@@ -10698,8 +10699,8 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		 */
 		if (rsinfo->nindAttNames == 0)
 		{
-			appendPQExpBuffer(out, ",\n\t'attname', ");
-			appendStringLiteralAH(out, attname, fout);
+			appendPQExpBuffer(&out, ",\n\t'attname', ");
+			appendStringLiteralAH(&out, attname, fout);
 		}
 		else
 		{
@@ -10709,7 +10710,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 			{
 				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
 				{
-					appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
 									  i + 1);
 					found = true;
 					break;
@@ -10721,66 +10722,92 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
 		}
 
 		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(out, fout, "inherited", "boolean",
+			appendNamedArgument(&out, fout, "inherited", "boolean",
 								PQgetvalue(res, rownum, i_inherited));
 		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(out, fout, "null_frac", "real",
+			appendNamedArgument(&out, fout, "null_frac", "real",
 								PQgetvalue(res, rownum, i_null_frac));
 		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(out, fout, "avg_width", "integer",
+			appendNamedArgument(&out, fout, "avg_width", "integer",
 								PQgetvalue(res, rownum, i_avg_width));
 		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(out, fout, "n_distinct", "real",
+			appendNamedArgument(&out, fout, "n_distinct", "real",
 								PQgetvalue(res, rownum, i_n_distinct));
 		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(out, fout, "most_common_vals", "text",
+			appendNamedArgument(&out, fout, "most_common_vals", "text",
 								PQgetvalue(res, rownum, i_most_common_vals));
 		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_freqs));
 		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(out, fout, "histogram_bounds", "text",
+			appendNamedArgument(&out, fout, "histogram_bounds", "text",
 								PQgetvalue(res, rownum, i_histogram_bounds));
 		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(out, fout, "correlation", "real",
+			appendNamedArgument(&out, fout, "correlation", "real",
 								PQgetvalue(res, rownum, i_correlation));
 		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(out, fout, "most_common_elems", "text",
+			appendNamedArgument(&out, fout, "most_common_elems", "text",
 								PQgetvalue(res, rownum, i_most_common_elems));
 		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_elem_freqs));
 		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
 								PQgetvalue(res, rownum, i_elem_count_histogram));
 		if (fout->remoteVersion >= 170000)
 		{
 			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(out, fout, "range_length_histogram", "text",
+				appendNamedArgument(&out, fout, "range_length_histogram", "text",
 									PQgetvalue(res, rownum, i_range_length_histogram));
 			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(out, fout, "range_empty_frac", "real",
+				appendNamedArgument(&out, fout, "range_empty_frac", "real",
 									PQgetvalue(res, rownum, i_range_empty_frac));
 			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
 									PQgetvalue(res, rownum, i_range_bounds_histogram));
 		}
-		appendPQExpBufferStr(out, "\n);\n");
+		appendPQExpBufferStr(&out, "\n);\n");
 	}
 
 	PQclear(res);
 
+	termPQExpBuffer(&query);
+	return out.data;
+}
+
+/*
+ * dumpRelationStats --
+ *
+ * Dump command to import stats into the relation on the new database.
+ */
+static void
+dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+{
+	const DumpableObject *dobj = &rsinfo->dobj;
+
+	DumpId	   *deps = NULL;
+	int			ndeps = 0;
+
+	/* nothing to do if we are not dumping statistics */
+	if (!fout->dopt->dumpStatistics)
+		return;
+
+	/* dependent on the relation definition, if doing schema */
+	if (fout->dopt->dumpSchema)
+	{
+		deps = dobj->dependencies;
+		ndeps = dobj->nDeps;
+	}
+
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 ARCHIVE_OPTS(.tag = dobj->name,
 							  .namespace = dobj->namespace->dobj.name,
 							  .description = "STATISTICS DATA",
 							  .section = rsinfo->section,
-							  .createStmt = out->data,
-							  .deps = dobj->dependencies,
-							  .nDeps = dobj->nDeps));
-
-	destroyPQExpBuffer(out);
-	destroyPQExpBuffer(query);
+							  .createFn = printRelationStats,
+							  .createArg = rsinfo,
+							  .deps = deps,
+							  .nDeps = ndeps));
 }
 
 /*

base-commit: e2809e3a1015697832ee4d37b75ba1cd0caac0f0
-- 
2.49.0



  [text/x-patch] v12-0002-Batching-getAttributeStats.patch (21.5K, ../../CADkLM=domd5+CvjKMHGbOfvSuY6J8G-x+9M2D6Ss2HamYefE9w@mail.gmail.com/4-v12-0002-Batching-getAttributeStats.patch)
  download | inline diff:
From 410171805037718c3adcae778bba56c485038e3f Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 14 Mar 2025 03:54:26 -0400
Subject: [PATCH v12 2/3] Batching getAttributeStats().

The prepared statement getAttributeStats() is fairly heavyweight and
could greatly increase pg_dump/pg_upgrade runtime. To alleviate this,
create a result set buffer of all of the attribute stats fetched for a
batch of 100 relations that could potentially have stats.

The query ensures that the order of results exactly matches the needs of
the code walking the TOC to print the stats calls.
---
 src/bin/pg_dump/pg_dump.c | 554 ++++++++++++++++++++++++++------------
 1 file changed, 383 insertions(+), 171 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index cc195d6cd9e..26144371b1b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -143,6 +143,25 @@ typedef enum OidOptions
 	zeroAsNone = 4,
 } OidOptions;
 
+typedef enum StatsBufferState
+{
+	STATSBUF_UNINITIALIZED = 0,
+	STATSBUF_ACTIVE,
+	STATSBUF_EXHAUSTED
+}			StatsBufferState;
+
+typedef struct
+{
+	PGresult   *res;			/* results from most recent
+								 * getAttributeStats() */
+	int			idx;			/* first un-consumed row of results */
+	TocEntry   *te;				/* next TOC entry to search for statsitics
+								 * data */
+
+	StatsBufferState state;		/* current state of the buffer */
+}			AttributeStatsBuffer;
+
+
 /* global decls */
 static bool dosync = true;		/* Issue fsync() to make dump durable on disk. */
 
@@ -209,6 +228,18 @@ static int	nbinaryUpgradeClassOids = 0;
 static SequenceItem *sequences = NULL;
 static int	nsequences = 0;
 
+static AttributeStatsBuffer attrstats =
+{
+	NULL, 0, NULL, STATSBUF_UNINITIALIZED
+};
+
+/*
+ * The maximum number of relations that should be fetched in any one
+ * getAttributeStats() call.
+ */
+
+#define MAX_ATTR_STATS_RELS 100
+
 /*
  * The default number of rows per INSERT when
  * --inserts is specified without --rows-per-insert
@@ -222,6 +253,8 @@ static int	nsequences = 0;
  */
 #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
 
+
+
 /*
  * Macro for producing quoted, schema-qualified name of a dumpable object.
  */
@@ -399,6 +432,9 @@ static void setupDumpWorker(Archive *AH);
 static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
 static bool forcePartitionRootLoad(const TableInfo *tbinfo);
 static void read_dump_filters(const char *filename, DumpOptions *dopt);
+static void appendNamedArgument(PQExpBuffer out, Archive *fout,
+								const char *argname, const char *argtype,
+								const char *argval);
 
 
 int
@@ -10560,7 +10596,286 @@ appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
 }
 
 /*
- * printDumpRelationStats --
+ * Fetch next batch of rows from getAttributeStats()
+ */
+static void
+fetchNextAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData schemas;
+	PQExpBufferData relations;
+	int			numoids = 0;
+
+	Assert(AH != NULL);
+
+	/* free last result set, if any */
+	if (attrstats.state == STATSBUF_ACTIVE)
+		PQclear(attrstats.res);
+
+	/* If we have looped around to the start of the TOC, restart */
+	if (attrstats.te == AH->toc)
+		attrstats.te = AH->toc->next;
+
+	initPQExpBuffer(&schemas);
+	initPQExpBuffer(&relations);
+
+	/*
+	 * Walk ahead looking for relstats entries that are active in this
+	 * section, adding the names to the schemas and relations lists.
+	 */
+	while ((attrstats.te != AH->toc) && (numoids < MAX_ATTR_STATS_RELS))
+	{
+		if (attrstats.te->reqs != 0 &&
+			strcmp(attrstats.te->desc, "STATISTICS DATA") == 0)
+		{
+			RelStatsInfo *rsinfo = (RelStatsInfo *) attrstats.te->createDumperArg;
+
+			Assert(rsinfo != NULL);
+
+			if (numoids > 0)
+			{
+				appendPQExpBufferStr(&schemas, ",");
+				appendPQExpBufferStr(&relations, ",");
+			}
+			appendPQExpBufferStr(&schemas, fmtId(rsinfo->dobj.namespace->dobj.name));
+			appendPQExpBufferStr(&relations, fmtId(rsinfo->dobj.name));
+			numoids++;
+		}
+
+		attrstats.te = attrstats.te->next;
+	}
+
+	if (numoids > 0)
+	{
+		PQExpBufferData query;
+
+		initPQExpBuffer(&query);
+		appendPQExpBuffer(&query,
+						  "EXECUTE getAttributeStats('{%s}'::pg_catalog.text[],'{%s}'::pg_catalog.text[])",
+						  schemas.data, relations.data);
+		attrstats.res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+		attrstats.idx = 0;
+	}
+	else
+	{
+		attrstats.state = STATSBUF_EXHAUSTED;
+		attrstats.res = NULL;
+		attrstats.idx = -1;
+	}
+
+	termPQExpBuffer(&schemas);
+	termPQExpBuffer(&relations);
+}
+
+/*
+ * Prepare the getAttributeStats() statement
+ *
+ * This is done automatically if the user specified dumpStatistics.
+ */
+static void
+initAttributeStats(Archive *fout)
+{
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
+	PQExpBufferData query;
+
+	Assert(AH != NULL);
+	initPQExpBuffer(&query);
+
+	appendPQExpBufferStr(&query,
+						 "PREPARE getAttributeStats(pg_catalog.text[], pg_catalog.text[]) AS\n"
+						 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
+						 "s.null_frac, s.avg_width, s.n_distinct, s.most_common_vals, "
+						 "s.most_common_freqs, s.histogram_bounds, s.correlation, "
+						 "s.most_common_elems, s.most_common_elem_freqs, "
+						 "s.elem_count_histogram, ");
+
+	if (fout->remoteVersion >= 170000)
+		appendPQExpBufferStr(&query,
+							 "s.range_length_histogram, "
+							 "s.range_empty_frac, "
+							 "s.range_bounds_histogram ");
+	else
+		appendPQExpBufferStr(&query,
+							 "NULL AS range_length_histogram, "
+							 "NULL AS range_empty_frac, "
+							 " NULL AS range_bounds_histogram ");
+
+	/*
+	 * The results must be in the order of relations supplied in the
+	 * parameters to ensure that they are in sync with a walk of the TOC.
+	 *
+	 * The redundant (and incomplete) filter clause on s.tablename = ANY(...)
+	 * is a way to lead the query into using the index
+	 * pg_class_relname_nsp_index which in turn allows the planner to avoid an
+	 * expensive full scan of pg_stats.
+	 *
+	 * We may need to adjust this query for versions that are not so easily
+	 * led.
+	 */
+	appendPQExpBufferStr(&query,
+						 "FROM pg_catalog.pg_stats AS s "
+						 "JOIN unnest($1, $2) WITH ORDINALITY AS u(schemaname, tablename, ord) "
+						 "ON s.schemaname = u.schemaname "
+						 "AND s.tablename = u.tablename "
+						 "WHERE s.tablename = ANY($2) "
+						 "ORDER BY u.ord, s.attname, s.inherited");
+
+	ExecuteSqlStatement(fout, query.data);
+
+	termPQExpBuffer(&query);
+
+	attrstats.te = AH->toc->next;
+
+	fetchNextAttributeStats(fout);
+
+	attrstats.state = STATSBUF_ACTIVE;
+}
+
+
+/*
+ * append a single attribute stat to the buffer for this relation.
+ */
+static void
+appendAttributeStats(Archive *fout, PQExpBuffer out,
+					 const RelStatsInfo *rsinfo)
+{
+	PGresult   *res = attrstats.res;
+	int			tup_num = attrstats.idx;
+
+	const char *attname;
+
+	static bool indexes_set = false;
+	static int	i_attname,
+				i_inherited,
+				i_null_frac,
+				i_avg_width,
+				i_n_distinct,
+				i_most_common_vals,
+				i_most_common_freqs,
+				i_histogram_bounds,
+				i_correlation,
+				i_most_common_elems,
+				i_most_common_elem_freqs,
+				i_elem_count_histogram,
+				i_range_length_histogram,
+				i_range_empty_frac,
+				i_range_bounds_histogram;
+
+	if (!indexes_set)
+	{
+		/*
+		 * It's a prepared statement, so the indexes will be the same for all
+		 * result sets, so we only need to set them once.
+		 */
+		i_attname = PQfnumber(res, "attname");
+		i_inherited = PQfnumber(res, "inherited");
+		i_null_frac = PQfnumber(res, "null_frac");
+		i_avg_width = PQfnumber(res, "avg_width");
+		i_n_distinct = PQfnumber(res, "n_distinct");
+		i_most_common_vals = PQfnumber(res, "most_common_vals");
+		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+		i_correlation = PQfnumber(res, "correlation");
+		i_most_common_elems = PQfnumber(res, "most_common_elems");
+		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+		indexes_set = true;
+	}
+
+	appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+	appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+					  fout->remoteVersion);
+	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, tup_num, i_attname))
+		pg_fatal("attname cannot be NULL");
+	attname = PQgetvalue(res, tup_num, i_attname);
+
+	/*
+	 * Indexes look up attname in indAttNames to derive attnum, all others use
+	 * attname directly.  We must specify attnum for indexes, since their
+	 * attnames are not necessarily stable across dump/reload.
+	 */
+	if (rsinfo->nindAttNames == 0)
+	{
+		appendPQExpBuffer(out, ",\n\t'attname', ");
+		appendStringLiteralAH(out, attname, fout);
+	}
+	else
+	{
+		bool		found = false;
+
+		for (int i = 0; i < rsinfo->nindAttNames; i++)
+			if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
+			{
+				appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+								  i + 1);
+				found = true;
+				break;
+			}
+
+		if (!found)
+			pg_fatal("could not find index attname \"%s\"", attname);
+	}
+
+	if (!PQgetisnull(res, tup_num, i_inherited))
+		appendNamedArgument(out, fout, "inherited", "boolean",
+							PQgetvalue(res, tup_num, i_inherited));
+	if (!PQgetisnull(res, tup_num, i_null_frac))
+		appendNamedArgument(out, fout, "null_frac", "real",
+							PQgetvalue(res, tup_num, i_null_frac));
+	if (!PQgetisnull(res, tup_num, i_avg_width))
+		appendNamedArgument(out, fout, "avg_width", "integer",
+							PQgetvalue(res, tup_num, i_avg_width));
+	if (!PQgetisnull(res, tup_num, i_n_distinct))
+		appendNamedArgument(out, fout, "n_distinct", "real",
+							PQgetvalue(res, tup_num, i_n_distinct));
+	if (!PQgetisnull(res, tup_num, i_most_common_vals))
+		appendNamedArgument(out, fout, "most_common_vals", "text",
+							PQgetvalue(res, tup_num, i_most_common_vals));
+	if (!PQgetisnull(res, tup_num, i_most_common_freqs))
+		appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_freqs));
+	if (!PQgetisnull(res, tup_num, i_histogram_bounds))
+		appendNamedArgument(out, fout, "histogram_bounds", "text",
+							PQgetvalue(res, tup_num, i_histogram_bounds));
+	if (!PQgetisnull(res, tup_num, i_correlation))
+		appendNamedArgument(out, fout, "correlation", "real",
+							PQgetvalue(res, tup_num, i_correlation));
+	if (!PQgetisnull(res, tup_num, i_most_common_elems))
+		appendNamedArgument(out, fout, "most_common_elems", "text",
+							PQgetvalue(res, tup_num, i_most_common_elems));
+	if (!PQgetisnull(res, tup_num, i_most_common_elem_freqs))
+		appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+							PQgetvalue(res, tup_num, i_most_common_elem_freqs));
+	if (!PQgetisnull(res, tup_num, i_elem_count_histogram))
+		appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+							PQgetvalue(res, tup_num, i_elem_count_histogram));
+	if (fout->remoteVersion >= 170000)
+	{
+		if (!PQgetisnull(res, tup_num, i_range_length_histogram))
+			appendNamedArgument(out, fout, "range_length_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_length_histogram));
+		if (!PQgetisnull(res, tup_num, i_range_empty_frac))
+			appendNamedArgument(out, fout, "range_empty_frac", "real",
+								PQgetvalue(res, tup_num, i_range_empty_frac));
+		if (!PQgetisnull(res, tup_num, i_range_bounds_histogram))
+			appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+								PQgetvalue(res, tup_num, i_range_bounds_histogram));
+	}
+	appendPQExpBufferStr(out, "\n);\n");
+}
+
+
+
+/*
+ * printRelationStats --
  *
  * Generate the SQL statements needed to restore a relation's statistics.
  */
@@ -10568,64 +10883,21 @@ static char *
 printRelationStats(Archive *fout, const void *userArg)
 {
 	const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
-	const DumpableObject *dobj = &rsinfo->dobj;
+	const DumpableObject *dobj;
+	const char *relschema;
+	const char *relname;
+
+	ArchiveHandle *AH = (ArchiveHandle *) fout;
 
-	PQExpBufferData query;
 	PQExpBufferData out;
 
-	PGresult   *res;
-
-	static bool first_query = true;
-	static int	i_attname;
-	static int	i_inherited;
-	static int	i_null_frac;
-	static int	i_avg_width;
-	static int	i_n_distinct;
-	static int	i_most_common_vals;
-	static int	i_most_common_freqs;
-	static int	i_histogram_bounds;
-	static int	i_correlation;
-	static int	i_most_common_elems;
-	static int	i_most_common_elem_freqs;
-	static int	i_elem_count_histogram;
-	static int	i_range_length_histogram;
-	static int	i_range_empty_frac;
-	static int	i_range_bounds_histogram;
-
-	initPQExpBuffer(&query);
-
-	if (first_query)
-	{
-		appendPQExpBufferStr(&query,
-							 "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
-							 "SELECT s.attname, s.inherited, "
-							 "s.null_frac, s.avg_width, s.n_distinct, "
-							 "s.most_common_vals, s.most_common_freqs, "
-							 "s.histogram_bounds, s.correlation, "
-							 "s.most_common_elems, s.most_common_elem_freqs, "
-							 "s.elem_count_histogram, ");
-
-		if (fout->remoteVersion >= 170000)
-			appendPQExpBufferStr(&query,
-								 "s.range_length_histogram, "
-								 "s.range_empty_frac, "
-								 "s.range_bounds_histogram ");
-		else
-			appendPQExpBufferStr(&query,
-								 "NULL AS range_length_histogram,"
-								 "NULL AS range_empty_frac,"
-								 "NULL AS range_bounds_histogram ");
-
-		appendPQExpBufferStr(&query,
-							 "FROM pg_catalog.pg_stats s "
-							 "WHERE s.schemaname = $1 "
-							 "AND s.tablename = $2 "
-							 "ORDER BY s.attname, s.inherited");
-
-		ExecuteSqlStatement(fout, query.data);
-
-		resetPQExpBuffer(&query);
-	}
+	Assert(rsinfo != NULL);
+	dobj = &rsinfo->dobj;
+	Assert(dobj != NULL);
+	relschema = dobj->namespace->dobj.name;
+	Assert(relschema != NULL);
+	relname = dobj->name;
+	Assert(relname != NULL);
 
 	initPQExpBuffer(&out);
 
@@ -10646,132 +10918,72 @@ printRelationStats(Archive *fout, const void *userArg)
 	appendPQExpBufferStr(&out, "\n);\n");
 
 
-	/* fetch attribute stats */
-	appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
-	appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
-	appendPQExpBufferStr(&query, ", ");
-	appendStringLiteralAH(&query, dobj->name, fout);
-	appendPQExpBufferStr(&query, ")");
+	AH->txnCount++;
 
-	res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
+	if (attrstats.state == STATSBUF_UNINITIALIZED)
+		initAttributeStats(fout);
 
-	if (first_query)
+	/*
+	 * Because the query returns rows in the same order as the relations
+	 * requested, and because every relation gets at least one row in the
+	 * result set, the first row for this relation must correspond either to
+	 * the current row of this result set (if one exists) or the first row of
+	 * the next result set (if this one is already consumed).
+	 */
+	if (attrstats.state != STATSBUF_ACTIVE)
+		pg_fatal("Exhausted getAttributeStats() before processing %s.%s",
+				 rsinfo->dobj.namespace->dobj.name,
+				 rsinfo->dobj.name);
+
+	/*
+	 * If the current result set has been fully consumed, then the row(s) we
+	 * need (if any) would be found in the next one. This will update
+	 * attrstats.res and attrstats.idx.
+	 */
+	if (PQntuples(attrstats.res) <= attrstats.idx)
+		fetchNextAttributeStats(fout);
+
+	while (true)
 	{
-		i_attname = PQfnumber(res, "attname");
-		i_inherited = PQfnumber(res, "inherited");
-		i_null_frac = PQfnumber(res, "null_frac");
-		i_avg_width = PQfnumber(res, "avg_width");
-		i_n_distinct = PQfnumber(res, "n_distinct");
-		i_most_common_vals = PQfnumber(res, "most_common_vals");
-		i_most_common_freqs = PQfnumber(res, "most_common_freqs");
-		i_histogram_bounds = PQfnumber(res, "histogram_bounds");
-		i_correlation = PQfnumber(res, "correlation");
-		i_most_common_elems = PQfnumber(res, "most_common_elems");
-		i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
-		i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
-		i_range_length_histogram = PQfnumber(res, "range_length_histogram");
-		i_range_empty_frac = PQfnumber(res, "range_empty_frac");
-		i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
-		first_query = false;
-	}
-
-	/* restore attribute stats */
-	for (int rownum = 0; rownum < PQntuples(res); rownum++)
-	{
-		const char *attname;
-
-		appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
-		appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
-						  fout->remoteVersion);
-		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");
-		attname = PQgetvalue(res, rownum, i_attname);
+		int			i_schemaname;
+		int			i_tablename;
+		char	   *schemaname;
+		char	   *tablename;	/* misnomer, following pg_stats naming */
 
 		/*
-		 * Indexes look up attname in indAttNames to derive attnum, all others
-		 * use attname directly.  We must specify attnum for indexes, since
-		 * their attnames are not necessarily stable across dump/reload.
+		 * If we hit the end of the result set, then there are no more records
+		 * for this relation, so we should stop, but first get the next result
+		 * set for the next batch of relations.
 		 */
-		if (rsinfo->nindAttNames == 0)
+		if (PQntuples(attrstats.res) <= attrstats.idx)
 		{
-			appendPQExpBuffer(&out, ",\n\t'attname', ");
-			appendStringLiteralAH(&out, attname, fout);
-		}
-		else
-		{
-			bool		found = false;
-
-			for (int i = 0; i < rsinfo->nindAttNames; i++)
-			{
-				if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
-				{
-					appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
-									  i + 1);
-					found = true;
-					break;
-				}
-			}
-
-			if (!found)
-				pg_fatal("could not find index attname \"%s\"", attname);
+			fetchNextAttributeStats(fout);
+			break;
 		}
 
-		if (!PQgetisnull(res, rownum, i_inherited))
-			appendNamedArgument(&out, fout, "inherited", "boolean",
-								PQgetvalue(res, rownum, i_inherited));
-		if (!PQgetisnull(res, rownum, i_null_frac))
-			appendNamedArgument(&out, fout, "null_frac", "real",
-								PQgetvalue(res, rownum, i_null_frac));
-		if (!PQgetisnull(res, rownum, i_avg_width))
-			appendNamedArgument(&out, fout, "avg_width", "integer",
-								PQgetvalue(res, rownum, i_avg_width));
-		if (!PQgetisnull(res, rownum, i_n_distinct))
-			appendNamedArgument(&out, fout, "n_distinct", "real",
-								PQgetvalue(res, rownum, i_n_distinct));
-		if (!PQgetisnull(res, rownum, i_most_common_vals))
-			appendNamedArgument(&out, fout, "most_common_vals", "text",
-								PQgetvalue(res, rownum, i_most_common_vals));
-		if (!PQgetisnull(res, rownum, i_most_common_freqs))
-			appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_freqs));
-		if (!PQgetisnull(res, rownum, i_histogram_bounds))
-			appendNamedArgument(&out, fout, "histogram_bounds", "text",
-								PQgetvalue(res, rownum, i_histogram_bounds));
-		if (!PQgetisnull(res, rownum, i_correlation))
-			appendNamedArgument(&out, fout, "correlation", "real",
-								PQgetvalue(res, rownum, i_correlation));
-		if (!PQgetisnull(res, rownum, i_most_common_elems))
-			appendNamedArgument(&out, fout, "most_common_elems", "text",
-								PQgetvalue(res, rownum, i_most_common_elems));
-		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
-			appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
-								PQgetvalue(res, rownum, i_most_common_elem_freqs));
-		if (!PQgetisnull(res, rownum, i_elem_count_histogram))
-			appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
-								PQgetvalue(res, rownum, i_elem_count_histogram));
-		if (fout->remoteVersion >= 170000)
-		{
-			if (!PQgetisnull(res, rownum, i_range_length_histogram))
-				appendNamedArgument(&out, fout, "range_length_histogram", "text",
-									PQgetvalue(res, rownum, i_range_length_histogram));
-			if (!PQgetisnull(res, rownum, i_range_empty_frac))
-				appendNamedArgument(&out, fout, "range_empty_frac", "real",
-									PQgetvalue(res, rownum, i_range_empty_frac));
-			if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
-				appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
-									PQgetvalue(res, rownum, i_range_bounds_histogram));
-		}
-		appendPQExpBufferStr(&out, "\n);\n");
+		i_schemaname = PQfnumber(attrstats.res, "schemaname");
+		Assert(i_schemaname >= 0);
+		i_tablename = PQfnumber(attrstats.res, "tablename");
+		Assert(i_tablename >= 0);
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_schemaname))
+			pg_fatal("getAttributeStats() schemaname cannot be NULL");
+
+		if (PQgetisnull(attrstats.res, attrstats.idx, i_tablename))
+			pg_fatal("getAttributeStats() tablename cannot be NULL");
+
+		schemaname = PQgetvalue(attrstats.res, attrstats.idx, i_schemaname);
+		tablename = PQgetvalue(attrstats.res, attrstats.idx, i_tablename);
+
+		/* stop if current stat row isn't for this relation */
+		if (strcmp(relname, tablename) != 0 || strcmp(relschema, schemaname) != 0)
+			break;
+
+		appendAttributeStats(fout, &out, rsinfo);
+		AH->txnCount++;
+		attrstats.idx++;
 	}
 
-	PQclear(res);
-
-	termPQExpBuffer(&query);
 	return out.data;
 }
 
-- 
2.49.0



  [text/x-patch] v12-0003-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch (30.4K, ../../CADkLM=domd5+CvjKMHGbOfvSuY6J8G-x+9M2D6Ss2HamYefE9w@mail.gmail.com/5-v12-0003-Downgrade-many-pg_restore_-_stats-errors-to-warn.patch)
  download | inline diff:
From 16794820dedd79ec58f8692da5b50a4d8976620a Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
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



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-04-01 02:33                                       ` Nathan Bossart <[email protected]>
  2025-04-01 03:02                                         ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Nathan Bossart @ 2025-04-01 02:33 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Mon, Mar 31, 2025 at 11:11:47AM -0400, Corey Huinker wrote:
> In light of v11-0001 being committed as 4694aedf63bf, I've rebased the
> remaining patches.

I spent the day preparing these for commit.  A few notes:

* I've added a new prerequisite patch that skips the second WriteToc() call
  for custom-format dumps that do not include data.  After some testing and
  code analysis, I haven't identified any examples where this produces
  different output.  This doesn't help much on its own, but it will become
  rather important when we move the attribute statistics queries to happen
  within WriteToc() in 0002.

* I was a little worried about the correctness of 0002 for dumps that run
  the attribute statistics queries twice, but I couldn't identify any
  problems here either.

* I removed a lot of miscellaneous refactoring that seemed unnecessary for
  these patches.  Let's move that to another patch set and keep these as
  simple as possible.

* I made a small adjustment to the TOC scan restarting logic in
  fetchAttributeStats().  Specifically, we now only allow the scan to
  restart once for custom-format dumps that include data.

* While these patches help decrease pg_dump's memory footprint, I believe
  pg_restore still reads the entire TOC into memory.  That's not this patch
  set's problem, but I think it's still an important consideration for the
  bigger picture.

Regarding whether pg_dump should dump statistics by default, my current
thinking is that it shouldn't, but I think we _should_ have pg_upgrade
dump/restore statistics by default because that is arguably the most
important use-case.  This is more a gut feeling than anything, so I reserve
the right to change my opinion.

My goal is to commit the attached patches on Friday morning, but of course
that is subject to change based on any feedback or objections that emerge
in the meantime.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-01 03:02                                         ` Robert Treat <[email protected]>
  1 sibling, 0 replies; 88+ messages in thread

From: Robert Treat @ 2025-04-01 03:02 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Mon, Mar 31, 2025 at 10:33 PM Nathan Bossart
<[email protected]> wrote:
> On Mon, Mar 31, 2025 at 11:11:47AM -0400, Corey Huinker wrote:
> Regarding whether pg_dump should dump statistics by default, my current
> thinking is that it shouldn't, but I think we _should_ have pg_upgrade
> dump/restore statistics by default because that is arguably the most
> important use-case.  This is more a gut feeling than anything, so I reserve
> the right to change my opinion.
>

I did some mental exercises on a number of different use cases and
scenarios (pagila work, pgextractor type stuff, backups, etc...) and I
couldn't come up with any strong arguments against including the stats
by default, generally because I think when your process needs to care
about the output of pg_dump, it seems like most cases require enough
specificity that this wouldn't actually break that.

Still, I am sympathetic to Greg's earlier concerns on the topic, but
would also agree it seems like a clear win for pg_upgrade, so I think
our gut feelings might actually be aligned on this one ;-)


Robert Treat
https://xzilla.net





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-01 18:20                                         ` Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-01 18:20 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Mon, Mar 31, 2025 at 09:33:15PM -0500, Nathan Bossart wrote:
> My goal is to commit the attached patches on Friday morning, but of course
> that is subject to change based on any feedback or objections that emerge
> in the meantime.

I spent some more time polishing these patches this morning.  There should
be no functional differences, but I did restructure 0003 to make it even
simpler.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-01 18:44                                           ` Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-01 18:44 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, Apr 01, 2025 at 01:20:30PM -0500, Nathan Bossart wrote:
> On Mon, Mar 31, 2025 at 09:33:15PM -0500, Nathan Bossart wrote:
>> My goal is to commit the attached patches on Friday morning, but of course
>> that is subject to change based on any feedback or objections that emerge
>> in the meantime.
> 
> I spent some more time polishing these patches this morning.  There should
> be no functional differences, but I did restructure 0003 to make it even
> simpler.

Apologies for the noise.  I noticed one more way to simplify 0002.  As
before, there should be no functional differences.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-02 03:21                                             ` Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:38                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Nathan Bossart @ 2025-04-02 03:21 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, Apr 01, 2025 at 03:05:59PM -0700, Jeff Davis wrote:
> To restate the problem: one of the problems being solved here is that
> the existing code for custom-format dumps calls WriteToc twice. That
> was not a big problem before this patch, when the contents of the
> entries was easily accessible in memory. But the point of 0002 is to
> avoid keeping all of the stats in memory at once, because that causes
> bloat; and instead to query it on demand.
> 
> In theory, we could fix the pre-existing code by making the second pass
> able to jump over the other contents of the entry and just update the
> data offsets. But that seems invasive, at least to do it properly.
> 
> 0001 sidesteps the problem by skipping the second pass if data's not
> being dumped (because there are no offsets that need updating). The
> worst case is when there are a lot of objects with a small amount of
> data. But that's a worst case for stats in general, so I don't think
> that needs to be solved here.
> 
> Issuing the stats queries twice is not great, though. If there's any
> non-deterministic output in the query, that could lead to strangeness.
> How bad can that be? If the results change in some way that looks
> benign, but changes the length of the definition string, can it lead to
> corruption of a ToC entry? I'm not saying there's a problem, but trying
> to understand the risk of future problems.

It certainly feels risky.  I was able to avoid executing the queries twice
in all cases by saving the definition length in the TOC entry and skipping
that many bytes the second time round.  That's simple enough, but it relies
on various assumptions such as fseeko() being available (IIUC the file will
only be open for writing so we cannot fall back on fread()) and WriteStr()
returning an accurate value (which I'm skeptical of because some formats
compress this data).  But AFAICT custom format is the only format that does
a second WriteToc() pass at the moment, and it only does so when fseeko()
is usable.  Plus, custom format doesn't appear to compress anything written
via WriteStr().

We might be able to improve this by inventing a new callback that fails for
all formats except for custom with feesko() available.  That would at least
ensure hard failures if these assumptions change.  That problably wouldn't
be terribly invasive.  I'm curious what you think.

> For 0003, it makes an assumption about the way the scan happens in
> WriteToc(). Can you add some additional sanity checks to verify that
> something doesn't happen in a different order than we expect?

Hm.  One thing we could do is to send the TocEntry to the callback and
verify that matches the one we were expecting to see next (as set by a
previous call).  Does that sound like a strong enough check?  FWIW the
pg_dump tests failed miserably until Corey and I got this part right, so
our usual tests should also offer some assurance.

> Also, why do we need the clause "WHERE s.tablename = ANY($2)"? Isn't
> that already implied by "JOIN unnest($1, $2) ... s.tablename =
> u.tablename"?

Good question.  Corey, do you recall why this was needed?

-- 
nathan


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-02 05:44                                               ` Jeff Davis <[email protected]>
  2025-04-02 16:42                                                 ` Re: Statistics Import and Export Andres Freund <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  1 sibling, 2 replies; 88+ messages in thread

From: Jeff Davis @ 2025-04-02 05:44 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, 2025-04-01 at 22:21 -0500, Nathan Bossart wrote:
> It certainly feels risky.  I was able to avoid executing the queries
> twice
> in all cases by saving the definition length in the TOC entry and
> skipping
> that many bytes the second time round.

That feels like a better approach.

>   That's simple enough, but it relies
> on various assumptions such as fseeko() being available (IIUC the
> file will
> only be open for writing so we cannot fall back on fread()) and
> WriteStr()
> returning an accurate value (which I'm skeptical of because some
> formats
> compress this data).  But AFAICT custom format is the only format
> that does
> a second WriteToc() pass at the moment, and it only does so when
> fseeko()
> is usable.

Even with those assumptions, I think it's much better than querying
twice and assuming that the results are the same.

>   Plus, custom format doesn't appear to compress anything written
> via WriteStr().

If WriteStr() was doing compression, that would make the second
WriteToc() pass to update the data offsets scary even in the existing
code.

> We might be able to improve this by inventing a new callback that
> fails for
> all formats except for custom with feesko() available.  That would at
> least
> ensure hard failures if these assumptions change.  That problably
> wouldn't
> be terribly invasive.  I'm curious what you think.

That sounds fine, I'd say do that if it feels reasonable, and if the
extra callbacks get too messy, we can just document the assumptions
instead.

> 
> Hm.  One thing we could do is to send the TocEntry to the callback
> and
> verify that matches the one we were expecting to see next (as set by
> a
> previous call).  Does that sound like a strong enough check?

Again, I'd just be practical here and do the check if it feels natural,
and if not, improve the comments so that someone modifying the code
would know where to look.


Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-04-02 16:42                                                 ` Andres Freund <[email protected]>
  1 sibling, 0 replies; 88+ messages in thread

From: Andres Freund @ 2025-04-02 16:42 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Hi,

https://commitfest.postgresql.org/patch/4538/ is still in "needs review", even
though the feature really has been committed.  Is that intention, e.g. to
track pending changes that we're planning to make?

Greetings,

Andres





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-04-03 02:26                                                 ` Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-03 02:26 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, Apr 01, 2025 at 10:44:19PM -0700, Jeff Davis wrote:
> On Tue, 2025-04-01 at 22:21 -0500, Nathan Bossart wrote:
>> We might be able to improve this by inventing a new callback that fails for
>> all formats except for custom with feesko() available.  That would at least
>> ensure hard failures if these assumptions change.  That problably wouldn't
>> be terribly invasive.  I'm curious what you think.
> 
> That sounds fine, I'd say do that if it feels reasonable, and if the
> extra callbacks get too messy, we can just document the assumptions
> instead.

I did write a version with callbacks, but it felt a bit silly because it is
very obviously intended for this one case.  So, I removed them in the
attached patch set.

>> Hm.  One thing we could do is to send the TocEntry to the callback and
>> verify that matches the one we were expecting to see next (as set by a
>> previous call).  Does that sound like a strong enough check?
> 
> Again, I'd just be practical here and do the check if it feels natural,
> and if not, improve the comments so that someone modifying the code
> would know where to look.

Okay, here is an updated patch set.  I did add some verification code,
which ended up being a really good idea because it revealed a couple of
cases we weren't handling:

* Besides custom format calling WriteToc() twice to update the data
  offsets, tar format calls WriteToc() followed by RestoreArchive() to
  write restore.sql.  I couldn't think of a great way to avoid executing
  the queries twice in this case, so I settled on allowing it for only that
  mode.  While we don't expect the second set of queries to result in
  different stats definitions, even if it did, the worst case is that the
  content of restore.sql (which isn't used by pg_restore) would be
  different.  I noticed some past discussion that seems to suggest that
  this format might be a candidate for deprecation [0], so I'm not sure
  it's worth doing anything fancier.

* Our batching code assumes that stats entries are dumped in TOC order,
  which unfortunately wasn't true for formats that use RestoreArchive() for
  dumping.  This is because RestoreArchive() does multiple passes through
  the TOC and selectively dumps certain entries each time.  This is
  particularly troublesome for index stats and a subset of matview stats;
  both are in SECTION_POST_DATA, but matview stats that depend on matview
  data are dumped in RESTORE_PASS_POST_ACL, while all other stats data is
  dumped in RESTORE_PASS_MAIN.  To deal with this, I propose moving all
  stats entries in SECTION_POST_DATA to RESTORE_PASS_POST_ACL, which
  ensures that we always dump stats in TOC order.  One convenient side
  effect of this change is that we can revert a decent chunk of commit
  a0a4601765.  It might be possible to do better via smarter lookahead code
  or a more sophisticated cache, but it's a bit late in the game for that.

[0] https://postgr.es/m/20180727015306.fzlo4inv5i3zqr2c%40alap3.anarazel.de

-- 
nathan


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-04 02:19                                                   ` Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-04 02:19 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Thanks for reviewing.

On Thu, Apr 03, 2025 at 03:23:40PM -0700, Jeff Davis wrote:
> This simplifies commit a0a4601765. I'd break out that simplification as
> a separate commit to make it easier to understand what happened. 

Done.

> In patch 0003, there are quite a few static function-scoped variables,
> which is not a style that I'm used to. One idea is to bundle them into
> a struct representing the cache state (including enough information to
> fetch the next batch), and have a single static variable that points to
> that.

As discussed off-list, I didn't take this suggestion for now.  Corey did
this originally, and I converted it to static function-scoped variables 1)
to reduce patch size and 2) because I noticed that each of the state
variables were only needed in one function.  I agree that a struct might be
slightly more readable, but we can always change this in the future if
desired.

> Also in 0003, the "next_te" variable is a bit confusing, because it's
> actually the last TocEntry, until it's advanced to point to the current
> one.

I've renamed it to expected_te.

> Other than that, looks good to me.

Great.  I'm planning to commit the attached patch set tomorrow morning.

For the record, I spent most of today trying very hard to fix the layering
violations in 0002.  While I was successful, the result was awkward,
complicated, and nigh unreadable.  This is now the second time I've
attempted to fix this and have felt the result was worse than where I
started.  So, I added extremely descriptive comments instead.  I'm hoping
that it will be possible to clean this up with some additional work in v19.
I have a few ideas, but if anyone has suggestions, I'm all ears.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-04 19:56                                                     ` Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-04 19:56 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Thu, Apr 03, 2025 at 09:19:51PM -0500, Nathan Bossart wrote:
> Great.  I'm planning to commit the attached patch set tomorrow morning.

Committed.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-04 20:06                                                       ` Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-04 20:06 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Apr 04, 2025 at 02:56:54PM -0500, Nathan Bossart wrote:
> Committed.

I see the buildfarm failure and am working on a fix.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-04 20:58                                                         ` Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-04 20:58 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Apr 04, 2025 at 03:06:45PM -0500, Nathan Bossart wrote:
> I see the buildfarm failure and am working on a fix.

I pushed commit 8ec0aae to fix this.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-04 22:25                                                           ` Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-04 22:25 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Apr 04, 2025 at 03:58:53PM -0500, Nathan Bossart wrote:
> I pushed commit 8ec0aae to fix this.

And now I'm seeing cross-version test failures due to our use of WITH
ORDINALITY, which wasn't added until v9.4.  Looking into it...

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-04 23:32                                                             ` Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-04-04 23:32 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Apr 4, 2025 at 6:25 PM Nathan Bossart <[email protected]>
wrote:

> On Fri, Apr 04, 2025 at 03:58:53PM -0500, Nathan Bossart wrote:
> > I pushed commit 8ec0aae to fix this.
>
> And now I'm seeing cross-version test failures due to our use of WITH
> ORDINALITY, which wasn't added until v9.4.  Looking into it...
>
>
>
This patch shrinks the array size to 1 for versions < 9.4, which keeps the
modern code fairly elegant.


Attachments:

  [text/x-patch] v1-0001-Fall-back-to-single-attribute-stat-fetching-for-v.patch (3.1K, ../../CADkLM=dMXt_=eeZbe=Kzx1bTNihmOar-1pyBfoe6iOoEWXsU2Q@mail.gmail.com/3-v1-0001-Fall-back-to-single-attribute-stat-fetching-for-v.patch)
  download | inline diff:
From fe551ab55622f95d84ac4c4d79fba898c6b60057 Mon Sep 17 00:00:00 2001
From: Corey Huinker <[email protected]>
Date: Fri, 4 Apr 2025 19:30:00 -0400
Subject: [PATCH v1] Fall back to single attribute stat fetching for versions <
 9.4

Existing attribute statistics batch fetching query relies on the
existence of WITH ORDINALTIY, as well as multi-parameter unnest() calls.
Without those, we have no choice but to fall back to single relation
fetching.

Preserve the existing array building infrastructure, and drop the array
size to 1 for older versions.
---
 src/bin/pg_dump/pg_dump.c | 35 +++++++++++++++++++++++++++--------
 1 file changed, 27 insertions(+), 8 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 0e915432e77..b88188448b0 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10571,6 +10571,14 @@ fetchAttributeStats(Archive *fout)
 	PGresult   *res = NULL;
 	static TocEntry *te;
 	static bool restarted;
+	int			max_rels = MAX_ATTR_STATS_RELS;
+
+	/*
+	 * Versions prior to 9.4 lack the unnest() WITH ORDINALITY feature
+	 * that we need to keep the relation batches in order.
+	 */
+	if (fout->remoteVersion < 90400)
+		max_rels = 1;
 
 	/* If we're just starting, set our TOC pointer. */
 	if (!te)
@@ -10596,7 +10604,7 @@ fetchAttributeStats(Archive *fout)
 	 * This is perhaps not the sturdiest assumption, so we verify it matches
 	 * reality in dumpRelationStats_dumper().
 	 */
-	for (; te != AH->toc && count < MAX_ATTR_STATS_RELS; te = te->next)
+	for (; te != AH->toc && count < max_rels; te = te->next)
 	{
 		if ((te->reqs & REQ_STATS) != 0 &&
 			strcmp(te->desc, "STATISTICS DATA") == 0)
@@ -10709,14 +10717,25 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * This may not work for all versions.
+		 *
+		 * WITH ORDINALITY was introduced in 9.4, and multi-argument unnest()
+		 * was introduced in 9.3. Rather than create a bunch of corner-cases,
+		 * we simply fall back to fetching a single relation per call.
 		 */
-		appendPQExpBufferStr(query,
-							 "FROM pg_catalog.pg_stats s "
-							 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
-							 "ON s.schemaname = u.schemaname "
-							 "AND s.tablename = u.tablename "
-							 "WHERE s.tablename = ANY($2) "
-							 "ORDER BY u.ord, s.attname, s.inherited");
+		if (fout->remoteVersion >= 90400)
+			appendPQExpBufferStr(query,
+								 "FROM pg_catalog.pg_stats s "
+								 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
+								 "ON s.schemaname = u.schemaname "
+								 "AND s.tablename = u.tablename "
+								 "WHERE s.tablename = ANY($2) "
+								 "ORDER BY u.ord, s.attname, s.inherited");
+		else
+			appendPQExpBufferStr(query,
+								 "FROM pg_catalog.pg_stats s "
+								 "WHERE s.schemaname = ($1::text[])[1] "
+								 "AND s.tablename = ($2::text[])[1] "
+								 "ORDER BY s.attname, s.inherited");
 
 		ExecuteSqlStatement(fout, query->data);
 

base-commit: 0f43083d16f4be7c01efa80d05d0eef5e5ff69d3
-- 
2.49.0



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-04-05 02:06                                                               ` Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-04-05 02:06 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, Apr 04, 2025 at 07:32:48PM -0400, Corey Huinker wrote:
> This patch shrinks the array size to 1 for versions < 9.4, which keeps the
> modern code fairly elegant.

Committed.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-14 00:01                                                                 ` Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-05-14 00:01 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

We found a minor issue when testing statistics import with upgrading from
versions older than v14. (We have VACUUM and ANALYZE disabled)
3d351d916b20534f973eda760cde17d96545d4c4
<https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3d351d916b20534f973eda760cde17d96545d...;
changed
the default value for reltuples from 0 to -1. So when such tables are
imported they get the pg13 default of 0 which in pg18 is treated
as "vacuumed and seen to be empty" instead of "never yet vacuumed". The
planner then proceeds to pick seq scans even if there are indexes for these
tables.
This is a very narrow edge case and the next VACUUM or ANALYZE will fix it
but the perf of these tables immediately after the upgrade is considerably
affected.

Can we instead use -1 if the version is older than 14, and reltuples is 0?
This will have the unintended consequence of treating a truly empty table
as "never yet vacuumed", but that should be fine as empty tables are going
to be fast regardless of the plan picked.

PS: This is my first patch, so apologies for any issues with the patch.


On Fri, Apr 4, 2025 at 7:06 PM Nathan Bossart <[email protected]>
wrote:

> On Fri, Apr 04, 2025 at 07:32:48PM -0400, Corey Huinker wrote:
> > This patch shrinks the array size to 1 for versions < 9.4, which keeps
> the
> > modern code fairly elegant.
>
> Committed.
>
> --
> nathan
>
>
>


Attachments:

  [application/octet-stream] 0001-Stats-import-Fix-default-reltuples-on-versions-older.patch (1.2K, ../../CAAeiqZ0o2p4SX5_xPcuAbbsmXjg6MJLNuPYSLUjC=Wh-VeW64A@mail.gmail.com/3-0001-Stats-import-Fix-default-reltuples-on-versions-older.patch)
  download | inline diff:
From 043ff3784e19c62615a8faff3ae65966cb83e557 Mon Sep 17 00:00:00 2001
From: Hari Krishna Sunder <[email protected]>
Date: Tue, 13 May 2025 23:26:32 +0000
Subject: [PATCH] Stats import: Fix default reltuples on versions older than 14

---
 src/bin/pg_dump/pg_dump.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e2e7975b34e..e75f3ca4cab 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10924,7 +10924,10 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 	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);
+	if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
+		appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
+	else
+		appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
 	appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
 					  rsinfo->relallvisible);
 
-- 
2.26.0



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
@ 2025-05-14 15:53                                                                   ` Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-05-14 15:53 UTC (permalink / raw)
  To: Hari Krishna Sunder <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, May 13, 2025 at 05:01:02PM -0700, Hari Krishna Sunder wrote:
> We found a minor issue when testing statistics import with upgrading from
> versions older than v14. (We have VACUUM and ANALYZE disabled)
> 3d351d916b20534f973eda760cde17d96545d4c4
> <https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3d351d916b20534f973eda760cde17d96545d...;
> changed
> the default value for reltuples from 0 to -1. So when such tables are
> imported they get the pg13 default of 0 which in pg18 is treated
> as "vacuumed and seen to be empty" instead of "never yet vacuumed". The
> planner then proceeds to pick seq scans even if there are indexes for these
> tables.
> This is a very narrow edge case and the next VACUUM or ANALYZE will fix it
> but the perf of these tables immediately after the upgrade is considerably
> affected.

There was a similar report for vacuumdb's new --missing-stats-only option.
We fixed that in commit 9879105 by removing the check for reltuples != 0,
which means that --missing-stats-only will process empty tables.

> Can we instead use -1 if the version is older than 14, and reltuples is 0?
> This will have the unintended consequence of treating a truly empty table
> as "never yet vacuumed", but that should be fine as empty tables are going
> to be fast regardless of the plan picked.

I'm inclined to agree that we should do this.  Even if it's much more
likely that 0 means empty versus not-yet-processed, the one-time cost of
processing some empty tables doesn't sound too bad.  In any case, since
this only applies to upgrades from <v14, that trade-off should dissipate
over time.

> PS: This is my first patch, so apologies for any issues with the patch.

It needs a comment, but otherwise it looks generally reasonable to me after
a quick glance.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-14 20:30                                                                     ` Hari Krishna Sunder <[email protected]>
  2025-05-16 18:47                                                                       ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-05-14 20:30 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Thanks Nathan.
Here is the patch with a comment.

On Wed, May 14, 2025 at 8:53 AM Nathan Bossart <[email protected]>
wrote:

> On Tue, May 13, 2025 at 05:01:02PM -0700, Hari Krishna Sunder wrote:
> > We found a minor issue when testing statistics import with upgrading from
> > versions older than v14. (We have VACUUM and ANALYZE disabled)
> > 3d351d916b20534f973eda760cde17d96545d4c4
> > <
> https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3d351d916b20534f973eda760cde17d96545d...
> >
> > changed
> > the default value for reltuples from 0 to -1. So when such tables are
> > imported they get the pg13 default of 0 which in pg18 is treated
> > as "vacuumed and seen to be empty" instead of "never yet vacuumed". The
> > planner then proceeds to pick seq scans even if there are indexes for
> these
> > tables.
> > This is a very narrow edge case and the next VACUUM or ANALYZE will fix
> it
> > but the perf of these tables immediately after the upgrade is
> considerably
> > affected.
>
> There was a similar report for vacuumdb's new --missing-stats-only option.
> We fixed that in commit 9879105 by removing the check for reltuples != 0,
> which means that --missing-stats-only will process empty tables.
>
> > Can we instead use -1 if the version is older than 14, and reltuples is
> 0?
> > This will have the unintended consequence of treating a truly empty table
> > as "never yet vacuumed", but that should be fine as empty tables are
> going
> > to be fast regardless of the plan picked.
>
> I'm inclined to agree that we should do this.  Even if it's much more
> likely that 0 means empty versus not-yet-processed, the one-time cost of
> processing some empty tables doesn't sound too bad.  In any case, since
> this only applies to upgrades from <v14, that trade-off should dissipate
> over time.
>
> > PS: This is my first patch, so apologies for any issues with the patch.
>
> It needs a comment, but otherwise it looks generally reasonable to me after
> a quick glance.
>
> --
> nathan
>


Attachments:

  [application/octet-stream] 0001-Stats-import-Fix-default-reltuples-on-versions-older.patch (1.4K, ../../CAAeiqZ3BPCXziob2-Ldf15h0eS-0C6qbNoT3n5jiXEvMrjEW-w@mail.gmail.com/3-0001-Stats-import-Fix-default-reltuples-on-versions-older.patch)
  download | inline diff:
From f3024a4b50af63f61fb91a82e7b91c98f9bf882d Mon Sep 17 00:00:00 2001
From: Hari Krishna Sunder <[email protected]>
Date: Tue, 13 May 2025 23:26:32 +0000
Subject: [PATCH] Stats import: Fix default reltuples on versions older than 14

---
 src/bin/pg_dump/pg_dump.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e2e7975b34e..45548004240 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10924,7 +10924,17 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 	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);
+
+	/*
+	 * Before version 14, the default value for reltuples of tables that had not yet been ANALYZED
+	 * was 0. In version 14, the default value was changed to -1. Even if the table is empty, let's
+	 * just assume it has not yet been ANALYZED and set to -1.
+	 */
+	if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
+		appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
+	else
+		appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
+
 	appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
 					  rsinfo->relallvisible);
 
-- 
2.26.0



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
@ 2025-05-16 18:47                                                                       ` Hari Krishna Sunder <[email protected]>
  2025-05-19 01:52                                                                         ` Re: Statistics Import and Export Michael Paquier <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-05-16 18:47 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Gentle ping on this.

---
Hari Krishna Sunder

On Wed, May 14, 2025 at 1:30 PM Hari Krishna Sunder <[email protected]>
wrote:

> Thanks Nathan.
> Here is the patch with a comment.
>
> On Wed, May 14, 2025 at 8:53 AM Nathan Bossart <[email protected]>
> wrote:
>
>> On Tue, May 13, 2025 at 05:01:02PM -0700, Hari Krishna Sunder wrote:
>> > We found a minor issue when testing statistics import with upgrading
>> from
>> > versions older than v14. (We have VACUUM and ANALYZE disabled)
>> > 3d351d916b20534f973eda760cde17d96545d4c4
>> > <
>> https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3d351d916b20534f973eda760cde17d96545d...
>> >
>> > changed
>> > the default value for reltuples from 0 to -1. So when such tables are
>> > imported they get the pg13 default of 0 which in pg18 is treated
>> > as "vacuumed and seen to be empty" instead of "never yet vacuumed". The
>> > planner then proceeds to pick seq scans even if there are indexes for
>> these
>> > tables.
>> > This is a very narrow edge case and the next VACUUM or ANALYZE will fix
>> it
>> > but the perf of these tables immediately after the upgrade is
>> considerably
>> > affected.
>>
>> There was a similar report for vacuumdb's new --missing-stats-only option.
>> We fixed that in commit 9879105 by removing the check for reltuples != 0,
>> which means that --missing-stats-only will process empty tables.
>>
>> > Can we instead use -1 if the version is older than 14, and reltuples is
>> 0?
>> > This will have the unintended consequence of treating a truly empty
>> table
>> > as "never yet vacuumed", but that should be fine as empty tables are
>> going
>> > to be fast regardless of the plan picked.
>>
>> I'm inclined to agree that we should do this.  Even if it's much more
>> likely that 0 means empty versus not-yet-processed, the one-time cost of
>> processing some empty tables doesn't sound too bad.  In any case, since
>> this only applies to upgrades from <v14, that trade-off should dissipate
>> over time.
>>
>> > PS: This is my first patch, so apologies for any issues with the patch.
>>
>> It needs a comment, but otherwise it looks generally reasonable to me
>> after
>> a quick glance.
>>
>> --
>> nathan
>>
>


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-16 18:47                                                                       ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
@ 2025-05-19 01:52                                                                         ` Michael Paquier <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Michael Paquier @ 2025-05-19 01:52 UTC (permalink / raw)
  To: Hari Krishna Sunder <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Fri, May 16, 2025 at 11:47:12AM -0700, Hari Krishna Sunder wrote:
> Gentle ping on this.

Most of the major PostgreSQL developers were at pgconf.dev held in
Montreal last week, explaining a reduction in the activity of the
mailing lists.

Your initial report was on Monday the 14th, with this ping being on
Friday the 16th, both happening during the conference.  I suspect that
that there was just no time for folks of this thread to be able to
provide feedback for your patch, so please be a bit more patient.

Thanks!
--
Michael


Attachments:

  [application/pgp-signature] signature.asc (833B, ../../[email protected]/2-signature.asc)
  download

^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
@ 2025-05-19 16:51                                                                       ` Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-05-19 16:51 UTC (permalink / raw)
  To: Hari Krishna Sunder <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Wed, May 14, 2025 at 01:30:48PM -0700, Hari Krishna Sunder wrote:
> Here is the patch with a comment.

Thanks.

> On Wed, May 14, 2025 at 8:53 AM Nathan Bossart <[email protected]>
> wrote:
>> There was a similar report for vacuumdb's new --missing-stats-only option.
>> We fixed that in commit 9879105 by removing the check for reltuples != 0,
>> which means that --missing-stats-only will process empty tables.

I'm wondering if we should revert commit 9879105 if we take this change,
which solves the --missing-stats-only problem in a different way.  My
current thinking is that we should just leave it in place, if for no other
reason than analyzing some empty tables seems unlikely to cause too much
trouble.  Thoughts?

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-19 21:13                                                                         ` Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-05-19 21:13 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Sorry didn't know about the conference.

I think it would be better to revert 9879105 since there can be a
considerable number of true empty tables that we don’t need to process.

---
Hari Krishna Sunder


On Mon, May 19, 2025 at 9:51 AM Nathan Bossart <[email protected]>
wrote:

> On Wed, May 14, 2025 at 01:30:48PM -0700, Hari Krishna Sunder wrote:
> > Here is the patch with a comment.
>
> Thanks.
>
> > On Wed, May 14, 2025 at 8:53 AM Nathan Bossart <[email protected]
> >
> > wrote:
> >> There was a similar report for vacuumdb's new --missing-stats-only
> option.
> >> We fixed that in commit 9879105 by removing the check for reltuples !=
> 0,
> >> which means that --missing-stats-only will process empty tables.
>
> I'm wondering if we should revert commit 9879105 if we take this change,
> which solves the --missing-stats-only problem in a different way.  My
> current thinking is that we should just leave it in place, if for no other
> reason than analyzing some empty tables seems unlikely to cause too much
> trouble.  Thoughts?
>
> --
> nathan
>


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
@ 2025-05-19 21:31                                                                           ` Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-05-19 21:31 UTC (permalink / raw)
  To: Hari Krishna Sunder <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Mon, May 19, 2025 at 02:13:45PM -0700, Hari Krishna Sunder wrote:
> I think it would be better to revert 9879105 since there can be a
> considerable number of true empty tables that we don´t need to process.

I'm not sure that's a use-case we really need to optimize.  Even with
100,000 empty tables, "vacuumdb --analyze-only --missing-stats-only --jobs
64" completes in ~5.5 seconds on my laptop.  Plus, even if reltuples is 0,
there might actually be rows in the table, in which case analyzing it will
produce rows in pg_statistic.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-20 17:32                                                                             ` Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-05-20 17:32 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Ah ya, forgot that reltuples are not always accurate. This sounds
reasonable to me.

On Mon, May 19, 2025 at 2:32 PM Nathan Bossart <[email protected]>
wrote:

> On Mon, May 19, 2025 at 02:13:45PM -0700, Hari Krishna Sunder wrote:
> > I think it would be better to revert 9879105 since there can be a
> > considerable number of true empty tables that we don´t need to process.
>
> I'm not sure that's a use-case we really need to optimize.  Even with
> 100,000 empty tables, "vacuumdb --analyze-only --missing-stats-only --jobs
> 64" completes in ~5.5 seconds on my laptop.  Plus, even if reltuples is 0,
> there might actually be rows in the table, in which case analyzing it will
> produce rows in pg_statistic.
>
> --
> nathan
>


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
@ 2025-05-21 16:08                                                                               ` Nathan Bossart <[email protected]>
  2025-05-21 19:33                                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 21:14                                                                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Nathan Bossart @ 2025-05-21 16:08 UTC (permalink / raw)
  To: Hari Krishna Sunder <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Tue, May 20, 2025 at 10:32:39AM -0700, Hari Krishna Sunder wrote:
> Ah ya, forgot that reltuples are not always accurate. This sounds
> reasonable to me.

Cool.  Here is what I have staged for commit, which I am planning to do
shortly.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-21 19:33                                                                                 ` Hari Krishna Sunder <[email protected]>
  1 sibling, 0 replies; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-05-21 19:33 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Corey Huinker <[email protected]>; Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Looks good to me.


On Wed, May 21, 2025 at 9:08 AM Nathan Bossart <[email protected]>
wrote:

> On Tue, May 20, 2025 at 10:32:39AM -0700, Hari Krishna Sunder wrote:
> > Ah ya, forgot that reltuples are not always accurate. This sounds
> > reasonable to me.
>
> Cool.  Here is what I have staged for commit, which I am planning to do
> shortly.
>
> --
> nathan
>


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-21 21:14                                                                                 ` Jeff Davis <[email protected]>
  2025-05-21 21:29                                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-05-21 21:14 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; Hari Krishna Sunder <[email protected]>; +Cc: Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Wed, 2025-05-21 at 11:08 -0500, Nathan Bossart wrote:
> On Tue, May 20, 2025 at 10:32:39AM -0700, Hari Krishna Sunder wrote:
> > Ah ya, forgot that reltuples are not always accurate. This sounds
> > reasonable to me.
> 
> Cool.  Here is what I have staged for commit, which I am planning to
> do
> shortly.

Originally, one of the reasons we added a version field during dump is
so that some future version could reinterpret stats in older dump files
during import.

This patch is using a newer version of pg_dump to interpret stats from
older versions during export. That might be fine, but it would be good
to understand where the line is between things we should reinterpret
during export vs things we should reinterpret during import.

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-21 21:14                                                                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-05-21 21:29                                                                                   ` Nathan Bossart <[email protected]>
  2025-05-21 23:11                                                                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-05-21 23:53                                                                                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Nathan Bossart @ 2025-05-21 21:29 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Hari Krishna Sunder <[email protected]>; Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Wed, May 21, 2025 at 02:14:55PM -0700, Jeff Davis wrote:
> Originally, one of the reasons we added a version field during dump is
> so that some future version could reinterpret stats in older dump files
> during import.
> 
> This patch is using a newer version of pg_dump to interpret stats from
> older versions during export. That might be fine, but it would be good
> to understand where the line is between things we should reinterpret
> during export vs things we should reinterpret during import.

I don't know precisely where that line might be, but in this case, the
dumped stats have no hope of restoring into anything older than v18 (since
the stats import functions won't exist), which is well past the point where
we started using -1 for reltuples.  If we could dump the stats from v13 and
restore them into v13, then I think there would be a reasonably strong
argument for dumping it as-is and reinterpreting as necessary during
import.  But I see no particular benefit from moving the complexity to the
import side here.

Does that seem like a reasonable position to you?  Is there anything else
we should consider?

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-21 21:14                                                                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-21 21:29                                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-21 23:11                                                                                     ` Corey Huinker <[email protected]>
  1 sibling, 0 replies; 88+ messages in thread

From: Corey Huinker @ 2025-05-21 23:11 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Jeff Davis <[email protected]>; Hari Krishna Sunder <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> I don't know precisely where that line might be, but in this case, the
> dumped stats have no hope of restoring into anything older than v18 (since
> the stats import functions won't exist), which is well past the point where
> we started using -1 for reltuples.  If we could dump the stats from v13 and
> restore them into v13, then I think there would be a reasonably strong
> argument for dumping it as-is and reinterpreting as necessary during
> import.  But I see no particular benefit from moving the complexity to the
> import side here.
>

Definitely keep complexity on the export-side.

Mapping reltuples 0 -> -1 if system version < 14 like the original patch
did makes the most sense to me. That allows vacuumdb to go back to ignoring
tables that are seemingly empty while still vacuuming the tables that had
the pre-14 suspicious 0 reltuples value.



>
> Does that seem like a reasonable position to you?  Is there anything else
> we should consider?
>

Automatically vacuuming tables that purport to be empty may not take much
time, but it may alarm users using --missing-only, wondering why so many
tables didn't get stats imported, especially if we introduce a --dry-run
parameter which would answer for a user the question "what tables does
vacuumdb think are missing statistics?".


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-21 21:14                                                                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-21 21:29                                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-21 23:53                                                                                     ` Jeff Davis <[email protected]>
  2025-05-22 15:25                                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-05-21 23:53 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Hari Krishna Sunder <[email protected]>; Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Wed, 2025-05-21 at 16:29 -0500, Nathan Bossart wrote:
> I don't know precisely where that line might be, but in this case,
> the
> dumped stats have no hope of restoring into anything older than
> v18... But I see no particular benefit from moving the complexity
> to the
> import side here.

That's fine with me. Perhaps we should just say that pre-18 behavior
differences can be fixed up during export, and post-18 behavior
differences are fixed up during import?

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-21 21:14                                                                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-21 21:29                                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-21 23:53                                                                                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-05-22 15:25                                                                                       ` Nathan Bossart <[email protected]>
  2025-05-22 19:25                                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-05-22 15:25 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Hari Krishna Sunder <[email protected]>; Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Wed, May 21, 2025 at 04:53:17PM -0700, Jeff Davis wrote:
> On Wed, 2025-05-21 at 16:29 -0500, Nathan Bossart wrote:
>> I don't know precisely where that line might be, but in this case,
>> the
>> dumped stats have no hope of restoring into anything older than
>> v18... But I see no particular benefit from moving the complexity
>> to the
>> import side here.
> 
> That's fine with me. Perhaps we should just say that pre-18 behavior
> differences can be fixed up during export, and post-18 behavior
> differences are fixed up during import?

WFM.  I've committed the patch.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
  2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-21 21:14                                                                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-21 21:29                                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-21 23:53                                                                                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-22 15:25                                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-22 19:25                                                                                         ` Hari Krishna Sunder <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Hari Krishna Sunder @ 2025-05-22 19:25 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Jeff Davis <[email protected]>; Corey Huinker <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Thanks for the help. This has unblocked us!

On Thu, May 22, 2025 at 8:25 AM Nathan Bossart <[email protected]>
wrote:

> On Wed, May 21, 2025 at 04:53:17PM -0700, Jeff Davis wrote:
> > On Wed, 2025-05-21 at 16:29 -0500, Nathan Bossart wrote:
> >> I don't know precisely where that line might be, but in this case,
> >> the
> >> dumped stats have no hope of restoring into anything older than
> >> v18... But I see no particular benefit from moving the complexity
> >> to the
> >> import side here.
> >
> > That's fine with me. Perhaps we should just say that pre-18 behavior
> > differences can be fixed up during export, and post-18 behavior
> > differences are fixed up during import?
>
> WFM.  I've committed the patch.
>
> --
> nathan
>


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-04-03 02:38                                               ` Nathan Bossart <[email protected]>
  1 sibling, 0 replies; 88+ messages in thread

From: Nathan Bossart @ 2025-04-03 02:38 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Wed, Apr 02, 2025 at 10:34:58PM -0400, Corey Huinker wrote:
>>
>> > Also, why do we need the clause "WHERE s.tablename = ANY($2)"? Isn't
>> > that already implied by "JOIN unnest($1, $2) ... s.tablename =
>> > u.tablename"?
>>
>> Good question.  Corey, do you recall why this was needed?
>>
> 
> In my patch, that SQL statement came with the comment:
> 
> + /*
> + * The results must be in the order of relations supplied in the
> + * parameters to ensure that they are in sync with a walk of the TOC.
> + *
> + * The redundant (and incomplete) filter clause on s.tablename = ANY(...)
> + * is a way to lead the query into using the index
> + * pg_class_relname_nsp_index which in turn allows the planner to avoid an
> + * expensive full scan of pg_stats.
> + *
> + * We may need to adjust this query for versions that are not so easily
> + * led.
> + */

Thanks.  I included that in the latest patch set.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-16 20:33                 ` Nathan Bossart <[email protected]>
  2025-03-16 21:32                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-03-16 20:33 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

Thanks for working on this, Corey.

On Fri, Mar 14, 2025 at 04:03:16PM -0400, Corey Huinker wrote:
> 0003 -
> 
> Storing the restore function calls in the archive entry hogged a lot of
> memory and made people nervous. This introduces a new function pointer that
> generates those restore SQL calls right before they're written to disk,
> thus reducing the memory load from "stats for every object to be dumped" to
> just one object. Thanks to Nathan for diagnosing some weird quirks with
> various formats.
> 
> 0004 -
> 
> This replaces the query in the prepared statement with one that batches
> them 100 relations at a time, and then maintains that result set until it
> is consumed. It seems to have obvious speedups.

I've been doing a variety of tests with my toy database of 100K relations
[0], and I'm seeing around 20% less memory usage.  That's still 20% more
than without stats, but that's still a pretty nice improvement.

I'd propose two small changes to the design:

* I tested a variety of batch sizes, and to my suprise, I saw the best
  results with around 64 relations per batch.  I imagine the absolute best
  batch size will vary greatly depending on the workload.  It might also
  depend on work_mem and friends.

* The custom format actually does two WriteToc() calls, and since these
  patches move the queries to this part of pg_dump, it means we'll run all
  the queries twice.  The comments around this code suggest that the second
  pass isn't strictly necessary and that it is really only useful for
  data/parallel restore, so we could probably skip it for no-data dumps.

With those two changes, a pg_upgrade-style dump of my test database goes
from ~21.6 seconds without these patches to ~11.2 seconds with them.  For
reference, the same dump without stats takes ~7 seconds on HEAD.

[0] https://postgr.es/m/Z9R9-mFbxukqKmg4%40nathan

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 20:33                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-03-16 21:32                   ` Corey Huinker <[email protected]>
  2025-03-17 14:23                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-16 21:32 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

>
> * The custom format actually does two WriteToc() calls, and since these
>   patches move the queries to this part of pg_dump, it means we'll run all
>   the queries twice.  The comments around this code suggest that the second
>   pass isn't strictly necessary and that it is really only useful for
>   data/parallel restore, so we could probably skip it for no-data dumps.
>

Is there any reason we couldn't have stats objects remove themselves from
the list after completion?


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 20:33                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-03-16 21:32                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-17 14:23                     ` Nathan Bossart <[email protected]>
  2025-03-17 23:24                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-03-17 14:23 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Sun, Mar 16, 2025 at 05:32:15PM -0400, Corey Huinker wrote:
>>
>> * The custom format actually does two WriteToc() calls, and since these
>>   patches move the queries to this part of pg_dump, it means we'll run all
>>   the queries twice.  The comments around this code suggest that the second
>>   pass isn't strictly necessary and that it is really only useful for
>>   data/parallel restore, so we could probably skip it for no-data dumps.
>>
> 
> Is there any reason we couldn't have stats objects remove themselves from
> the list after completion?

I'm assuming that writing a completely different TOC on the second pass
would corrupt the dump file.  Perhaps we could teach it to skip stats
entries on the second pass or something, but I'm not too wild about adding
to the list of invasive changes we're making last-minute for v18.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 20:33                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-03-16 21:32                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-17 14:23                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-03-17 23:24                       ` Corey Huinker <[email protected]>
  2025-03-18 01:01                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Corey Huinker @ 2025-03-17 23:24 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Mon, Mar 17, 2025 at 10:24 AM Nathan Bossart <[email protected]>
wrote:

> On Sun, Mar 16, 2025 at 05:32:15PM -0400, Corey Huinker wrote:
> >>
> >> * The custom format actually does two WriteToc() calls, and since these
> >>   patches move the queries to this part of pg_dump, it means we'll run
> all
> >>   the queries twice.  The comments around this code suggest that the
> second
> >>   pass isn't strictly necessary and that it is really only useful for
> >>   data/parallel restore, so we could probably skip it for no-data dumps.
> >>
> >
> > Is there any reason we couldn't have stats objects remove themselves from
> > the list after completion?
>
> I'm assuming that writing a completely different TOC on the second pass
> would corrupt the dump file.  Perhaps we could teach it to skip stats
> entries on the second pass or something, but I'm not too wild about adding
> to the list of invasive changes we're making last-minute for v18.


I'm confused, are they needed in both places? If so, would it make sense to
write out each stat entry to a file and then re-read the file on the second
pass, or maybe do a \i filename in the sql script?

Not suggesting we do any of this for v18, but when I hear about doing
something twice when that thing was painful the first time, I look for ways
to avoid doing it, or set pan_is_hot = true for the next person.


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
  2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-16 20:33                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-03-16 21:32                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
  2025-03-17 14:23                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-03-17 23:24                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
@ 2025-03-18 01:01                         ` Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Nathan Bossart @ 2025-03-18 01:01 UTC (permalink / raw)
  To: Corey Huinker <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Treat <[email protected]>; Robert Haas <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; jian he <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]

On Mon, Mar 17, 2025 at 07:24:46PM -0400, Corey Huinker wrote:
> On Mon, Mar 17, 2025 at 10:24 AM Nathan Bossart <[email protected]>
> wrote:
>> I'm assuming that writing a completely different TOC on the second pass
>> would corrupt the dump file.  Perhaps we could teach it to skip stats
>> entries on the second pass or something, but I'm not too wild about adding
>> to the list of invasive changes we're making last-minute for v18.
> 
> I'm confused, are they needed in both places?

AFAICT yes.  The second pass rewrites the TOC to udpate the data offset
information.  If we wrote a different TOC the second time around, then the
dump file would be broken, right?

		/*
		 * If possible, re-write the TOC in order to update the data offset
		 * information.  This is not essential, as pg_restore can cope in most
		 * cases without it; but it can make pg_restore significantly faster
		 * in some situations (especially parallel restore).
		 */
		if (ctx->hasSeek &&
			fseeko(AH->FH, tpos, SEEK_SET) == 0)
			WriteToc(AH);

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
@ 2025-03-31 17:39 ` Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2 siblings, 1 reply; 88+ messages in thread

From: Robert Haas @ 2025-03-31 17:39 UTC (permalink / raw)
  To: Greg Sabino Mullane <[email protected]>; +Cc: Jeff Davis <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Thu, Feb 27, 2025 at 10:43 PM Greg Sabino Mullane <[email protected]> wrote:
> I know I'm coming late to this, but I would like us to rethink having statistics dumped by default.

+1. I think I said this before, but I don't think it's correct to
regard the statistics as part of the database. It's great for
pg_upgrade to preserve them, but I think doing so for a regular dump
should be opt-in.

-- 
Robert Haas
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
@ 2025-03-31 22:04   ` Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-03-31 22:04 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; Greg Sabino Mullane <[email protected]>; +Cc: Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Mon, 2025-03-31 at 13:39 -0400, Robert Haas wrote:
> +1. I think I said this before, but I don't think it's correct to
> regard the statistics as part of the database. It's great for
> pg_upgrade to preserve them, but I think doing so for a regular dump
> should be opt-in.

I'm confused about the timing of this message -- we already have an
Open Item for 18 to make this decision. After commit bde2fb797a,
changing the default is a one-line change, so there's no technical
problem.

I thought the general plan was to decide during beta. Would you like to
make the decision now for some reason?

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-04-01 20:24     ` Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-04-01 20:24 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Tue, 2025-04-01 at 09:37 -0400, Robert Haas wrote:
> I don't think I was aware of the open item; I was just catching up on
> email.

I lean towards making it opt-in for pg_dump and opt-out for pg_upgrade.
But I think we should leave open the possibility for changing the
default to opt-out for pg_dump in the future.

My reasoning for pg_dump is that releasing with stats as opt-in doesn't
put us in a worse position for making it opt-out later, so long as we
have the right set of both positive and negative options. It may even
be a better position because people have time to make their scripts
future proof by using the right combination of options.

> But I also don't really see the value of waiting until beta to
> make this decision. I seriously doubt that my opinion is going to
> change. Maybe other people's will, though: I can only speak for
> myself.

I don't think the last week before feature freeze, deep in a 400-email
thread is the best way to make decisions like this. Let's at least have
a focused thread on this topic and see if we can solicit opinions from
both sides.

Also, waiting to see if the performance improvements make it in, or
waiting for beta reports, may yield some new information that could
change minds.

Mid-beta might be too long, but let's wait for the final CF to settle
and give people the chance to respond to a top-level thread?

Regards,
	Jeff Davis






^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-05-10 19:51       ` Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
  0 siblings, 1 reply; 88+ messages in thread

From: Greg Sabino Mullane @ 2025-05-10 19:51 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; +Cc: Jeff Davis <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Tue, Apr 1, 2025 at 10:24 PM Robert Haas <[email protected]> wrote:

> On Tue, Apr 1, 2025 at 4:24 PM Jeff Davis <[email protected]> wrote:
> > On Tue, 2025-04-01 at 09:37 -0400, Robert Haas wrote:
> > > I don't think I was aware of the open item; I was just catching up on
> > > email.
> >
> > I lean towards making it opt-in for pg_dump and opt-out for pg_upgrade.
>
> Big +1.
>

I may have missed something (we seem to have a lot of threads for this
subject), but we are in beta and both pg_dump and pg_upgrade seem to be
opt-out? I still object strongly to this;  pg_dump is meant to be a
canonical representation of the schema and data. Adding metadata that can
change from dump to dump seems wrong, and should be opt-in. I've not been
convinced otherwise why stats should be output by default.

To be clear, I 100% want it to be the default for pg_upgrade.

Maybe we are just leaving it enabled to see if anyone complains in beta,
but I don't want us to forget about it. :)

Cheers,
Greg

--
Crunchy Data - https://www.crunchydata.com
Enterprise Postgres Software Products & Tech Support


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
@ 2025-05-22 14:20         ` Robert Haas <[email protected]>
  2025-05-22 14:30           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-22 18:52           ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Robert Haas @ 2025-05-22 14:20 UTC (permalink / raw)
  To: Greg Sabino Mullane <[email protected]>; +Cc: Jeff Davis <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Sat, May 10, 2025 at 3:51 PM Greg Sabino Mullane <[email protected]> wrote:
> I may have missed something (we seem to have a lot of threads for this subject), but we are in beta and both pg_dump and pg_upgrade seem to be opt-out? I still object strongly to this;  pg_dump is meant to be a canonical representation of the schema and data. Adding metadata that can change from dump to dump seems wrong, and should be opt-in. I've not been convinced otherwise why stats should be output by default.
>
> To be clear, I 100% want it to be the default for pg_upgrade.
>
> Maybe we are just leaving it enabled to see if anyone complains in beta, but I don't want us to forget about it. :)

Yeah. This could use comments from a few more people, but I really
hope we don't ship the final release this way. We do have a "Enable
statistics in pg_dump by default" item in the open items list under
"Decisions to Recheck Mid-Beta", but that's arguably now. It also sort
of looks like we might have a consensus anyway: Jeff said "I lean
towards making it opt-in for pg_dump and opt-out for pg_upgrade" and I
agree with that and it seems you do, too. So perhaps Jeff should make
it so?

-- 
Robert Haas
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
@ 2025-05-22 14:30           ` Nathan Bossart <[email protected]>
  2025-05-22 14:53             ` Re: Statistics Import and Export Tom Lane <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-05-22 14:30 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Jeff Davis <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Thu, May 22, 2025 at 10:20:16AM -0400, Robert Haas wrote:
> It also sort
> of looks like we might have a consensus anyway: Jeff said "I lean
> towards making it opt-in for pg_dump and opt-out for pg_upgrade" and I
> agree with that and it seems you do, too. So perhaps Jeff should make
> it so?

+1, I think we should go ahead and do this.  If Jeff can't get to it, I'm
happy to pick it up in the next week or so.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-05-22 14:30           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-22 14:53             ` Tom Lane <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Tom Lane @ 2025-05-22 14:53 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Robert Haas <[email protected]>; Greg Sabino Mullane <[email protected]>; Jeff Davis <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Michael Paquier <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

Nathan Bossart <[email protected]> writes:
> On Thu, May 22, 2025 at 10:20:16AM -0400, Robert Haas wrote:
>> It also sort
>> of looks like we might have a consensus anyway: Jeff said "I lean
>> towards making it opt-in for pg_dump and opt-out for pg_upgrade" and I
>> agree with that and it seems you do, too. So perhaps Jeff should make
>> it so?

> +1, I think we should go ahead and do this.  If Jeff can't get to it, I'm
> happy to pick it up in the next week or so.

Works for me, too.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
@ 2025-05-22 18:52           ` Jeff Davis <[email protected]>
  2025-05-22 19:29             ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Jeff Davis @ 2025-05-22 18:52 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; Greg Sabino Mullane <[email protected]>; +Cc: Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Thu, 2025-05-22 at 10:20 -0400, Robert Haas wrote:
> Yeah. This could use comments from a few more people, but I really
> hope we don't ship the final release this way. We do have a "Enable
> statistics in pg_dump by default" item in the open items list under
> "Decisions to Recheck Mid-Beta", but that's arguably now. It also
> sort
> of looks like we might have a consensus anyway: Jeff said "I lean
> towards making it opt-in for pg_dump and opt-out for pg_upgrade" and
> I
> agree with that and it seems you do, too. So perhaps Jeff should make
> it so?

Patch attached.

A couple minor points:

 * The default for pg_restore is --no-statistics. That could cause a
minor surprise if the user specifies --with-statistics for pg_dump and
not for pg_restore. An argument could be made that "if the stats are
there, restore them", and I don't have a strong opinion about this
point, but defaulting to --no-statistics seems more consistent with
pg_dump.

 * I added --with-statistics to most of the pg_dump tests. We can be
more judicious about which tests exercise statistics as a separate
commit, but I didn't want to change the test results as a part of this
commit.

Regards,
	Jeff Davis



Attachments:

  [text/x-patch] v1-0001-Change-defaults-for-statistics-export.patch (15.1K, ../../[email protected]/2-v1-0001-Change-defaults-for-statistics-export.patch)
  download | inline diff:
From b76cb91441e2eefe278249e23fcd703d27a85a06 Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Thu, 22 May 2025 11:03:03 -0700
Subject: [PATCH v1] Change defaults for statistics export.

Set the default behavior of pg_dump, pg_dumpall, and pg_restore to be
--no-statistics. Leave the default for pg_upgrade to be
--with-statistics.

Discussion: https://postgr.es/m/CA+TgmoZ9=RnWcCOZiKYYjZs_AW1P4QXCw--h4dOLLHuf1Omung@mail.gmail.com
---
 src/bin/pg_dump/pg_backup_archiver.c |  4 +-
 src/bin/pg_dump/t/002_pg_dump.pl     | 59 ++++++++++++++++++++++++++++
 src/bin/pg_upgrade/dump.c            |  2 +-
 src/bin/pg_upgrade/pg_upgrade.c      |  6 ++-
 4 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index afa42337b11..a66d88bbc51 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -152,7 +152,7 @@ InitDumpOptions(DumpOptions *opts)
 	opts->dumpSections = DUMP_UNSECTIONED;
 	opts->dumpSchema = true;
 	opts->dumpData = true;
-	opts->dumpStatistics = true;
+	opts->dumpStatistics = false;
 }
 
 /*
@@ -1101,7 +1101,7 @@ NewRestoreOptions(void)
 	opts->compression_spec.level = 0;
 	opts->dumpSchema = true;
 	opts->dumpData = true;
-	opts->dumpStatistics = true;
+	opts->dumpStatistics = false;
 
 	return opts;
 }
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index cf34f71ea11..386e21e0c59 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -68,6 +68,7 @@ my %pgdump_runs = (
 			'--no-data',
 			'--sequence-data',
 			'--binary-upgrade',
+			'--with-statistics',
 			'--dbname' => 'postgres',    # alternative way to specify database
 		],
 		restore_cmd => [
@@ -75,6 +76,7 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--verbose',
 			'--file' => "$tempdir/binary_upgrade.sql",
+			'--with-statistics',
 			"$tempdir/binary_upgrade.dump",
 		],
 	},
@@ -88,11 +90,13 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--compress' => '1',
 			'--file' => "$tempdir/compression_gzip_custom.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/compression_gzip_custom.sql",
+			'--with-statistics',
 			"$tempdir/compression_gzip_custom.dump",
 		],
 		command_like => {
@@ -115,6 +119,7 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--compress' => 'gzip:1',
 			'--file' => "$tempdir/compression_gzip_dir",
+			'--with-statistics',
 			'postgres',
 		],
 		# Give coverage for manually compressed blobs.toc files during
@@ -132,6 +137,7 @@ my %pgdump_runs = (
 			'pg_restore',
 			'--jobs' => '2',
 			'--file' => "$tempdir/compression_gzip_dir.sql",
+			'--with-statistics',
 			"$tempdir/compression_gzip_dir",
 		],
 	},
@@ -144,6 +150,7 @@ my %pgdump_runs = (
 			'--format' => 'plain',
 			'--compress' => '1',
 			'--file' => "$tempdir/compression_gzip_plain.sql.gz",
+			'--with-statistics',
 			'postgres',
 		],
 		# Decompress the generated file to run through the tests.
@@ -162,11 +169,13 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--compress' => 'lz4',
 			'--file' => "$tempdir/compression_lz4_custom.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/compression_lz4_custom.sql",
+			'--with-statistics',
 			"$tempdir/compression_lz4_custom.dump",
 		],
 		command_like => {
@@ -189,6 +198,7 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--compress' => 'lz4:1',
 			'--file' => "$tempdir/compression_lz4_dir",
+			'--with-statistics',
 			'postgres',
 		],
 		# Verify that data files were compressed
@@ -200,6 +210,7 @@ my %pgdump_runs = (
 			'pg_restore',
 			'--jobs' => '2',
 			'--file' => "$tempdir/compression_lz4_dir.sql",
+			'--with-statistics',
 			"$tempdir/compression_lz4_dir",
 		],
 	},
@@ -212,6 +223,7 @@ my %pgdump_runs = (
 			'--format' => 'plain',
 			'--compress' => 'lz4',
 			'--file' => "$tempdir/compression_lz4_plain.sql.lz4",
+			'--with-statistics',
 			'postgres',
 		],
 		# Decompress the generated file to run through the tests.
@@ -233,11 +245,13 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--compress' => 'zstd',
 			'--file' => "$tempdir/compression_zstd_custom.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/compression_zstd_custom.sql",
+			'--with-statistics',
 			"$tempdir/compression_zstd_custom.dump",
 		],
 		command_like => {
@@ -259,6 +273,7 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--compress' => 'zstd:1',
 			'--file' => "$tempdir/compression_zstd_dir",
+			'--with-statistics',
 			'postgres',
 		],
 		# Give coverage for manually compressed blobs.toc files during
@@ -279,6 +294,7 @@ my %pgdump_runs = (
 			'pg_restore',
 			'--jobs' => '2',
 			'--file' => "$tempdir/compression_zstd_dir.sql",
+			'--with-statistics',
 			"$tempdir/compression_zstd_dir",
 		],
 	},
@@ -292,6 +308,7 @@ my %pgdump_runs = (
 			'--format' => 'plain',
 			'--compress' => 'zstd:long',
 			'--file' => "$tempdir/compression_zstd_plain.sql.zst",
+			'--with-statistics',
 			'postgres',
 		],
 		# Decompress the generated file to run through the tests.
@@ -310,6 +327,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/clean.sql",
 			'--clean',
+			'--with-statistics',
 			'--dbname' => 'postgres',    # alternative way to specify database
 		],
 	},
@@ -320,6 +338,7 @@ my %pgdump_runs = (
 			'--clean',
 			'--if-exists',
 			'--encoding' => 'UTF8',      # no-op, just for testing
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -338,6 +357,7 @@ my %pgdump_runs = (
 			'--create',
 			'--no-reconnect',    # no-op, just for testing
 			'--verbose',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -356,6 +376,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/defaults.sql",
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -364,6 +385,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/defaults_no_public.sql",
+			'--with-statistics',
 			'regress_pg_dump_test',
 		],
 	},
@@ -373,6 +395,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--clean',
 			'--file' => "$tempdir/defaults_no_public_clean.sql",
+			'--with-statistics',
 			'regress_pg_dump_test',
 		],
 	},
@@ -381,6 +404,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/defaults_public_owner.sql",
+			'--with-statistics',
 			'regress_public_owner',
 		],
 	},
@@ -395,12 +419,14 @@ my %pgdump_runs = (
 			'pg_dump',
 			'--format' => 'custom',
 			'--file' => "$tempdir/defaults_custom_format.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--format' => 'custom',
 			'--file' => "$tempdir/defaults_custom_format.sql",
+			'--with-statistics',
 			"$tempdir/defaults_custom_format.dump",
 		],
 		command_like => {
@@ -425,12 +451,14 @@ my %pgdump_runs = (
 			'pg_dump',
 			'--format' => 'directory',
 			'--file' => "$tempdir/defaults_dir_format",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--format' => 'directory',
 			'--file' => "$tempdir/defaults_dir_format.sql",
+			'--with-statistics',
 			"$tempdir/defaults_dir_format",
 		],
 		command_like => {
@@ -456,11 +484,13 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--jobs' => 2,
 			'--file' => "$tempdir/defaults_parallel",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/defaults_parallel.sql",
+			'--with-statistics',
 			"$tempdir/defaults_parallel",
 		],
 	},
@@ -472,12 +502,14 @@ my %pgdump_runs = (
 			'pg_dump',
 			'--format' => 'tar',
 			'--file' => "$tempdir/defaults_tar_format.tar",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--format' => 'tar',
 			'--file' => "$tempdir/defaults_tar_format.sql",
+			'--with-statistics',
 			"$tempdir/defaults_tar_format.tar",
 		],
 	},
@@ -486,6 +518,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/exclude_dump_test_schema.sql",
 			'--exclude-schema' => 'dump_test',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -494,6 +527,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/exclude_test_table.sql",
 			'--exclude-table' => 'dump_test.test_table',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -502,6 +536,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/exclude_measurement.sql",
 			'--exclude-table-and-children' => 'dump_test.measurement',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -511,6 +546,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/exclude_measurement_data.sql",
 			'--exclude-table-data-and-children' => 'dump_test.measurement',
 			'--no-unlogged-table-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -520,6 +556,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/exclude_test_table_data.sql",
 			'--exclude-table-data' => 'dump_test.test_table',
 			'--no-unlogged-table-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -538,6 +575,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/pg_dumpall_globals.sql",
 			'--globals-only',
 			'--no-sync',
+			'--with-statistics',
 		],
 	},
 	pg_dumpall_globals_clean => {
@@ -547,12 +585,14 @@ my %pgdump_runs = (
 			'--globals-only',
 			'--clean',
 			'--no-sync',
+			'--with-statistics',
 		],
 	},
 	pg_dumpall_dbprivs => {
 		dump_cmd => [
 			'pg_dumpall', '--no-sync',
 			'--file' => "$tempdir/pg_dumpall_dbprivs.sql",
+			'--with-statistics',
 		],
 	},
 	pg_dumpall_exclude => {
@@ -562,6 +602,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/pg_dumpall_exclude.sql",
 			'--exclude-database' => '*dump_test*',
 			'--no-sync',
+			'--with-statistics',
 		],
 	},
 	no_toast_compression => {
@@ -569,6 +610,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_toast_compression.sql",
 			'--no-toast-compression',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -577,6 +619,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_large_objects.sql",
 			'--no-large-objects',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -585,6 +628,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_policies.sql",
 			'--no-policies',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -593,6 +637,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_privs.sql",
 			'--no-privileges',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -601,6 +646,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_owner.sql",
 			'--no-owner',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -609,6 +655,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_table_access_method.sql",
 			'--no-table-access-method',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -617,6 +664,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/only_dump_test_schema.sql",
 			'--schema' => 'dump_test',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -627,6 +675,7 @@ my %pgdump_runs = (
 			'--table' => 'dump_test.test_table',
 			'--lock-wait-timeout' =>
 			  (1000 * $PostgreSQL::Test::Utils::timeout_default),
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -637,6 +686,7 @@ my %pgdump_runs = (
 			'--table-and-children' => 'dump_test.measurement',
 			'--lock-wait-timeout' =>
 			  (1000 * $PostgreSQL::Test::Utils::timeout_default),
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -646,6 +696,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/role.sql",
 			'--role' => 'regress_dump_test_role',
 			'--schema' => 'dump_test_second_schema',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -658,11 +709,13 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/role_parallel",
 			'--role' => 'regress_dump_test_role',
 			'--schema' => 'dump_test_second_schema',
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/role_parallel.sql",
+			'--with-statistics',
 			"$tempdir/role_parallel",
 		],
 	},
@@ -691,6 +744,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/section_pre_data.sql",
 			'--section' => 'pre-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -699,6 +753,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/section_data.sql",
 			'--section' => 'data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -707,6 +762,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/section_post_data.sql",
 			'--section' => 'post-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -717,6 +773,7 @@ my %pgdump_runs = (
 			'--schema' => 'dump_test',
 			'--large-objects',
 			'--no-large-objects',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -732,6 +789,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			"--file=$tempdir/no_data_no_schema.sql", '--no-data',
 			'--no-schema', 'postgres',
+			'--with-statistics',
 		],
 	},
 	statistics_only => {
@@ -752,6 +810,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			"--file=$tempdir/no_schema.sql", '--no-schema',
+			'--with-statistics',
 			'postgres',
 		],
 	},);
diff --git a/src/bin/pg_upgrade/dump.c b/src/bin/pg_upgrade/dump.c
index 23cb08e8347..183f08ce1e8 100644
--- a/src/bin/pg_upgrade/dump.c
+++ b/src/bin/pg_upgrade/dump.c
@@ -58,7 +58,7 @@ generate_old_dump(void)
 						   (user_opts.transfer_mode == TRANSFER_MODE_SWAP) ?
 						   "" : "--sequence-data",
 						   log_opts.verbose ? "--verbose" : "",
-						   user_opts.do_statistics ? "" : "--no-statistics",
+						   user_opts.do_statistics ? "--with-statistics" : "--no-statistics",
 						   log_opts.dumpdir,
 						   sql_file_name, escaped_connstr.data);
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 536e49d2616..81a394f249d 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -618,12 +618,13 @@ create_new_objects(void)
 				  NULL,
 				  true,
 				  true,
-				  "\"%s/pg_restore\" %s %s --exit-on-error --verbose "
+				  "\"%s/pg_restore\" %s %s %s --exit-on-error --verbose "
 				  "--transaction-size=%d "
 				  "--dbname postgres \"%s/%s\"",
 				  new_cluster.bindir,
 				  cluster_conn_opts(&new_cluster),
 				  create_opts,
+				  user_opts.do_statistics ? "--with-statistics" : "--no-statistics",
 				  RESTORE_TRANSACTION_SIZE,
 				  log_opts.dumpdir,
 				  sql_file_name);
@@ -672,12 +673,13 @@ create_new_objects(void)
 
 		parallel_exec_prog(log_file_name,
 						   NULL,
-						   "\"%s/pg_restore\" %s %s --exit-on-error --verbose "
+						   "\"%s/pg_restore\" %s %s %s --exit-on-error --verbose "
 						   "--transaction-size=%d "
 						   "--dbname template1 \"%s/%s\"",
 						   new_cluster.bindir,
 						   cluster_conn_opts(&new_cluster),
 						   create_opts,
+						   user_opts.do_statistics ? "--with-statistics" : "--no-statistics",
 						   txn_size,
 						   log_opts.dumpdir,
 						   sql_file_name);
-- 
2.43.0



^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-05-22 18:52           ` Re: Statistics Import and Export Jeff Davis <[email protected]>
@ 2025-05-22 19:29             ` Greg Sabino Mullane <[email protected]>
  2025-05-22 19:36               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
  2025-05-22 19:41               ` Re: Statistics Import and Export Tom Lane <[email protected]>
  0 siblings, 2 replies; 88+ messages in thread

From: Greg Sabino Mullane @ 2025-05-22 19:29 UTC (permalink / raw)
  To: Jeff Davis <[email protected]>; +Cc: Robert Haas <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Thu, May 22, 2025 at 2:52 PM Jeff Davis <[email protected]> wrote:

>  * The default for pg_restore is --no-statistics. That could cause a minor
> surprise if the user specifies --with-statistics for pg_dump and
> not for pg_restore. An argument could be made that "if the stats are
> there, restore them", and I don't have a strong opinion about this point,
> but defaulting to --no-statistics seems more consistent with pg_dump.
>

Hm...somewhat to my own surprise, I don't like this. If it's in the dump,
restore it.

Cheers,
Greg

--
Crunchy Data - https://www.crunchydata.com
Enterprise Postgres Software Products & Tech Support


^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-05-22 18:52           ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-22 19:29             ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
@ 2025-05-22 19:36               ` Nathan Bossart <[email protected]>
  2025-05-22 19:36                 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Nathan Bossart @ 2025-05-22 19:36 UTC (permalink / raw)
  To: Greg Sabino Mullane <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Haas <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Thu, May 22, 2025 at 03:29:38PM -0400, Greg Sabino Mullane wrote:
> On Thu, May 22, 2025 at 2:52 PM Jeff Davis <[email protected]> wrote:
>>  * The default for pg_restore is --no-statistics. That could cause a minor
>> surprise if the user specifies --with-statistics for pg_dump and
>> not for pg_restore. An argument could be made that "if the stats are
>> there, restore them", and I don't have a strong opinion about this point,
>> but defaulting to --no-statistics seems more consistent with pg_dump.
> 
> Hm...somewhat to my own surprise, I don't like this. If it's in the dump,
> restore it.

+1, I think defaulting to restoring everything in the dump file is much
less surprising than the alternative.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-05-22 18:52           ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-22 19:29             ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 19:36               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
@ 2025-05-22 19:36                 ` Robert Haas <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Robert Haas @ 2025-05-22 19:36 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Jeff Davis <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Michael Paquier <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Thu, May 22, 2025 at 3:36 PM Nathan Bossart <[email protected]> wrote:
> +1, I think defaulting to restoring everything in the dump file is much
> less surprising than the alternative.

+1.

-- 
Robert Haas
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-05-22 18:52           ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-22 19:29             ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
@ 2025-05-22 19:41               ` Tom Lane <[email protected]>
  2025-05-22 21:29                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  1 sibling, 1 reply; 88+ messages in thread

From: Tom Lane @ 2025-05-22 19:41 UTC (permalink / raw)
  To: Greg Sabino Mullane <[email protected]>; +Cc: Jeff Davis <[email protected]>; Robert Haas <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

Greg Sabino Mullane <[email protected]> writes:
> On Thu, May 22, 2025 at 2:52 PM Jeff Davis <[email protected]> wrote:
>> * The default for pg_restore is --no-statistics. That could cause a minor
>> surprise if the user specifies --with-statistics for pg_dump and
>> not for pg_restore.

> Hm...somewhat to my own surprise, I don't like this. If it's in the dump,
> restore it.

Yeah, I tend to lean that way too.  If the user went out of their way
to say --with-statistics for pg_dump, how likely is it that they
don't want the statistics restored?

Another argument pointing in that direction is that the definition
Jeff proposes creates an inconsistency in the output between text
mode:

	pg_dump --with-statistics ... | psql

and non-text mode:

	pg_dump -Fc --with-statistics ... | pg_restore

There is no additional filter in text mode, so I think pg_restore's
default behavior should also be "no additional filter".

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 88+ messages in thread

* Re: Statistics Import and Export
  2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
  2025-05-22 18:52           ` Re: Statistics Import and Export Jeff Davis <[email protected]>
  2025-05-22 19:29             ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
  2025-05-22 19:41               ` Re: Statistics Import and Export Tom Lane <[email protected]>
@ 2025-05-22 21:29                 ` Jeff Davis <[email protected]>
  0 siblings, 0 replies; 88+ messages in thread

From: Jeff Davis @ 2025-05-22 21:29 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; Greg Sabino Mullane <[email protected]>; +Cc: Robert Haas <[email protected]>; Ashutosh Bapat <[email protected]>; Corey Huinker <[email protected]>; Andres Freund <[email protected]>; Michael Paquier <[email protected]>; Nathan Bossart <[email protected]>; Bruce Momjian <[email protected]>; Matthias van de Meent <[email protected]>; Magnus Hagander <[email protected]>; Stephen Frost <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>; [email protected]; jian he <[email protected]>

On Thu, 2025-05-22 at 15:41 -0400, Tom Lane wrote:
> There is no additional filter in text mode, so I think pg_restore's
> default behavior should also be "no additional filter".

Attached. Only the defaults for pg_dump and pg_dumpall are changed, and
pg_upgrade explicitly specifies --with-statistics.

Regards,
	Jeff Davis



Attachments:

  [text/x-patch] v2-0001-Change-defaults-for-statistics-export.patch (15.3K, ../../[email protected]/2-v2-0001-Change-defaults-for-statistics-export.patch)
  download | inline diff:
From 5b73253f8848638f1754f4b9da82e90e8814b4b1 Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Thu, 22 May 2025 11:03:03 -0700
Subject: [PATCH v2] Change defaults for statistics export.

Set the default behavior of pg_dump, pg_dumpall, and pg_restore to be
--no-statistics. Leave the default for pg_upgrade to be
--with-statistics.

Discussion: https://postgr.es/m/CA+TgmoZ9=RnWcCOZiKYYjZs_AW1P4QXCw--h4dOLLHuf1Omung@mail.gmail.com
---
 doc/src/sgml/ref/pg_dump.sgml        |  4 +-
 doc/src/sgml/ref/pg_dumpall.sgml     |  4 +-
 src/bin/pg_dump/pg_backup_archiver.c |  2 +-
 src/bin/pg_dump/t/002_pg_dump.pl     | 59 ++++++++++++++++++++++++++++
 src/bin/pg_upgrade/dump.c            |  2 +-
 5 files changed, 65 insertions(+), 6 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index c10bca63e55..995d8f9a040 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -1134,7 +1134,7 @@ PostgreSQL documentation
       <term><option>--no-statistics</option></term>
       <listitem>
        <para>
-        Do not dump statistics.
+        Do not dump statistics. This is the default.
        </para>
       </listitem>
      </varlistentry>
@@ -1461,7 +1461,7 @@ PostgreSQL documentation
       <term><option>--with-statistics</option></term>
       <listitem>
        <para>
-        Dump statistics. This is the default.
+        Dump statistics.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 8c5141d036c..81d34df3386 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -567,7 +567,7 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
       <term><option>--no-statistics</option></term>
       <listitem>
        <para>
-        Do not dump statistics.
+        Do not dump statistics. This is the default.
        </para>
       </listitem>
      </varlistentry>
@@ -741,7 +741,7 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
       <term><option>--with-statistics</option></term>
       <listitem>
        <para>
-        Dump statistics. This is the default.
+        Dump statistics.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index afa42337b11..175fe9c4273 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -152,7 +152,7 @@ InitDumpOptions(DumpOptions *opts)
 	opts->dumpSections = DUMP_UNSECTIONED;
 	opts->dumpSchema = true;
 	opts->dumpData = true;
-	opts->dumpStatistics = true;
+	opts->dumpStatistics = false;
 }
 
 /*
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index cf34f71ea11..386e21e0c59 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -68,6 +68,7 @@ my %pgdump_runs = (
 			'--no-data',
 			'--sequence-data',
 			'--binary-upgrade',
+			'--with-statistics',
 			'--dbname' => 'postgres',    # alternative way to specify database
 		],
 		restore_cmd => [
@@ -75,6 +76,7 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--verbose',
 			'--file' => "$tempdir/binary_upgrade.sql",
+			'--with-statistics',
 			"$tempdir/binary_upgrade.dump",
 		],
 	},
@@ -88,11 +90,13 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--compress' => '1',
 			'--file' => "$tempdir/compression_gzip_custom.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/compression_gzip_custom.sql",
+			'--with-statistics',
 			"$tempdir/compression_gzip_custom.dump",
 		],
 		command_like => {
@@ -115,6 +119,7 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--compress' => 'gzip:1',
 			'--file' => "$tempdir/compression_gzip_dir",
+			'--with-statistics',
 			'postgres',
 		],
 		# Give coverage for manually compressed blobs.toc files during
@@ -132,6 +137,7 @@ my %pgdump_runs = (
 			'pg_restore',
 			'--jobs' => '2',
 			'--file' => "$tempdir/compression_gzip_dir.sql",
+			'--with-statistics',
 			"$tempdir/compression_gzip_dir",
 		],
 	},
@@ -144,6 +150,7 @@ my %pgdump_runs = (
 			'--format' => 'plain',
 			'--compress' => '1',
 			'--file' => "$tempdir/compression_gzip_plain.sql.gz",
+			'--with-statistics',
 			'postgres',
 		],
 		# Decompress the generated file to run through the tests.
@@ -162,11 +169,13 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--compress' => 'lz4',
 			'--file' => "$tempdir/compression_lz4_custom.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/compression_lz4_custom.sql",
+			'--with-statistics',
 			"$tempdir/compression_lz4_custom.dump",
 		],
 		command_like => {
@@ -189,6 +198,7 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--compress' => 'lz4:1',
 			'--file' => "$tempdir/compression_lz4_dir",
+			'--with-statistics',
 			'postgres',
 		],
 		# Verify that data files were compressed
@@ -200,6 +210,7 @@ my %pgdump_runs = (
 			'pg_restore',
 			'--jobs' => '2',
 			'--file' => "$tempdir/compression_lz4_dir.sql",
+			'--with-statistics',
 			"$tempdir/compression_lz4_dir",
 		],
 	},
@@ -212,6 +223,7 @@ my %pgdump_runs = (
 			'--format' => 'plain',
 			'--compress' => 'lz4',
 			'--file' => "$tempdir/compression_lz4_plain.sql.lz4",
+			'--with-statistics',
 			'postgres',
 		],
 		# Decompress the generated file to run through the tests.
@@ -233,11 +245,13 @@ my %pgdump_runs = (
 			'--format' => 'custom',
 			'--compress' => 'zstd',
 			'--file' => "$tempdir/compression_zstd_custom.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/compression_zstd_custom.sql",
+			'--with-statistics',
 			"$tempdir/compression_zstd_custom.dump",
 		],
 		command_like => {
@@ -259,6 +273,7 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--compress' => 'zstd:1',
 			'--file' => "$tempdir/compression_zstd_dir",
+			'--with-statistics',
 			'postgres',
 		],
 		# Give coverage for manually compressed blobs.toc files during
@@ -279,6 +294,7 @@ my %pgdump_runs = (
 			'pg_restore',
 			'--jobs' => '2',
 			'--file' => "$tempdir/compression_zstd_dir.sql",
+			'--with-statistics',
 			"$tempdir/compression_zstd_dir",
 		],
 	},
@@ -292,6 +308,7 @@ my %pgdump_runs = (
 			'--format' => 'plain',
 			'--compress' => 'zstd:long',
 			'--file' => "$tempdir/compression_zstd_plain.sql.zst",
+			'--with-statistics',
 			'postgres',
 		],
 		# Decompress the generated file to run through the tests.
@@ -310,6 +327,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/clean.sql",
 			'--clean',
+			'--with-statistics',
 			'--dbname' => 'postgres',    # alternative way to specify database
 		],
 	},
@@ -320,6 +338,7 @@ my %pgdump_runs = (
 			'--clean',
 			'--if-exists',
 			'--encoding' => 'UTF8',      # no-op, just for testing
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -338,6 +357,7 @@ my %pgdump_runs = (
 			'--create',
 			'--no-reconnect',    # no-op, just for testing
 			'--verbose',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -356,6 +376,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/defaults.sql",
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -364,6 +385,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/defaults_no_public.sql",
+			'--with-statistics',
 			'regress_pg_dump_test',
 		],
 	},
@@ -373,6 +395,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--clean',
 			'--file' => "$tempdir/defaults_no_public_clean.sql",
+			'--with-statistics',
 			'regress_pg_dump_test',
 		],
 	},
@@ -381,6 +404,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/defaults_public_owner.sql",
+			'--with-statistics',
 			'regress_public_owner',
 		],
 	},
@@ -395,12 +419,14 @@ my %pgdump_runs = (
 			'pg_dump',
 			'--format' => 'custom',
 			'--file' => "$tempdir/defaults_custom_format.dump",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--format' => 'custom',
 			'--file' => "$tempdir/defaults_custom_format.sql",
+			'--with-statistics',
 			"$tempdir/defaults_custom_format.dump",
 		],
 		command_like => {
@@ -425,12 +451,14 @@ my %pgdump_runs = (
 			'pg_dump',
 			'--format' => 'directory',
 			'--file' => "$tempdir/defaults_dir_format",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--format' => 'directory',
 			'--file' => "$tempdir/defaults_dir_format.sql",
+			'--with-statistics',
 			"$tempdir/defaults_dir_format",
 		],
 		command_like => {
@@ -456,11 +484,13 @@ my %pgdump_runs = (
 			'--format' => 'directory',
 			'--jobs' => 2,
 			'--file' => "$tempdir/defaults_parallel",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/defaults_parallel.sql",
+			'--with-statistics',
 			"$tempdir/defaults_parallel",
 		],
 	},
@@ -472,12 +502,14 @@ my %pgdump_runs = (
 			'pg_dump',
 			'--format' => 'tar',
 			'--file' => "$tempdir/defaults_tar_format.tar",
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--format' => 'tar',
 			'--file' => "$tempdir/defaults_tar_format.sql",
+			'--with-statistics',
 			"$tempdir/defaults_tar_format.tar",
 		],
 	},
@@ -486,6 +518,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/exclude_dump_test_schema.sql",
 			'--exclude-schema' => 'dump_test',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -494,6 +527,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/exclude_test_table.sql",
 			'--exclude-table' => 'dump_test.test_table',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -502,6 +536,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/exclude_measurement.sql",
 			'--exclude-table-and-children' => 'dump_test.measurement',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -511,6 +546,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/exclude_measurement_data.sql",
 			'--exclude-table-data-and-children' => 'dump_test.measurement',
 			'--no-unlogged-table-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -520,6 +556,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/exclude_test_table_data.sql",
 			'--exclude-table-data' => 'dump_test.test_table',
 			'--no-unlogged-table-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -538,6 +575,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/pg_dumpall_globals.sql",
 			'--globals-only',
 			'--no-sync',
+			'--with-statistics',
 		],
 	},
 	pg_dumpall_globals_clean => {
@@ -547,12 +585,14 @@ my %pgdump_runs = (
 			'--globals-only',
 			'--clean',
 			'--no-sync',
+			'--with-statistics',
 		],
 	},
 	pg_dumpall_dbprivs => {
 		dump_cmd => [
 			'pg_dumpall', '--no-sync',
 			'--file' => "$tempdir/pg_dumpall_dbprivs.sql",
+			'--with-statistics',
 		],
 	},
 	pg_dumpall_exclude => {
@@ -562,6 +602,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/pg_dumpall_exclude.sql",
 			'--exclude-database' => '*dump_test*',
 			'--no-sync',
+			'--with-statistics',
 		],
 	},
 	no_toast_compression => {
@@ -569,6 +610,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_toast_compression.sql",
 			'--no-toast-compression',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -577,6 +619,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_large_objects.sql",
 			'--no-large-objects',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -585,6 +628,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_policies.sql",
 			'--no-policies',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -593,6 +637,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_privs.sql",
 			'--no-privileges',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -601,6 +646,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_owner.sql",
 			'--no-owner',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -609,6 +655,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/no_table_access_method.sql",
 			'--no-table-access-method',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -617,6 +664,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/only_dump_test_schema.sql",
 			'--schema' => 'dump_test',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -627,6 +675,7 @@ my %pgdump_runs = (
 			'--table' => 'dump_test.test_table',
 			'--lock-wait-timeout' =>
 			  (1000 * $PostgreSQL::Test::Utils::timeout_default),
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -637,6 +686,7 @@ my %pgdump_runs = (
 			'--table-and-children' => 'dump_test.measurement',
 			'--lock-wait-timeout' =>
 			  (1000 * $PostgreSQL::Test::Utils::timeout_default),
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -646,6 +696,7 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/role.sql",
 			'--role' => 'regress_dump_test_role',
 			'--schema' => 'dump_test_second_schema',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -658,11 +709,13 @@ my %pgdump_runs = (
 			'--file' => "$tempdir/role_parallel",
 			'--role' => 'regress_dump_test_role',
 			'--schema' => 'dump_test_second_schema',
+			'--with-statistics',
 			'postgres',
 		],
 		restore_cmd => [
 			'pg_restore',
 			'--file' => "$tempdir/role_parallel.sql",
+			'--with-statistics',
 			"$tempdir/role_parallel",
 		],
 	},
@@ -691,6 +744,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/section_pre_data.sql",
 			'--section' => 'pre-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -699,6 +753,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/section_data.sql",
 			'--section' => 'data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -707,6 +762,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			'--file' => "$tempdir/section_post_data.sql",
 			'--section' => 'post-data',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -717,6 +773,7 @@ my %pgdump_runs = (
 			'--schema' => 'dump_test',
 			'--large-objects',
 			'--no-large-objects',
+			'--with-statistics',
 			'postgres',
 		],
 	},
@@ -732,6 +789,7 @@ my %pgdump_runs = (
 			'pg_dump', '--no-sync',
 			"--file=$tempdir/no_data_no_schema.sql", '--no-data',
 			'--no-schema', 'postgres',
+			'--with-statistics',
 		],
 	},
 	statistics_only => {
@@ -752,6 +810,7 @@ my %pgdump_runs = (
 		dump_cmd => [
 			'pg_dump', '--no-sync',
 			"--file=$tempdir/no_schema.sql", '--no-schema',
+			'--with-statistics',
 			'postgres',
 		],
 	},);
diff --git a/src/bin/pg_upgrade/dump.c b/src/bin/pg_upgrade/dump.c
index 23cb08e8347..183f08ce1e8 100644
--- a/src/bin/pg_upgrade/dump.c
+++ b/src/bin/pg_upgrade/dump.c
@@ -58,7 +58,7 @@ generate_old_dump(void)
 						   (user_opts.transfer_mode == TRANSFER_MODE_SWAP) ?
 						   "" : "--sequence-data",
 						   log_opts.verbose ? "--verbose" : "",
-						   user_opts.do_statistics ? "" : "--no-statistics",
+						   user_opts.do_statistics ? "--with-statistics" : "--no-statistics",
 						   log_opts.dumpdir,
 						   sql_file_name, escaped_connstr.data);
 
-- 
2.43.0



^ permalink  raw  reply  [nested|flat] 88+ messages in thread


end of thread, other threads:[~2025-05-22 21:29 UTC | newest]

Thread overview: 88+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-02-28 20:44 [PATCH v6 2/3] Add index_get_partition convenience function Alvaro Herrera <[email protected]>
2019-08-04 00:02 [PATCH] Add new GUC compression_algorithm Petr Jelinek <[email protected]>
2024-01-22 06:09 Re: Statistics Import and Export Peter Smith <[email protected]>
2024-08-26 04:32 [PATCH v21 3/8] Row pattern recognition patch (rewriter). Tatsuo Ishii <[email protected]>
2025-03-07 00:58 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-07 01:47   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-07 01:42 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-07 02:56   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-07 16:22   ` Re: Statistics Import and Export Andres Freund <[email protected]>
2025-03-07 16:53     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-07 17:41   ` Re: Statistics Import and Export Robert Treat <[email protected]>
2025-03-07 18:41     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-07 20:46       ` Re: Statistics Import and Export Robert Treat <[email protected]>
2025-03-07 21:43         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-08 03:43           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-08 05:51             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-03-08 07:51               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-08 07:56                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-08 03:40         ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-08 15:56           ` Re: Statistics Import and Export Robert Treat <[email protected]>
2025-03-08 19:09             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-25 05:32               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-25 17:51                 ` Re: Statistics Import and Export Robert Treat <[email protected]>
2025-03-09 17:00             ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-14 20:03               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-16 01:37                 ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-19 22:17                   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-19 22:35                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-25 06:53                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-25 14:53                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-25 18:42                         ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-25 19:59                           ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-26 01:41                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-29 01:11                               ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-29 05:29                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-03-29 05:44                                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-31 15:11                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-04-01 02:33                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-01 03:02                                         ` Re: Statistics Import and Export Robert Treat <[email protected]>
2025-04-01 18:20                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-01 18:44                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-02 03:21                                             ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-02 05:44                                               ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-04-02 16:42                                                 ` Re: Statistics Import and Export Andres Freund <[email protected]>
2025-04-03 02:26                                                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-04 02:19                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-04 19:56                                                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-04 20:06                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-04 20:58                                                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-04 22:25                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-04-04 23:32                                                             ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-04-05 02:06                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-14 00:01                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-05-14 15:53                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-14 20:30                                                                     ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-05-16 18:47                                                                       ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-05-19 01:52                                                                         ` Re: Statistics Import and Export Michael Paquier <[email protected]>
2025-05-19 16:51                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-19 21:13                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-05-19 21:31                                                                           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-20 17:32                                                                             ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-05-21 16:08                                                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-21 19:33                                                                                 ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-05-21 21:14                                                                                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-05-21 21:29                                                                                   ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-21 23:11                                                                                     ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-05-21 23:53                                                                                     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-05-22 15:25                                                                                       ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-22 19:25                                                                                         ` Re: Statistics Import and Export Hari Krishna Sunder <[email protected]>
2025-04-03 02:38                                               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-03-16 20:33                 ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-03-16 21:32                   ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-17 14:23                     ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-03-17 23:24                       ` Re: Statistics Import and Export Corey Huinker <[email protected]>
2025-03-18 01:01                         ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-03-31 17:39 ` Re: Statistics Import and Export Robert Haas <[email protected]>
2025-03-31 22:04   ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-04-01 20:24     ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-05-10 19:51       ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
2025-05-22 14:20         ` Re: Statistics Import and Export Robert Haas <[email protected]>
2025-05-22 14:30           ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-22 14:53             ` Re: Statistics Import and Export Tom Lane <[email protected]>
2025-05-22 18:52           ` Re: Statistics Import and Export Jeff Davis <[email protected]>
2025-05-22 19:29             ` Re: Statistics Import and Export Greg Sabino Mullane <[email protected]>
2025-05-22 19:36               ` Re: Statistics Import and Export Nathan Bossart <[email protected]>
2025-05-22 19:36                 ` Re: Statistics Import and Export Robert Haas <[email protected]>
2025-05-22 19:41               ` Re: Statistics Import and Export Tom Lane <[email protected]>
2025-05-22 21:29                 ` Re: Statistics Import and Export Jeff Davis <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox