Remove redundant pg_set_*_stats() variants.
authorJeff Davis <[email protected]>
Wed, 26 Feb 2025 00:15:47 +0000 (16:15 -0800)
committerJeff Davis <[email protected]>
Wed, 26 Feb 2025 00:15:47 +0000 (16:15 -0800)
After commit f3dae2ae58, the primary purpose of separating the
pg_set_*_stats() from the pg_restore_*_stats() variants was
eliminated.

Leave pg_restore_relation_stats() and pg_restore_attribute_stats(),
which satisfy both purposes, and remove pg_set_relation_stats() and
pg_set_attribute_stats().

Reviewed-by: Corey Huinker <[email protected]>
Discussion: https://postgr.es/m/1457469.1740419458@sss.pgh.pa.us

doc/src/sgml/func.sgml
src/backend/catalog/system_functions.sql
src/backend/statistics/attribute_stats.c
src/backend/statistics/relation_stats.c
src/backend/statistics/stat_utils.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/include/statistics/stat_utils.h
src/test/regress/expected/stats_import.out
src/test/regress/sql/stats_import.sql

index f0ccb751106cb1bf6434ce7d517c8b4c8fce47aa..12206e0cfc6785886647ebc4ad6fa078c74ed624 100644 (file)
@@ -30180,66 +30180,6 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
      </thead>
 
      <tbody>
-      <row>
-       <entry role="func_table_entry">
-        <para role="func_signature">
-         <indexterm>
-          <primary>pg_set_relation_stats</primary>
-         </indexterm>
-         <function>pg_set_relation_stats</function> (
-         <parameter>relation</parameter> <type>regclass</type>
-         <optional>, <parameter>relpages</parameter> <type>integer</type></optional>
-         <optional>, <parameter>reltuples</parameter> <type>real</type></optional>
-         <optional>, <parameter>relallvisible</parameter> <type>integer</type></optional> )
-         <returnvalue>void</returnvalue>
-        </para>
-        <para>
-         Updates relation-level statistics for the given relation to the
-         specified values. The parameters correspond to columns in <link
-         linkend="catalog-pg-class"><structname>pg_class</structname></link>. Unspecified
-         or <literal>NULL</literal> values leave the setting unchanged.
-        </para>
-        <para>
-         Ordinarily, these statistics are collected automatically or updated
-         as a part of <xref linkend="sql-vacuum"/> or <xref
-         linkend="sql-analyze"/>, so it's not necessary to call this
-         function. However, it may be useful when testing the effects of
-         statistics on the planner to understand or anticipate plan changes.
-        </para>
-        <para>
-         The caller must have the <literal>MAINTAIN</literal> privilege on
-         the table or be the owner of the database.
-        </para>
-        <para>
-         The value of <structfield>relpages</structfield> must be greater than
-         or equal to <literal>-1</literal>,
-         <structfield>reltuples</structfield> must be greater than or equal to
-         <literal>-1.0</literal>, and <structfield>relallvisible</structfield>
-         must be greater than or equal to <literal>0</literal>.
-        </para>
-       </entry>
-      </row>
-
-      <row>
-       <entry role="func_table_entry">
-        <para role="func_signature">
-         <indexterm>
-          <primary>pg_clear_relation_stats</primary>
-         </indexterm>
-         <function>pg_clear_relation_stats</function> ( <parameter>relation</parameter> <type>regclass</type> )
-         <returnvalue>void</returnvalue>
-        </para>
-        <para>
-         Clears table-level statistics for the given relation, as though the
-         table was newly created.
-        </para>
-        <para>
-         The caller must have the <literal>MAINTAIN</literal> privilege on
-         the table or be the owner of the database.
-        </para>
-       </entry>
-      </row>
-
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -30248,26 +30188,25 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
         <function>pg_restore_relation_stats</function> (
         <literal>VARIADIC</literal> <parameter>kwargs</parameter> <type>"any"</type> )
         <returnvalue>boolean</returnvalue>
-        </para>
-        <para>
-         Similar to <function>pg_set_relation_stats()</function>, but intended
-         for bulk restore of relation statistics. The tracked statistics may
-         change from version to version, so the primary purpose of this
-         function is to maintain a consistent function signature to avoid
-         errors when restoring statistics from previous versions.
-        </para>
+       </para>
         <para>
-         Arguments are passed as pairs of <replaceable>argname</replaceable>
-         and <replaceable>argvalue</replaceable>, where
-         <replaceable>argname</replaceable> corresponds to a named argument in
-         <function>pg_set_relation_stats()</function> and
-         <replaceable>argvalue</replaceable> is of the corresponding type.
+         Updates table-level statistics.  Ordinarily, these statistics are
+         collected automatically or updated as a part of <xref
+         linkend="sql-vacuum"/> or <xref linkend="sql-analyze"/>, so it's not
+         necessary to call this function.  However, it is useful after a
+         restore to enable the optimizer to choose better plans if
+         <command>ANALYZE</command> has not been run yet.
         </para>
         <para>
-         Additionally, this function supports argument name
-         <literal>version</literal> of type <type>integer</type>, which
-         specifies the version from which the statistics originated, improving
-         interpretation of older statistics.
+         The tracked statistics may change from version to version, so
+         arguments are passed as pairs of <replaceable>argname</replaceable>
+         and <replaceable>argvalue</replaceable> in the form:
+<programlisting>
+ SELECT pg_restore_relation_stats(
+    '<replaceable>arg1name</replaceable>', '<replaceable>arg1value</replaceable>'::<replaceable>arg1type</replaceable>,
+    '<replaceable>arg2name</replaceable>', '<replaceable>arg2value</replaceable>'::<replaceable>arg2type</replaceable>,
+    '<replaceable>arg3name</replaceable>', '<replaceable>arg3value</replaceable>'::<replaceable>arg3type</replaceable>);
+</programlisting>
         </para>
         <para>
          For example, to set the <structname>relpages</structname> and
@@ -30277,62 +30216,37 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
  SELECT pg_restore_relation_stats(
     'relation',  'mytable'::regclass,
     'relpages',  173::integer,
-    'reltuples', 10000::float4);
+    'reltuples', 10000::real);
 </programlisting>
         </para>
         <para>
-         Minor errors are reported as a <literal>WARNING</literal> and
-         ignored, and remaining statistics will still be restored. If all
-         specified statistics are successfully restored, return
-         <literal>true</literal>, otherwise <literal>false</literal>.
-        </para>
-       </entry>
-      </row>
-
-      <row>
-       <entry role="func_table_entry">
-        <para role="func_signature">
-         <indexterm>
-          <primary>pg_set_attribute_stats</primary>
-         </indexterm>
-         <function>pg_set_attribute_stats</function> (
-         <parameter>relation</parameter> <type>regclass</type>,
-         <parameter>attname</parameter> <type>name</type>,
-         <parameter>inherited</parameter> <type>boolean</type>
-         <optional>, <parameter>null_frac</parameter> <type>real</type></optional>
-         <optional>, <parameter>avg_width</parameter> <type>integer</type></optional>
-         <optional>, <parameter>n_distinct</parameter> <type>real</type></optional>
-         <optional>, <parameter>most_common_vals</parameter> <type>text</type>, <parameter>most_common_freqs</parameter> <type>real[]</type> </optional>
-         <optional>, <parameter>histogram_bounds</parameter> <type>text</type> </optional>
-         <optional>, <parameter>correlation</parameter> <type>real</type> </optional>
-         <optional>, <parameter>most_common_elems</parameter> <type>text</type>, <parameter>most_common_elem_freqs</parameter> <type>real[]</type> </optional>
-         <optional>, <parameter>elem_count_histogram</parameter> <type>real[]</type> </optional>
-         <optional>, <parameter>range_length_histogram</parameter> <type>text</type> </optional>
-         <optional>, <parameter>range_empty_frac</parameter> <type>real</type> </optional>
-         <optional>, <parameter>range_bounds_histogram</parameter> <type>text</type> </optional> )
-         <returnvalue>void</returnvalue>
-        </para>
-        <para>
-         Creates or updates attribute-level statistics for the given relation
-         and attribute name to the specified values. The parameters correspond
-         to attributes of the same name found in the <link
-         linkend="view-pg-stats"><structname>pg_stats</structname></link>
-         view.
+         The argument <literal>relation</literal> with a value of type
+         <type>regclass</type> is required, and specifies the table. Other
+         arguments are the names 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>.
         </para>
         <para>
-         Optional parameters default to <literal>NULL</literal>, which leave
-         the corresponding statistic unchanged.
+         Additionally, this function supports argument name
+         <literal>version</literal> of type <type>integer</type>, which
+         specifies the version from which the statistics originated, improving
+         interpretation of statistics from older versions of
+         <productname>PostgreSQL</productname>.
         </para>
         <para>
-         Ordinarily, these statistics are collected automatically or updated
-         as a part of <xref linkend="sql-vacuum"/> or <xref
-         linkend="sql-analyze"/>, so it's not necessary to call this
-         function. However, it may be useful when testing the effects of
-         statistics on the planner to understand or anticipate plan changes.
+         Minor errors are reported as a <literal>WARNING</literal> and
+         ignored, and remaining statistics will still be restored. If all
+         specified statistics are successfully restored, return
+         <literal>true</literal>, otherwise <literal>false</literal>.
         </para>
         <para>
-         The caller must have the <literal>MAINTAIN</literal> privilege on
-         the table or be the owner of the database.
+         The caller must have the <literal>MAINTAIN</literal> privilege on the
+         table or be the owner of the database.
         </para>
        </entry>
       </row>
@@ -30341,21 +30255,18 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
        <entry role="func_table_entry">
         <para role="func_signature">
          <indexterm>
-          <primary>pg_clear_attribute_stats</primary>
+          <primary>pg_clear_relation_stats</primary>
          </indexterm>
-         <function>pg_clear_attribute_stats</function> (
-         <parameter>relation</parameter> <type>regclass</type>,
-         <parameter>attname</parameter> <type>name</type>,
-         <parameter>inherited</parameter> <type>boolean</type> )
+         <function>pg_clear_relation_stats</function> ( <parameter>relation</parameter> <type>regclass</type> )
          <returnvalue>void</returnvalue>
         </para>
         <para>
-         Clears table-level statistics for the given relation attribute, as
-         though the table was newly created.
+         Clears table-level statistics for the given relation, as though the
+         table was newly created.
         </para>
         <para>
-         The caller must have the <literal>MAINTAIN</literal> privilege on
-         the table or be the owner of the database.
+         The caller must have the <literal>MAINTAIN</literal> privilege on the
+         table or be the owner of the database.
         </para>
        </entry>
       </row>
@@ -30368,26 +30279,25 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
         <function>pg_restore_attribute_stats</function> (
         <literal>VARIADIC</literal> <parameter>kwargs</parameter> <type>"any"</type> )
         <returnvalue>boolean</returnvalue>
-        </para>
-        <para>
-         Similar to <function>pg_set_attribute_stats()</function>, but
-         intended for bulk restore of attribute statistics. The tracked
-         statistics may change from version to version, so the primary purpose
-         of this function is to maintain a consistent function signature to
-         avoid errors when restoring statistics from previous versions.
-        </para>
+       </para>
         <para>
-         Arguments are passed as pairs of <replaceable>argname</replaceable>
-         and <replaceable>argvalue</replaceable>, where
-         <replaceable>argname</replaceable> corresponds to a named argument in
-         <function>pg_set_attribute_stats()</function> and
-         <replaceable>argvalue</replaceable> is of the corresponding type.
+         Create or update column-level statistics.  Ordinarily, these
+         statistics are collected automatically or updated as a part of <xref
+         linkend="sql-vacuum"/> or <xref linkend="sql-analyze"/>, so it's not
+         necessary to call this function.  However, it is useful after a
+         restore to enable the optimizer to choose better plans if
+         <command>ANALYZE</command> has not been run yet.
         </para>
         <para>
-         Additionally, this function supports argument name
-         <literal>version</literal> of type <type>integer</type>, which
-         specifies the version from which the statistics originated, improving
-         interpretation of older statistics.
+         The tracked statistics may change from version to version, so
+         arguments are passed as pairs of <replaceable>argname</replaceable>
+         and <replaceable>argvalue</replaceable> in the form:
+<programlisting>
+ SELECT pg_restore_attribute_stats(
+    '<replaceable>arg1name</replaceable>', '<replaceable>arg1value</replaceable>'::<replaceable>arg1type</replaceable>,
+    '<replaceable>arg2name</replaceable>', '<replaceable>arg2value</replaceable>'::<replaceable>arg2type</replaceable>,
+    '<replaceable>arg3name</replaceable>', '<replaceable>arg3value</replaceable>'::<replaceable>arg3type</replaceable>);
+</programlisting>
         </para>
         <para>
          For example, to set the <structname>avg_width</structname> and
@@ -30403,12 +30313,56 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
     '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;
+         <literal>attname</literal> with a value of type <type>name</type>,
+         which specifies the column; and <literal>inherited</literal>, which
+         specifies whether the statistics includes values from child tables.
+         Other arguments are the names of statistics corresponding to columns
+         in <link
+         linkend="view-pg-stats"><structname>pg_stats</structname></link>.
+        </para>
+        <para>
+         Additionally, this function supports argument name
+         <literal>version</literal> of type <type>integer</type>, which
+         specifies the version from which the statistics originated, improving
+         interpretation of statistics from older versions of
+         <productname>PostgreSQL</productname>.
+        </para>
         <para>
          Minor errors are reported as a <literal>WARNING</literal> and
          ignored, and remaining statistics will still be restored. If all
          specified statistics are successfully restored, return
          <literal>true</literal>, otherwise <literal>false</literal>.
         </para>
+        <para>
+         The caller must have the <literal>MAINTAIN</literal> privilege on the
+         table or be the owner of the database.
+        </para>
+       </entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry">
+        <para role="func_signature">
+         <indexterm>
+          <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>inherited</parameter> <type>boolean</type> )
+         <returnvalue>void</returnvalue>
+        </para>
+        <para>
+         Clears column-level statistics for the given relation and
+         attribute, as though the table was newly created.
+        </para>
+        <para>
+         The caller must have the <literal>MAINTAIN</literal> privilege on
+         the table or be the owner of the database.
+        </para>
        </entry>
       </row>
      </tbody>
index 591157b1d1b1c3218a956b1c772876c53139c37c..86888cd3201f2032a18c2b8c7beb360c877624a2 100644 (file)
@@ -636,38 +636,6 @@ LANGUAGE INTERNAL
 CALLED ON NULL INPUT VOLATILE PARALLEL SAFE
 AS 'pg_stat_reset_slru';
 
-CREATE OR REPLACE FUNCTION
-  pg_set_relation_stats(relation regclass,
-                        relpages integer DEFAULT NULL,
-                        reltuples real DEFAULT NULL,
-                        relallvisible integer DEFAULT NULL)
-RETURNS void
-LANGUAGE INTERNAL
-CALLED ON NULL INPUT VOLATILE
-AS 'pg_set_relation_stats';
-
-CREATE OR REPLACE FUNCTION
-  pg_set_attribute_stats(relation regclass,
-                         attname name,
-                         inherited bool,
-                         null_frac real DEFAULT NULL,
-                         avg_width integer DEFAULT NULL,
-                         n_distinct real DEFAULT NULL,
-                         most_common_vals text DEFAULT NULL,
-                         most_common_freqs real[] DEFAULT NULL,
-                         histogram_bounds text DEFAULT NULL,
-                         correlation real DEFAULT NULL,
-                         most_common_elems text DEFAULT NULL,
-                         most_common_elem_freqs real[] DEFAULT NULL,
-                         elem_count_histogram real[] DEFAULT NULL,
-                         range_length_histogram text DEFAULT NULL,
-                         range_empty_frac real DEFAULT NULL,
-                         range_bounds_histogram text DEFAULT NULL)
-RETURNS void
-LANGUAGE INTERNAL
-CALLED ON NULL INPUT VOLATILE
-AS 'pg_set_attribute_stats';
-
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
index c0c398a4bb268870904038b2f68dec21e494c5f7..66a5676c810ac8e3f7b64a99f3b08a949eb15af9 100644 (file)
@@ -76,16 +76,16 @@ static struct StatsArgInfo attarginfo[] =
    [NUM_ATTRIBUTE_STATS_ARGS] = {0}
 };
 
-static bool attribute_statistics_update(FunctionCallInfo fcinfo, int elevel);
+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, int elevel,
+static void get_attr_stat_type(Oid reloid, AttrNumber attnum,
                               Oid *atttypid, int32 *atttypmod,
                               char *atttyptype, Oid *atttypcoll,
                               Oid *eq_opr, Oid *lt_opr);
-static bool get_elem_stat_type(Oid atttypid, char atttyptype, int elevel,
+static bool get_elem_stat_type(Oid atttypid, char atttyptype,
                               Oid *elemtypid, Oid *elem_eq_opr);
 static Datum text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d,
-                              Oid typid, int32 typmod, int elevel, bool *ok);
+                              Oid typid, int32 typmod, bool *ok);
 static void set_stats_slot(Datum *values, bool *nulls, bool *replaces,
                           int16 stakind, Oid staop, Oid stacoll,
                           Datum stanumbers, bool stanumbers_isnull,
@@ -109,11 +109,11 @@ static void init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
  *
  * 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 at 'elevel',
+ * as a conversion failure on one statistic kind, are reported as a WARNING
  * and other statistic kinds may still be updated.
  */
 static bool
-attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
+attribute_statistics_update(FunctionCallInfo fcinfo)
 {
    Oid         reloid;
    Name        attname;
@@ -184,33 +184,29 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
    inherited = PG_GETARG_BOOL(INHERITED_ARG);
 
    /*
-    * Check argument sanity. If some arguments are unusable, emit at elevel
+    * Check argument sanity. If some arguments are unusable, emit a WARNING
     * and set the corresponding argument to NULL in fcinfo.
     */
 
-   if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG,
-                              elevel))
+   if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG))
    {
        do_mcv = false;
        result = false;
    }
 
-   if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG,
-                              elevel))
+   if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG))
    {
        do_mcelem = false;
        result = false;
    }
-   if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG,
-                              elevel))
+   if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG))
    {
        do_dechist = false;
        result = false;
    }
 
    if (!stats_check_arg_pair(fcinfo, attarginfo,
-                             MOST_COMMON_VALS_ARG, MOST_COMMON_FREQS_ARG,
-                             elevel))
+                             MOST_COMMON_VALS_ARG, MOST_COMMON_FREQS_ARG))
    {
        do_mcv = false;
        result = false;
@@ -218,7 +214,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
 
    if (!stats_check_arg_pair(fcinfo, attarginfo,
                              MOST_COMMON_ELEMS_ARG,
-                             MOST_COMMON_ELEM_FREQS_ARG, elevel))
+                             MOST_COMMON_ELEM_FREQS_ARG))
    {
        do_mcelem = false;
        result = false;
@@ -226,14 +222,14 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
 
    if (!stats_check_arg_pair(fcinfo, attarginfo,
                              RANGE_LENGTH_HISTOGRAM_ARG,
-                             RANGE_EMPTY_FRAC_ARG, elevel))
+                             RANGE_EMPTY_FRAC_ARG))
    {
        do_range_length_histogram = false;
        result = false;
    }
 
    /* derive information from attribute */
-   get_attr_stat_type(reloid, attnum, elevel,
+   get_attr_stat_type(reloid, attnum,
                       &atttypid, &atttypmod,
                       &atttyptype, &atttypcoll,
                       &eq_opr, &lt_opr);
@@ -241,10 +237,10 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
    /* if needed, derive element type */
    if (do_mcelem || do_dechist)
    {
-       if (!get_elem_stat_type(atttypid, atttyptype, elevel,
+       if (!get_elem_stat_type(atttypid, atttyptype,
                                &elemtypid, &elem_eq_opr))
        {
-           ereport(elevel,
+           ereport(WARNING,
                    (errmsg("unable to determine element type of attribute \"%s\"", NameStr(*attname)),
                     errdetail("Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.")));
            elemtypid = InvalidOid;
@@ -259,7 +255,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
    /* histogram and correlation require less-than operator */
    if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
    {
-       ereport(elevel,
+       ereport(WARNING,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("could not determine less-than operator for attribute \"%s\"", NameStr(*attname)),
                 errdetail("Cannot set STATISTIC_KIND_HISTOGRAM or STATISTIC_KIND_CORRELATION.")));
@@ -273,7 +269,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
    if ((do_range_length_histogram || do_bounds_histogram) &&
        !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE))
    {
-       ereport(elevel,
+       ereport(WARNING,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("attribute \"%s\" is not a range type", NameStr(*attname)),
                 errdetail("Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM.")));
@@ -322,7 +318,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
                                                  &array_in_fn,
                                                  PG_GETARG_DATUM(MOST_COMMON_VALS_ARG),
                                                  atttypid, atttypmod,
-                                                 elevel, &converted);
+                                                 &converted);
 
        if (converted)
        {
@@ -344,7 +340,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
        stavalues = text_to_stavalues("histogram_bounds",
                                      &array_in_fn,
                                      PG_GETARG_DATUM(HISTOGRAM_BOUNDS_ARG),
-                                     atttypid, atttypmod, elevel,
+                                     atttypid, atttypmod,
                                      &converted);
 
        if (converted)
@@ -382,7 +378,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
                                      &array_in_fn,
                                      PG_GETARG_DATUM(MOST_COMMON_ELEMS_ARG),
                                      elemtypid, atttypmod,
-                                     elevel, &converted);
+                                     &converted);
 
        if (converted)
        {
@@ -422,7 +418,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
                                      &array_in_fn,
                                      PG_GETARG_DATUM(RANGE_BOUNDS_HISTOGRAM_ARG),
                                      atttypid, atttypmod,
-                                     elevel, &converted);
+                                     &converted);
 
        if (converted)
        {
@@ -449,7 +445,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
        stavalues = text_to_stavalues("range_length_histogram",
                                      &array_in_fn,
                                      PG_GETARG_DATUM(RANGE_LENGTH_HISTOGRAM_ARG),
-                                     FLOAT8OID, 0, elevel, &converted);
+                                     FLOAT8OID, 0, &converted);
 
        if (converted)
        {
@@ -517,7 +513,7 @@ get_attr_expr(Relation rel, int attnum)
  * Derive type information from the attribute.
  */
 static void
-get_attr_stat_type(Oid reloid, AttrNumber attnum, int elevel,
+get_attr_stat_type(Oid reloid, AttrNumber attnum,
                   Oid *atttypid, int32 *atttypmod,
                   char *atttyptype, Oid *atttypcoll,
                   Oid *eq_opr, Oid *lt_opr)
@@ -599,7 +595,7 @@ get_attr_stat_type(Oid reloid, AttrNumber attnum, int elevel,
  * Derive element type information from the attribute type.
  */
 static bool
-get_elem_stat_type(Oid atttypid, char atttyptype, int elevel,
+get_elem_stat_type(Oid atttypid, char atttyptype,
                   Oid *elemtypid, Oid *elem_eq_opr)
 {
    TypeCacheEntry *elemtypcache;
@@ -634,13 +630,13 @@ get_elem_stat_type(Oid atttypid, char atttyptype, int elevel,
 /*
  * Cast a text datum into an array with element type elemtypid.
  *
- * If an error is encountered, capture it and re-throw at elevel, and set ok
- * to false. If the resulting array contains NULLs, raise an error at elevel
- * and set ok to false. Otherwise, set ok to true.
+ * If an error is encountered, capture it and re-throw a WARNING, and set ok
+ * to false. If the resulting array contains NULLs, raise a WARNING and set ok
+ * to false. Otherwise, set ok to true.
  */
 static Datum
 text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid,
-                 int32 typmod, int elevel, bool *ok)
+                 int32 typmod, bool *ok)
 {
    LOCAL_FCINFO(fcinfo, 8);
    char       *s;
@@ -667,8 +663,7 @@ text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid,
 
    if (escontext.error_occurred)
    {
-       if (elevel != ERROR)
-           escontext.error_data->elevel = elevel;
+       escontext.error_data->elevel = WARNING;
        ThrowErrorData(escontext.error_data);
        *ok = false;
        return (Datum) 0;
@@ -676,7 +671,7 @@ text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid,
 
    if (array_contains_nulls(DatumGetArrayTypeP(result)))
    {
-       ereport(elevel,
+       ereport(WARNING,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("\"%s\" array cannot contain NULL values", staname)));
        *ok = false;
@@ -851,33 +846,6 @@ init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
    }
 }
 
-/*
- * Import statistics for a given relation attribute.
- *
- * Inserts or replaces a row in pg_statistic for the given relation and
- * attribute name. It takes input parameters that correspond to columns in the
- * view pg_stats.
- *
- * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
- * columns in pg_statistic. The remaining parameters all belong to a specific
- * stakind. Some stakinds require multiple parameters, which must be specified
- * together (or neither specified).
- *
- * Parameters are only superficially validated. Omitting a parameter or
- * passing NULL leaves the statistic unchanged.
- *
- * Parameters corresponding to ANYARRAY columns are instead passed in as text
- * values, which is a valid input string for an array of the type or element
- * type of the attribute. Any error generated by the array_in() function will
- * in turn fail the function.
- */
-Datum
-pg_set_attribute_stats(PG_FUNCTION_ARGS)
-{
-   attribute_statistics_update(fcinfo, ERROR);
-   PG_RETURN_VOID();
-}
-
 /*
  * Delete statistics for the given attribute.
  */
@@ -933,10 +901,10 @@ pg_restore_attribute_stats(PG_FUNCTION_ARGS)
                             InvalidOid, NULL, NULL);
 
    if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
-                                         attarginfo, WARNING))
+                                         attarginfo))
        result = false;
 
-   if (!attribute_statistics_update(positional_fcinfo, WARNING))
+   if (!attribute_statistics_update(positional_fcinfo))
        result = false;
 
    PG_RETURN_BOOL(result);
index 66731290a3e6beb226758489a44c4362f048d9be..11b1ef2dbc2ef12b4a6607a7aeb789cc93243f70 100644 (file)
@@ -48,13 +48,13 @@ static struct StatsArgInfo relarginfo[] =
    [NUM_RELATION_STATS_ARGS] = {0}
 };
 
-static bool relation_statistics_update(FunctionCallInfo fcinfo, int elevel);
+static bool relation_statistics_update(FunctionCallInfo fcinfo);
 
 /*
  * Internal function for modifying statistics for a relation.
  */
 static bool
-relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
+relation_statistics_update(FunctionCallInfo fcinfo)
 {
    bool        result = true;
    Oid         reloid;
@@ -83,7 +83,7 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
        reltuples = PG_GETARG_FLOAT4(RELTUPLES_ARG);
        if (reltuples < -1.0)
        {
-           ereport(elevel,
+           ereport(WARNING,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("reltuples cannot be < -1.0")));
            result = false;
@@ -118,7 +118,7 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
    ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
    if (!HeapTupleIsValid(ctup))
    {
-       ereport(elevel,
+       ereport(WARNING,
                (errcode(ERRCODE_OBJECT_IN_USE),
                 errmsg("pg_class entry for relid %u not found", reloid)));
        table_close(crel, RowExclusiveLock);
@@ -169,16 +169,6 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
    return result;
 }
 
-/*
- * Set statistics for a given pg_class entry.
- */
-Datum
-pg_set_relation_stats(PG_FUNCTION_ARGS)
-{
-   relation_statistics_update(fcinfo, ERROR);
-   PG_RETURN_VOID();
-}
-
 /*
  * Clear statistics for a given pg_class entry; that is, set back to initial
  * stats for a newly-created table.
@@ -199,7 +189,7 @@ pg_clear_relation_stats(PG_FUNCTION_ARGS)
    newfcinfo->args[3].value = UInt32GetDatum(0);
    newfcinfo->args[3].isnull = false;
 
-   relation_statistics_update(newfcinfo, ERROR);
+   relation_statistics_update(newfcinfo);
    PG_RETURN_VOID();
 }
 
@@ -214,10 +204,10 @@ pg_restore_relation_stats(PG_FUNCTION_ARGS)
                             InvalidOid, NULL, NULL);
 
    if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
-                                         relarginfo, WARNING))
+                                         relarginfo))
        result = false;
 
-   if (!relation_statistics_update(positional_fcinfo, WARNING))
+   if (!relation_statistics_update(positional_fcinfo))
        result = false;
 
    PG_RETURN_BOOL(result);
index e70ea1ce73857e47c0740357793538a9c33232b7..54ead90b5bb5ad76fab603670d4767055395798d 100644 (file)
@@ -48,13 +48,13 @@ stats_check_required_arg(FunctionCallInfo fcinfo,
  * Check that argument is either NULL or a one dimensional array with no
  * NULLs.
  *
- * If a problem is found, emit at elevel, and return false. Otherwise return
+ * If a problem is found, emit a WARNING, and return false. Otherwise return
  * true.
  */
 bool
 stats_check_arg_array(FunctionCallInfo fcinfo,
                      struct StatsArgInfo *arginfo,
-                     int argnum, int elevel)
+                     int argnum)
 {
    ArrayType  *arr;
 
@@ -65,7 +65,7 @@ stats_check_arg_array(FunctionCallInfo fcinfo,
 
    if (ARR_NDIM(arr) != 1)
    {
-       ereport(elevel,
+       ereport(WARNING,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("\"%s\" cannot be a multidimensional array",
                        arginfo[argnum].argname)));
@@ -74,7 +74,7 @@ stats_check_arg_array(FunctionCallInfo fcinfo,
 
    if (array_contains_nulls(arr))
    {
-       ereport(elevel,
+       ereport(WARNING,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("\"%s\" array cannot contain NULL values",
                        arginfo[argnum].argname)));
@@ -89,13 +89,13 @@ stats_check_arg_array(FunctionCallInfo fcinfo,
  * a particular stakind, such as most_common_vals and most_common_freqs for
  * STATISTIC_KIND_MCV.
  *
- * If a problem is found, emit at elevel, and return false. Otherwise return
+ * If a problem is found, emit a WARNING, and return false. Otherwise return
  * true.
  */
 bool
 stats_check_arg_pair(FunctionCallInfo fcinfo,
                     struct StatsArgInfo *arginfo,
-                    int argnum1, int argnum2, int elevel)
+                    int argnum1, int argnum2)
 {
    if (PG_ARGISNULL(argnum1) && PG_ARGISNULL(argnum2))
        return true;
@@ -105,7 +105,7 @@ stats_check_arg_pair(FunctionCallInfo fcinfo,
        int         nullarg = PG_ARGISNULL(argnum1) ? argnum1 : argnum2;
        int         otherarg = PG_ARGISNULL(argnum1) ? argnum2 : argnum1;
 
-       ereport(elevel,
+       ereport(WARNING,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("\"%s\" must be specified when \"%s\" is specified",
                        arginfo[nullarg].argname,
@@ -216,7 +216,7 @@ stats_lock_check_privileges(Oid reloid)
  * found.
  */
 static int
-get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo, int elevel)
+get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo)
 {
    int         argnum;
 
@@ -224,7 +224,7 @@ get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo, int elevel)
        if (pg_strcasecmp(argname, arginfo[argnum].argname) == 0)
            return argnum;
 
-   ereport(elevel,
+   ereport(WARNING,
            (errmsg("unrecognized argument name: \"%s\"", argname)));
 
    return -1;
@@ -234,11 +234,11 @@ get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo, int elevel)
  * Ensure that a given argument matched the expected type.
  */
 static bool
-stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype, int elevel)
+stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype)
 {
    if (argtype != expectedtype)
    {
-       ereport(elevel,
+       ereport(WARNING,
                (errmsg("argument \"%s\" has type \"%s\", expected type \"%s\"",
                        argname, format_type_be(argtype),
                        format_type_be(expectedtype))));
@@ -260,8 +260,7 @@ stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype, int ele
 bool
 stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
                                 FunctionCallInfo positional_fcinfo,
-                                struct StatsArgInfo *arginfo,
-                                int elevel)
+                                struct StatsArgInfo *arginfo)
 {
    Datum      *args;
    bool       *argnulls;
@@ -319,11 +318,10 @@ stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
        if (pg_strcasecmp(argname, "version") == 0)
            continue;
 
-       argnum = get_arg_by_name(argname, arginfo, elevel);
+       argnum = get_arg_by_name(argname, arginfo);
 
        if (argnum < 0 || !stats_check_arg_type(argname, types[i + 1],
-                                               arginfo[argnum].argtype,
-                                               elevel))
+                                               arginfo[argnum].argtype))
        {
            result = false;
            continue;
index f6a99472e769eea613caee585ff3adee0105067b..12163ae94be6a01c8ec857ef82c10ddd9afdf810 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202502241
+#define CATALOG_VERSION_NO 202502242
 
 #endif
index af9546de23df7e718e81617f7b776df7e699e283..1c1d96e0c7e81ac732160842578f7b43a4e2a8fb 100644 (file)
@@ -1,4 +1,4 @@
-#----------------------------------------------------------------------
+\#----------------------------------------------------------------------
 #
 # pg_proc.dat
 #    Initial contents of the pg_proc system catalog.
   proargnames => '{kwargs}',
   proargmodes => '{v}',
   prosrc => 'pg_restore_relation_stats' },
-{ oid => '8460',
+{ oid => '9160',
+  descr => 'clear statistics on relation',
+  proname => 'pg_clear_relation_stats', provolatile => 'v', proisstrict => 'f',
+  proparallel => 'u', prorettype => 'void',
+  proargtypes => 'regclass',
+  proargnames => '{relation}',
+  prosrc => 'pg_clear_relation_stats' },
+{ oid => '8461',
   descr => 'restore statistics on attribute',
   proname => 'pg_restore_attribute_stats', provolatile => 'v', proisstrict => 'f',
   provariadic => 'any',
   proargmodes => '{v}',
   prosrc => 'pg_restore_attribute_stats' },
 { oid => '9162',
-  descr => 'set statistics on attribute',
-  proname => 'pg_set_attribute_stats', provolatile => 'v', proisstrict => 'f',
-  proparallel => 'u', prorettype => 'void',
-  proargtypes => 'regclass name bool float4 int4 float4 text _float4 text float4 text _float4 _float4 text float4 text',
-  proargnames => '{relation,attname,inherited,null_frac,avg_width,n_distinct,most_common_vals,most_common_freqs,histogram_bounds,correlation,most_common_elems,most_common_elem_freqs,elem_count_histogram,range_length_histogram,range_empty_frac,range_bounds_histogram}',
-  prosrc => 'pg_set_attribute_stats' },
-{ oid => '9163',
   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}',
   prosrc => 'pg_clear_attribute_stats' },
-{ oid => '9944',
-  descr => 'set statistics on relation',
-  proname => 'pg_set_relation_stats', provolatile => 'v', proisstrict => 'f',
-  proparallel => 'u', prorettype => 'void',
-  proargtypes => 'regclass int4 float4 int4',
-  proargnames => '{relation,relpages,reltuples,relallvisible}',
-  prosrc => 'pg_set_relation_stats' },
-{ oid => '9945',
-  descr => 'clear statistics on relation',
-  proname => 'pg_clear_relation_stats', provolatile => 'v', proisstrict => 'f',
-  proparallel => 'u', prorettype => 'void',
-  proargtypes => 'regclass',
-  proargnames => '{relation}',
-  prosrc => 'pg_clear_relation_stats' },
 
 # GiST stratnum implementations
 { oid => '8047', descr => 'GiST support',
index 6edb5ea0321ff5be849002ad86ed1b79fa8379c1..0eb4decfcac7f8772d84e8f75ef2c4e98f24a0a0 100644 (file)
@@ -25,17 +25,15 @@ extern void stats_check_required_arg(FunctionCallInfo fcinfo,
                                     struct StatsArgInfo *arginfo,
                                     int argnum);
 extern bool stats_check_arg_array(FunctionCallInfo fcinfo,
-                                 struct StatsArgInfo *arginfo, int argnum,
-                                 int elevel);
+                                 struct StatsArgInfo *arginfo, int argnum);
 extern bool stats_check_arg_pair(FunctionCallInfo fcinfo,
                                 struct StatsArgInfo *arginfo,
-                                int argnum1, int argnum2, int elevel);
+                                int argnum1, int argnum2);
 
 extern void stats_lock_check_privileges(Oid reloid);
 
 extern bool stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
                                             FunctionCallInfo positional_fcinfo,
-                                            struct StatsArgInfo *arginfo,
-                                            int elevel);
+                                            struct StatsArgInfo *arginfo);
 
 #endif                         /* STATS_UTILS_H */
index d6713eacc2c61587449cadfdad4315b9f88c3e19..7e8b7f429c94e82c189de5ec892e5556111cc6be 100644 (file)
@@ -12,6 +12,7 @@ CREATE TABLE stats_import.test(
     arange int4range,
     tags text[]
 ) WITH (autovacuum_enabled = false);
+CREATE INDEX test_i ON stats_import.test(id);
 -- starting stats
 SELECT relpages, reltuples, relallvisible
 FROM pg_class
@@ -21,80 +22,15 @@ WHERE oid = 'stats_import.test'::regclass;
         0 |        -1 |             0
 (1 row)
 
--- error: regclass not found
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 0::Oid,
-        relpages => 17::integer,
-        reltuples => 400.0::real,
-        relallvisible => 4::integer);
-ERROR:  could not open relation with OID 0
--- relpages default
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => NULL::integer,
-        reltuples => 400.0::real,
-        relallvisible => 4::integer);
- pg_set_relation_stats 
------------------------
-(1 row)
-
--- reltuples default
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => 17::integer,
-        reltuples => NULL::real,
-        relallvisible => 4::integer);
- pg_set_relation_stats 
------------------------
-(1 row)
-
--- relallvisible default
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => 17::integer,
-        reltuples => 400.0::real,
-        relallvisible => NULL::integer);
- pg_set_relation_stats 
------------------------
-(1 row)
-
--- named arguments
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => 17::integer,
-        reltuples => 400.0::real,
-        relallvisible => 4::integer);
- pg_set_relation_stats 
------------------------
-(1 row)
-
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
- relpages | reltuples | relallvisible 
-----------+-----------+---------------
-       17 |       400 |             4
-(1 row)
-
-CREATE INDEX test_i ON stats_import.test(id);
 BEGIN;
 -- regular indexes have special case locking rules
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test_i'::regclass,
-        relpages => 18::integer);
- pg_set_relation_stats 
------------------------
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.test_i'::regclass,
+        'relpages', 18::integer);
+ pg_restore_relation_stats 
+---------------------------
+ t
 (1 row)
 
 SELECT mode FROM pg_locks
@@ -123,34 +59,6 @@ SELECT
  t
 (1 row)
 
--- positional arguments
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        'stats_import.test'::regclass,
-        18::integer,
-        401.0::real,
-        5::integer);
- pg_set_relation_stats 
------------------------
-(1 row)
-
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
- relpages | reltuples | relallvisible 
-----------+-----------+---------------
-       18 |       401 |             5
-(1 row)
-
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
- relpages | reltuples | relallvisible 
-----------+-----------+---------------
-       18 |       401 |             5
-(1 row)
-
 -- clear
 SELECT
     pg_catalog.pg_clear_relation_stats(
@@ -168,19 +76,6 @@ WHERE oid = 'stats_import.test'::regclass;
         0 |        -1 |             0
 (1 row)
 
--- invalid relkinds for statistics
-CREATE SEQUENCE stats_import.testseq;
-CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test;
-SELECT
-    pg_catalog.pg_clear_relation_stats(
-        'stats_import.testseq'::regclass);
-ERROR:  cannot modify statistics for relation "testseq"
-DETAIL:  This operation is not supported for sequences.
-SELECT
-    pg_catalog.pg_clear_relation_stats(
-        'stats_import.testview'::regclass);
-ERROR:  cannot modify statistics for relation "testview"
-DETAIL:  This operation is not supported for views.
 --  relpages may be -1 for partitioned tables
 CREATE TABLE stats_import.part_parent ( i integer ) PARTITION BY RANGE(i);
 CREATE TABLE stats_import.part_child_1
@@ -200,21 +95,21 @@ WHERE oid = 'stats_import.part_parent'::regclass;
 -- although partitioned tables have no storage, setting relpages to a
 -- positive value is still allowed
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent_i'::regclass,
-        relpages => 2::integer);
- pg_set_relation_stats 
------------------------
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent_i'::regclass,
+        'relpages', 2::integer);
+ pg_restore_relation_stats 
+---------------------------
+ t
 (1 row)
 
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent'::regclass,
-        relpages => 2::integer);
- pg_set_relation_stats 
------------------------
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent'::regclass,
+        'relpages', 2::integer);
+ pg_restore_relation_stats 
+---------------------------
+ t
 (1 row)
 
 --
@@ -225,12 +120,12 @@ SELECT
 --
 BEGIN;
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent_i'::regclass,
-        relpages => 2::integer);
- pg_set_relation_stats 
------------------------
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent_i'::regclass,
+        'relpages', 2::integer);
+ pg_restore_relation_stats 
+---------------------------
+ t
 (1 row)
 
 SELECT mode FROM pg_locks
@@ -261,527 +156,14 @@ SELECT
 
 -- nothing stops us from setting it to -1
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent'::regclass,
-        relpages => -1::integer);
- pg_set_relation_stats 
------------------------
-(1 row)
-
--- error: object doesn't exist
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => '0'::oid,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-ERROR:  could not open relation with OID 0
--- error: object doesn't exist
-SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => '0'::oid,
-    attname => 'id'::name,
-    inherited => false::boolean);
-ERROR:  could not open relation with OID 0
--- error: relation null
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => NULL::oid,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-ERROR:  "relation" cannot be NULL
--- error: attribute is system column
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'xmin'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-ERROR:  cannot modify statistics on system column "xmin"
--- error: attname doesn't exist
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'nope'::name,
-    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: attribute is system column
-SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'ctid'::name,
-    inherited => false::boolean);
-ERROR:  cannot clear statistics on system column "ctid"
--- error: attname doesn't exist
-SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'nope'::name,
-    inherited => false::boolean);
-ERROR:  column "nope" of relation "test" does not exist
--- error: attname null
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => NULL::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-ERROR:  "attname" cannot be NULL
--- error: inherited null
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => NULL::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-ERROR:  "inherited" cannot be NULL
--- ok: no stakinds
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT stanullfrac, stawidth, stadistinct
-FROM pg_statistic
-WHERE starelid = 'stats_import.test'::regclass;
- stanullfrac | stawidth | stadistinct 
--------------+----------+-------------
-         0.1 |        2 |         0.3
-(1 row)
-
--- error: mcv / mcf null mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_freqs => '{0.1,0.2,0.3}'::real[]
-    );
-ERROR:  "most_common_vals" must be specified when "most_common_freqs" is specified
--- error: mcv / mcf null mismatch part 2
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{1,2,3}'::text
-    );
-ERROR:  "most_common_freqs" must be specified when "most_common_vals" is specified
--- error: mcv / mcf type mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{2023-09-30,2024-10-31,3}'::text,
-    most_common_freqs => '{0.2,0.1}'::real[]
-    );
-ERROR:  invalid input syntax for type integer: "2023-09-30"
--- error: mcv cast failure
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{2,four,3}'::text,
-    most_common_freqs => '{0.3,0.25,0.05}'::real[]
-    );
-ERROR:  invalid input syntax for type integer: "four"
--- ok: mcv+mcf
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{2,1,3}'::text,
-    most_common_freqs => '{0.3,0.25,0.05}'::real[]
-    );
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'id';
-  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram 
---------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
- stats_import | test      | id      | f         |       0.5 |         2 |       -0.1 | {2,1,3}          | {0.3,0.25,0.05}   |                  |             |                   |                        |                      |                        |                  | 
-(1 row)
-
--- error: histogram elements null value
--- this generates no warnings, but perhaps it should
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    histogram_bounds => '{1,NULL,3,4}'::text
-    );
-ERROR:  "histogram_bounds" array cannot contain NULL values
--- ok: histogram_bounds
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    histogram_bounds => '{1,2,3,4}'::text
-    );
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'id';
-  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram 
---------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
- stats_import | test      | id      | f         |       0.5 |         2 |       -0.1 | {2,1,3}          | {0.3,0.25,0.05}   | {1,2,3,4}        |             |                   |                        |                      |                        |                  | 
-(1 row)
-
--- ok: correlation
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    correlation => 0.5::real);
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'id';
-  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram 
---------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
- stats_import | test      | id      | f         |       0.5 |         2 |       -0.1 | {2,1,3}          | {0.3,0.25,0.05}   | {1,2,3,4}        |         0.5 |                   |                        |                      |                        |                  | 
-(1 row)
-
--- error: scalars can't have mcelem
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elems => '{1,3}'::text,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3,0.0}'::real[]
-    );
-ERROR:  unable to determine element type of attribute "id"
-DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
--- error: mcelem / mcelem mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elems => '{one,two}'::text
-    );
-ERROR:  "most_common_elem_freqs" must be specified when "most_common_elems" is specified
--- error: mcelem / mcelem null mismatch part 2
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3}'::real[]
-    );
-ERROR:  "most_common_elems" must be specified when "most_common_elem_freqs" is specified
--- ok: mcelem
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elems => '{one,three}'::text,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3,0.0}'::real[]
-    );
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'tags';
-  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram 
---------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
- stats_import | test      | tags    | f         |       0.5 |         2 |       -0.1 |                  |                   |                  |             | {one,three}       | {0.3,0.2,0.2,0.3,0}    |                      |                        |                  | 
-(1 row)
-
--- error: scalars can't have elem_count_histogram
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::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[]
-    );
-ERROR:  unable to determine element type of attribute "id"
-DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
--- error: elem_count_histogram null element
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    elem_count_histogram => '{1,1,NULL,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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[]
-    );
-ERROR:  "elem_count_histogram" array cannot contain NULL values
--- ok: elem_count_histogram
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::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[]
-    );
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'tags';
-  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs |                                                                                            elem_count_histogram                                                                                             | range_length_histogram | range_empty_frac | range_bounds_histogram 
---------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------+------------------+------------------------
- stats_import | test      | tags    | f         |       0.5 |         2 |       -0.1 |                  |                   |                  |             | {one,three}       | {0.3,0.2,0.2,0.3,0}    | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} |                        |                  | 
-(1 row)
-
--- error: scalars can't have range stats
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_empty_frac => 0.5::real,
-    range_length_histogram => '{399,499,Infinity}'::text
-    );
-ERROR:  attribute "id" is not a range type
-DETAIL:  Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM.
--- error: range_empty_frac range_length_hist null mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_length_histogram => '{399,499,Infinity}'::text
-    );
-ERROR:  "range_empty_frac" must be specified when "range_length_histogram" is specified
--- error: range_empty_frac range_length_hist null mismatch part 2
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_empty_frac => 0.5::real
-    );
-ERROR:  "range_length_histogram" must be specified when "range_empty_frac" is specified
--- ok: range_empty_frac + range_length_hist
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_empty_frac => 0.5::real,
-    range_length_histogram => '{399,499,Infinity}'::text
-    );
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'arange';
-  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram 
---------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
- stats_import | test      | arange  | f         |       0.5 |         2 |       -0.1 |                  |                   |                  |             |                   |                        |                      | {399,499,Infinity}     |              0.5 | 
-(1 row)
-
--- error: scalars can't have range stats
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_bounds_histogram => '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
-    );
-ERROR:  attribute "id" is not a range type
-DETAIL:  Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM.
--- ok: range_bounds_histogram
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_bounds_histogram => '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
-    );
- pg_set_attribute_stats 
-------------------------
-(1 row)
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'arange';
-  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac |        range_bounds_histogram        
---------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+--------------------------------------
- stats_import | test      | arange  | f         |       0.5 |         2 |       -0.1 |                  |                   |                  |             |                   |                        |                      | {399,499,Infinity}     |              0.5 | {"[-1,1)","[0,4)","[1,4)","[1,100)"}
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent'::regclass,
+        'relpages', -1::integer);
+ pg_restore_relation_stats 
+---------------------------
+ t
 (1 row)
 
--- error: cannot set most_common_elems for range type
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{"[2,3)","[1,2)","[3,4)"}'::text,
-    most_common_freqs => '{0.3,0.25,0.05}'::real[],
-    histogram_bounds => '{"[1,2)","[2,3)","[3,4)","[4,5)"}'::text,
-    correlation => 1.1::real,
-    most_common_elems => '{3,1}'::text,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3,0.0}'::real[],
-    range_empty_frac => -0.5::real,
-    range_length_histogram => '{399,499,Infinity}'::text,
-    range_bounds_histogram => '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
-    );
-ERROR:  unable to determine element type of attribute "arange"
-DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
---
--- Clear attribute stats to try again with restore functions
--- (relation stats were already cleared).
---
-SELECT
-  pg_catalog.pg_clear_attribute_stats(
-        'stats_import.test'::regclass,
-        s.attname,
-        s.inherited)
-FROM pg_catalog.pg_stats AS s
-WHERE s.schemaname = 'stats_import'
-AND s.tablename = 'test'
-ORDER BY s.attname, s.inherited;
- pg_clear_attribute_stats 
---------------------------
-(3 rows)
-
--- reject: argument name is NULL
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        NULL, '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible', 4::integer);
-ERROR:  name at variadic position 5 is NULL
--- reject: argument name is an integer
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        17, '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible', 4::integer);
-ERROR:  name at variadic position 5 has type "integer", expected type "text"
--- reject: odd number of variadic arguments cannot be pairs
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        'relpages', '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible');
-ERROR:  variadic arguments must be name/value pairs
-HINT:  Provide an even number of variadic arguments that can be divided into pairs.
--- reject: object doesn't exist
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        'relpages', '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible', 4::integer);
-ERROR:  could not open relation with OID 0
 -- ok: set all stats
 SELECT pg_restore_relation_stats(
         'relation', 'stats_import.test'::regclass,
@@ -856,15 +238,6 @@ WHERE oid = 'stats_import.test'::regclass;
        16 |       500 |             5
 (1 row)
 
--- warn and error: unrecognized argument name
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        'relpages', '17'::integer,
-        'reltuples', 400::real,
-        'nope', 4::integer);
-WARNING:  unrecognized argument name: "nope"
-ERROR:  could not open relation with OID 0
 -- warn: bad relpages type
 SELECT pg_restore_relation_stats(
         'relation', 'stats_import.test'::regclass,
@@ -886,56 +259,19 @@ WHERE oid = 'stats_import.test'::regclass;
        16 |       400 |             4
 (1 row)
 
--- error: object does not exist
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', '0'::oid::regclass,
-    'attname', 'id'::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-ERROR:  could not open relation with OID 0
--- error: relation null
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', NULL::oid,
-    'attname', 'id'::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-ERROR:  "relation" cannot be NULL
--- error: attname null
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', NULL::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-ERROR:  "attname" cannot be NULL
--- error: attname doesn't exist
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'nope'::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-ERROR:  column "nope" of relation "test" does not exist
--- error: inherited null
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
-    'inherited', NULL::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-ERROR:  "inherited" cannot be NULL
+-- invalid relkinds for statistics
+CREATE SEQUENCE stats_import.testseq;
+CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test;
+SELECT
+    pg_catalog.pg_clear_relation_stats(
+        'stats_import.testseq'::regclass);
+ERROR:  cannot modify statistics for relation "testseq"
+DETAIL:  This operation is not supported for sequences.
+SELECT
+    pg_catalog.pg_clear_relation_stats(
+        'stats_import.testview'::regclass);
+ERROR:  cannot modify statistics for relation "testview"
+DETAIL:  This operation is not supported for views.
 -- ok: no stakinds
 SELECT pg_catalog.pg_restore_attribute_stats(
     'relation', 'stats_import.test'::regclass,
@@ -1409,24 +745,24 @@ AND attname = 'arange';
  stats_import | test      | arange  | f         |       0.2 |         3 |      -0.39 |                  |                   |                  |             |                   |                        |                      | {399,499,Infinity}     |              0.5 | {"[-1,1)","[0,4)","[1,4)","[1,100)"}
 (1 row)
 
--- warn: too many stat kinds
+-- warn: cannot set most_common_elems for range type
 SELECT pg_catalog.pg_restore_attribute_stats(
     'relation', 'stats_import.test'::regclass,
     'attname', 'arange'::name,
     'inherited', false::boolean,
-    'version', 150000::integer,
     'null_frac', 0.5::real,
     'avg_width', 2::integer,
     'n_distinct', -0.1::real,
-    'most_common_vals', '{"[2,3)","[1,3)","[3,9)"}'::text,
+    'most_common_vals', '{"[2,3)","[1,2)","[3,4)"}'::text,
     'most_common_freqs', '{0.3,0.25,0.05}'::real[],
-    'histogram_bounds', '{"[1,2)","[2,3)","[3,4)","[4,)"}'::text,
+    'histogram_bounds', '{"[1,2)","[2,3)","[3,4)","[4,5)"}'::text,
     'correlation', 1.1::real,
     'most_common_elems', '{3,1}'::text,
     'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[],
     'range_empty_frac', -0.5::real,
     'range_length_histogram', '{399,499,Infinity}'::text,
-    'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text);
+    'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
+    );
 WARNING:  unable to determine element type of attribute "arange"
 DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
  pg_restore_attribute_stats 
@@ -1434,251 +770,215 @@ DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
  f
 (1 row)
 
---
--- Test the ability to exactly copy data from one table to an identical table,
--- correctly reconstructing the stakind order as well as the staopN and
--- stacollN values. Because oids are not stable across databases, we can only
--- test this when the source and destination are on the same database
--- instance. For that reason, we borrow and adapt a query found in fe_utils
--- and used by pg_dump/pg_upgrade.
---
-INSERT INTO stats_import.test
-SELECT 1, 'one', (1, 1.1, 'ONE', '2001-01-01', '{ "xkey": "xval" }')::stats_import.complex_type, int4range(1,4), array['red','green']
-UNION ALL
-SELECT 2, 'two', (2, 2.2, 'TWO', '2002-02-02', '[true, 4, "six"]')::stats_import.complex_type,  int4range(1,4), array['blue','yellow']
-UNION ALL
-SELECT 3, 'tre', (3, 3.3, 'TRE', '2003-03-03', NULL)::stats_import.complex_type, int4range(-1,1), array['"orange"', 'purple', 'cyan']
-UNION ALL
-SELECT 4, 'four', NULL, int4range(0,100), NULL;
-CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
--- restoring stats on index
-SELECT * FROM pg_catalog.pg_restore_relation_stats(
-    'relation', 'stats_import.is_odd'::regclass,
-    'version', '180000'::integer,
-    'relpages', '11'::integer,
-    'reltuples', '10000'::real,
-    'relallvisible', '0'::integer
-);
- pg_restore_relation_stats 
----------------------------
- t
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'arange';
+  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct |     most_common_vals      | most_common_freqs |         histogram_bounds          | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac |        range_bounds_histogram        
+--------------+-----------+---------+-----------+-----------+-----------+------------+---------------------------+-------------------+-----------------------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+--------------------------------------
+ stats_import | test      | arange  | f         |       0.5 |         2 |       -0.1 | {"[2,3)","[1,2)","[3,4)"} | {0.3,0.25,0.05}   | {"[1,2)","[2,3)","[3,4)","[4,5)"} |         1.1 |                   |                        |                      | {399,499,Infinity}     |             -0.5 | {"[-1,1)","[0,4)","[1,4)","[1,100)"}
 (1 row)
 
--- Generate statistics on table with data
-ANALYZE stats_import.test;
-CREATE TABLE stats_import.test_clone ( LIKE stats_import.test )
-    WITH (autovacuum_enabled = false);
-CREATE INDEX is_odd_clone ON stats_import.test_clone(((comp).a % 2 = 1));
---
--- Copy stats from test to test_clone, and is_odd to is_odd_clone
---
-SELECT s.schemaname, s.tablename, s.attname, s.inherited
-FROM pg_catalog.pg_stats AS s
-CROSS JOIN LATERAL
-    pg_catalog.pg_set_attribute_stats(
-        relation => ('stats_import.' || s.tablename || '_clone')::regclass::oid,
-        attname => s.attname,
-        inherited => s.inherited,
-        null_frac => s.null_frac,
-        avg_width => s.avg_width,
-        n_distinct => s.n_distinct,
-        most_common_vals => s.most_common_vals::text,
-        most_common_freqs => s.most_common_freqs,
-        histogram_bounds => s.histogram_bounds::text,
-        correlation => s.correlation,
-        most_common_elems => s.most_common_elems::text,
-        most_common_elem_freqs => s.most_common_elem_freqs,
-        elem_count_histogram => s.elem_count_histogram,
-        range_bounds_histogram => s.range_bounds_histogram::text,
-        range_empty_frac => s.range_empty_frac,
-        range_length_histogram => s.range_length_histogram::text) AS r
-WHERE s.schemaname = 'stats_import'
-AND s.tablename IN ('test', 'is_odd')
-ORDER BY s.tablename, s.attname, s.inherited;
-  schemaname  | tablename | attname | inherited 
---------------+-----------+---------+-----------
- stats_import | is_odd    | expr    | f
- stats_import | test      | arange  | f
- stats_import | test      | comp    | f
- stats_import | test      | id      | f
- stats_import | test      | name    | f
- stats_import | test      | tags    | f
-(6 rows)
+-- warn: scalars can't have mcelem
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elems', '{1,3}'::text,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[]
+    );
+WARNING:  unable to determine element type of attribute "id"
+DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
 
-SELECT c.relname, COUNT(*) AS num_stats
-FROM pg_class AS c
-JOIN pg_statistic s ON s.starelid = c.oid
-WHERE c.relnamespace = 'stats_import'::regnamespace
-AND c.relname IN ('test', 'test_clone', 'is_odd', 'is_odd_clone')
-GROUP BY c.relname
-ORDER BY c.relname;
-   relname    | num_stats 
---------------+-----------
- is_odd       |         1
- is_odd_clone |         1
- test         |         5
- test_clone   |         5
-(4 rows)
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'id';
+  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram 
+--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
+ stats_import | test      | id      | f         |       0.5 |         2 |       -0.1 | {2,1,3}          | {0.3,0.25,0.05}   | {1,2,3,4}        |             |                   |                        |                      |                        |                  | 
+(1 row)
 
--- check test minus test_clone
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test_clone'::regclass;
- attname | stainherit | stanullfrac | stawidth | stadistinct | stakind1 | stakind2 | stakind3 | stakind4 | stakind5 | staop1 | staop2 | staop3 | staop4 | staop5 | stacoll1 | stacoll2 | stacoll3 | stacoll4 | stacoll5 | stanumbers1 | stanumbers2 | stanumbers3 | stanumbers4 | stanumbers5 | sv1 | sv2 | sv3 | sv4 | sv5 | direction 
----------+------------+-------------+----------+-------------+----------+----------+----------+----------+----------+--------+--------+--------+--------+--------+----------+----------+----------+----------+----------+-------------+-------------+-------------+-------------+-------------+-----+-----+-----+-----+-----+-----------
-(0 rows)
+-- warn: mcelem / mcelem mismatch
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'tags'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elems', '{one,two}'::text
+    );
+WARNING:  "most_common_elem_freqs" must be specified when "most_common_elems" is specified
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
 
--- check test_clone minus test
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test_clone'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test'::regclass;
- attname | stainherit | stanullfrac | stawidth | stadistinct | stakind1 | stakind2 | stakind3 | stakind4 | stakind5 | staop1 | staop2 | staop3 | staop4 | staop5 | stacoll1 | stacoll2 | stacoll3 | stacoll4 | stacoll5 | stanumbers1 | stanumbers2 | stanumbers3 | stanumbers4 | stanumbers5 | sv1 | sv2 | sv3 | sv4 | sv5 | direction 
----------+------------+-------------+----------+-------------+----------+----------+----------+----------+----------+--------+--------+--------+--------+--------+----------+----------+----------+----------+----------+-------------+-------------+-------------+-------------+-------------+-----+-----+-----+-----+-----+-----------
-(0 rows)
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'tags';
+  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs |                                                                                            elem_count_histogram                                                                                             | range_length_histogram | range_empty_frac | range_bounds_histogram 
+--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------+------------------+------------------------
+ stats_import | test      | tags    | f         |       0.5 |         2 |       -0.1 |                  |                   |                  |             |                   |                        | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} |                        |                  | 
+(1 row)
 
--- check is_odd minus is_odd_clone
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd_clone'::regclass;
- attname | stainherit | stanullfrac | stawidth | stadistinct | stakind1 | stakind2 | stakind3 | stakind4 | stakind5 | staop1 | staop2 | staop3 | staop4 | staop5 | stacoll1 | stacoll2 | stacoll3 | stacoll4 | stacoll5 | stanumbers1 | stanumbers2 | stanumbers3 | stanumbers4 | stanumbers5 | sv1 | sv2 | sv3 | sv4 | sv5 | direction 
----------+------------+-------------+----------+-------------+----------+----------+----------+----------+----------+--------+--------+--------+--------+--------+----------+----------+----------+----------+----------+-------------+-------------+-------------+-------------+-------------+-----+-----+-----+-----+-----+-----------
-(0 rows)
+-- warn: mcelem / mcelem null mismatch part 2
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'tags'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3}'::real[]
+    );
+WARNING:  "most_common_elems" must be specified when "most_common_elem_freqs" is specified
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+-- ok: mcelem
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'tags'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elems', '{one,three}'::text,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[]
+    );
+ pg_restore_attribute_stats 
+----------------------------
+ t
+(1 row)
+
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'tags';
+  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs |                                                                                            elem_count_histogram                                                                                             | range_length_histogram | range_empty_frac | range_bounds_histogram 
+--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------+------------------+------------------------
+ stats_import | test      | tags    | f         |       0.5 |         2 |       -0.1 |                  |                   |                  |             | {one,three}       | {0.3,0.2,0.2,0.3,0}    | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} |                        |                  | 
+(1 row)
+
+-- warn: scalars can't have elem_count_histogram
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::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[]
+    );
+WARNING:  unable to determine element type of attribute "id"
+DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
 
--- check is_odd_clone minus is_odd
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd_clone'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd'::regclass;
- attname | stainherit | stanullfrac | stawidth | stadistinct | stakind1 | stakind2 | stakind3 | stakind4 | stakind5 | staop1 | staop2 | staop3 | staop4 | staop5 | stacoll1 | stacoll2 | stacoll3 | stacoll4 | stacoll5 | stanumbers1 | stanumbers2 | stanumbers3 | stanumbers4 | stanumbers5 | sv1 | sv2 | sv3 | sv4 | sv5 | direction 
----------+------------+-------------+----------+-------------+----------+----------+----------+----------+----------+--------+--------+--------+--------+--------+----------+----------+----------+----------+----------+-------------+-------------+-------------+-------------+-------------+-----+-----+-----+-----+-----+-----------
-(0 rows)
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'id';
+  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram 
+--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
+ stats_import | test      | id      | f         |       0.5 |         2 |       -0.1 | {2,1,3}          | {0.3,0.25,0.05}   | {1,2,3,4}        |             |                   |                        |                      |                        |                  | 
+(1 row)
 
---
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
- relpages | reltuples | relallvisible 
-----------+-----------+---------------
-        1 |         4 |             0
+-- warn: too many stat kinds
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'arange'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_vals', '{"[2,3)","[1,3)","[3,9)"}'::text,
+    'most_common_freqs', '{0.3,0.25,0.05}'::real[],
+    'histogram_bounds', '{"[1,2)","[2,3)","[3,4)","[4,)"}'::text,
+    'correlation', 1.1::real,
+    'most_common_elems', '{3,1}'::text,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[],
+    'range_empty_frac', -0.5::real,
+    'range_length_histogram', '{399,499,Infinity}'::text,
+    'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text);
+WARNING:  unable to determine element type of attribute "arange"
+DETAIL:  Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.
+ pg_restore_attribute_stats 
+----------------------------
+ f
+(1 row)
+
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'arange';
+  schemaname  | tablename | attname | inherited | null_frac | avg_width | n_distinct |     most_common_vals      | most_common_freqs |         histogram_bounds         | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac |        range_bounds_histogram        
+--------------+-----------+---------+-----------+-----------+-----------+------------+---------------------------+-------------------+----------------------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+--------------------------------------
+ stats_import | test      | arange  | f         |       0.5 |         2 |       -0.1 | {"[2,3)","[1,3)","[3,9)"} | {0.3,0.25,0.05}   | {"[1,2)","[2,3)","[3,4)","[4,)"} |         1.1 |                   |                        |                      | {399,499,Infinity}     |             -0.5 | {"[-1,1)","[0,4)","[1,4)","[1,100)"}
 (1 row)
 
 --
--- Clear clone stats to try again with pg_restore_attribute_stats
+-- Test the ability to exactly copy data from one table to an identical table,
+-- correctly reconstructing the stakind order as well as the staopN and
+-- stacollN values. Because oids are not stable across databases, we can only
+-- test this when the source and destination are on the same database
+-- instance. For that reason, we borrow and adapt a query found in fe_utils
+-- and used by pg_dump/pg_upgrade.
 --
-SELECT
-  pg_catalog.pg_clear_attribute_stats(
-        ('stats_import.' || s.tablename)::regclass,
-        s.attname,
-        s.inherited)
-FROM pg_catalog.pg_stats AS s
-WHERE s.schemaname = 'stats_import'
-AND s.tablename IN ('test_clone', 'is_odd_clone')
-ORDER BY s.tablename, s.attname, s.inherited;
- pg_clear_attribute_stats 
---------------------------
-(6 rows)
+INSERT INTO stats_import.test
+SELECT 1, 'one', (1, 1.1, 'ONE', '2001-01-01', '{ "xkey": "xval" }')::stats_import.complex_type, int4range(1,4), array['red','green']
+UNION ALL
+SELECT 2, 'two', (2, 2.2, 'TWO', '2002-02-02', '[true, 4, "six"]')::stats_import.complex_type,  int4range(1,4), array['blue','yellow']
+UNION ALL
+SELECT 3, 'tre', (3, 3.3, 'TRE', '2003-03-03', NULL)::stats_import.complex_type, int4range(-1,1), array['"orange"', 'purple', 'cyan']
+UNION ALL
+SELECT 4, 'four', NULL, int4range(0,100), NULL;
+CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
+-- restoring stats on index
+SELECT * FROM pg_catalog.pg_restore_relation_stats(
+    'relation', 'stats_import.is_odd'::regclass,
+    'version', '180000'::integer,
+    'relpages', '11'::integer,
+    'reltuples', '10000'::real,
+    'relallvisible', '0'::integer
+);
+ pg_restore_relation_stats 
+---------------------------
+ t
+(1 row)
 
-SELECT
-SELECT COUNT(*)
-FROM pg_catalog.pg_stats AS s
-WHERE s.schemaname = 'stats_import'
-AND s.tablename IN ('test_clone', 'is_odd_clone');
-ERROR:  syntax error at or near "SELECT"
-LINE 2: SELECT COUNT(*)
-        ^
+-- Generate statistics on table with data
+ANALYZE stats_import.test;
+CREATE TABLE stats_import.test_clone ( LIKE stats_import.test )
+    WITH (autovacuum_enabled = false);
+CREATE INDEX is_odd_clone ON stats_import.test_clone(((comp).a % 2 = 1));
 --
 -- Copy stats from test to test_clone, and is_odd to is_odd_clone
 --
@@ -1851,11 +1151,158 @@ WHERE s.starelid = 'stats_import.is_odd'::regclass;
 ---------+------------+-------------+----------+-------------+----------+----------+----------+----------+----------+--------+--------+--------+--------+--------+----------+----------+----------+----------+----------+-------------+-------------+-------------+-------------+-------------+-----+-----+-----+-----+-----+-----------
 (0 rows)
 
+-- ok
+SELECT pg_catalog.pg_clear_attribute_stats(
+    relation => 'stats_import.test'::regclass,
+    attname => 'arange'::name,
+    inherited => false::boolean);
+ pg_clear_attribute_stats 
+--------------------------
+(1 row)
+
+--
+-- Negative tests
+--
+--- error: relation is wrong type
+SELECT pg_catalog.pg_restore_relation_stats(
+        'relation', 0::oid,
+        'relpages', 17::integer,
+        'reltuples', 400.0::real,
+        'relallvisible', 4::integer);
+WARNING:  argument "relation" has type "oid", expected type "regclass"
+ERROR:  "relation" cannot be NULL
+--- error: relation not found
+SELECT pg_catalog.pg_restore_relation_stats(
+        'relation', 0::regclass,
+        'relpages', 17::integer,
+        'reltuples', 400.0::real,
+        'relallvisible', 4::integer);
+ERROR:  could not open relation with OID 0
+-- warn and error: unrecognized argument name
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        'relpages', '17'::integer,
+        'reltuples', 400::real,
+        'nope', 4::integer);
+WARNING:  unrecognized argument name: "nope"
+ERROR:  could not open relation with OID 0
+-- error: argument name is NULL
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        NULL, '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible', 4::integer);
+ERROR:  name at variadic position 5 is NULL
+-- error: argument name is an integer
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        17, '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible', 4::integer);
+ERROR:  name at variadic position 5 has type "integer", expected type "text"
+-- error: odd number of variadic arguments cannot be pairs
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        'relpages', '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible');
+ERROR:  variadic arguments must be name/value pairs
+HINT:  Provide an even number of variadic arguments that can be divided into pairs.
+-- error: object doesn't exist
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        'relpages', '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible', 4::integer);
+ERROR:  could not open relation with OID 0
+-- error: object does not exist
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', '0'::oid::regclass,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+ERROR:  could not open relation with OID 0
+-- error: relation null
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', NULL::oid,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+ERROR:  "relation" cannot be NULL
+-- error: attname null
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', NULL::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+ERROR:  "attname" cannot be NULL
+-- error: attname doesn't exist
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'nope'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+ERROR:  column "nope" of relation "test" does not exist
+-- error: attribute is system column
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'xmin'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::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,
+    'inherited', NULL::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+ERROR:  "inherited" cannot be NULL
+-- error: relation not found
+SELECT pg_catalog.pg_clear_relation_stats(
+    relation => 'stats_import.nope'::regclass);
+ERROR:  relation "stats_import.nope" does not exist
+LINE 2:     relation => 'stats_import.nope'::regclass);
+                        ^
+-- error: attribute is system column
+SELECT pg_catalog.pg_clear_attribute_stats(
+    relation => 'stats_import.test'::regclass,
+    attname => 'ctid'::name,
+    inherited => false::boolean);
+ERROR:  cannot clear statistics on system column "ctid"
+-- error: attname doesn't exist
+SELECT pg_catalog.pg_clear_attribute_stats(
+    relation => 'stats_import.test'::regclass,
+    attname => 'nope'::name,
+    inherited => false::boolean);
+ERROR:  column "nope" of relation "test" does not exist
 DROP SCHEMA stats_import CASCADE;
 NOTICE:  drop cascades to 6 other objects
 DETAIL:  drop cascades to type stats_import.complex_type
 drop cascades to table stats_import.test
+drop cascades to table stats_import.part_parent
 drop cascades to sequence stats_import.testseq
 drop cascades to view stats_import.testview
-drop cascades to table stats_import.part_parent
 drop cascades to table stats_import.test_clone
index 9740ab3ff0254fc1833def732474611335c3f1ca..57422750b90758d575b11c48f0924c29aba2bcd8 100644 (file)
@@ -15,63 +15,19 @@ CREATE TABLE stats_import.test(
     tags text[]
 ) WITH (autovacuum_enabled = false);
 
--- starting stats
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
-
--- error: regclass not found
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 0::Oid,
-        relpages => 17::integer,
-        reltuples => 400.0::real,
-        relallvisible => 4::integer);
-
--- relpages default
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => NULL::integer,
-        reltuples => 400.0::real,
-        relallvisible => 4::integer);
-
--- reltuples default
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => 17::integer,
-        reltuples => NULL::real,
-        relallvisible => 4::integer);
-
--- relallvisible default
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => 17::integer,
-        reltuples => 400.0::real,
-        relallvisible => NULL::integer);
-
--- named arguments
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test'::regclass,
-        relpages => 17::integer,
-        reltuples => 400.0::real,
-        relallvisible => 4::integer);
+CREATE INDEX test_i ON stats_import.test(id);
 
+-- starting stats
 SELECT relpages, reltuples, relallvisible
 FROM pg_class
 WHERE oid = 'stats_import.test'::regclass;
 
-CREATE INDEX test_i ON stats_import.test(id);
-
 BEGIN;
 -- regular indexes have special case locking rules
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.test_i'::regclass,
-        relpages => 18::integer);
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.test_i'::regclass,
+        'relpages', 18::integer);
 
 SELECT mode FROM pg_locks
 WHERE relation = 'stats_import.test'::regclass AND
@@ -88,22 +44,6 @@ SELECT
         'relation', 'stats_import.test_i'::regclass,
         'relpages', 19::integer );
 
--- positional arguments
-SELECT
-    pg_catalog.pg_set_relation_stats(
-        'stats_import.test'::regclass,
-        18::integer,
-        401.0::real,
-        5::integer);
-
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
-
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
-
 -- clear
 SELECT
     pg_catalog.pg_clear_relation_stats(
@@ -113,16 +53,6 @@ SELECT relpages, reltuples, relallvisible
 FROM pg_class
 WHERE oid = 'stats_import.test'::regclass;
 
--- invalid relkinds for statistics
-CREATE SEQUENCE stats_import.testseq;
-CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test;
-SELECT
-    pg_catalog.pg_clear_relation_stats(
-        'stats_import.testseq'::regclass);
-SELECT
-    pg_catalog.pg_clear_relation_stats(
-        'stats_import.testview'::regclass);
-
 --  relpages may be -1 for partitioned tables
 CREATE TABLE stats_import.part_parent ( i integer ) PARTITION BY RANGE(i);
 CREATE TABLE stats_import.part_child_1
@@ -141,14 +71,14 @@ WHERE oid = 'stats_import.part_parent'::regclass;
 -- although partitioned tables have no storage, setting relpages to a
 -- positive value is still allowed
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent_i'::regclass,
-        relpages => 2::integer);
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent_i'::regclass,
+        'relpages', 2::integer);
 
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent'::regclass,
-        relpages => 2::integer);
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent'::regclass,
+        'relpages', 2::integer);
 
 --
 -- Partitioned indexes aren't analyzed but it is possible to set
@@ -159,9 +89,9 @@ SELECT
 BEGIN;
 
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent_i'::regclass,
-        relpages => 2::integer);
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent_i'::regclass,
+        'relpages', 2::integer);
 
 SELECT mode FROM pg_locks
 WHERE relation = 'stats_import.part_parent'::regclass AND
@@ -180,440 +110,9 @@ SELECT
 
 -- nothing stops us from setting it to -1
 SELECT
-    pg_catalog.pg_set_relation_stats(
-        relation => 'stats_import.part_parent'::regclass,
-        relpages => -1::integer);
-
--- error: object doesn't exist
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => '0'::oid,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-
--- error: object doesn't exist
-SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => '0'::oid,
-    attname => 'id'::name,
-    inherited => false::boolean);
-
--- error: relation null
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => NULL::oid,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-
--- error: attribute is system column
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'xmin'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-
--- error: attname doesn't exist
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'nope'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-
--- error: attribute is system column
-SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'ctid'::name,
-    inherited => false::boolean);
-
--- error: attname doesn't exist
-SELECT pg_catalog.pg_clear_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'nope'::name,
-    inherited => false::boolean);
-
--- error: attname null
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => NULL::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-
--- error: inherited null
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => NULL::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-
--- ok: no stakinds
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.1::real,
-    avg_width => 2::integer,
-    n_distinct => 0.3::real);
-
-SELECT stanullfrac, stawidth, stadistinct
-FROM pg_statistic
-WHERE starelid = 'stats_import.test'::regclass;
-
--- error: mcv / mcf null mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_freqs => '{0.1,0.2,0.3}'::real[]
-    );
-
--- error: mcv / mcf null mismatch part 2
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{1,2,3}'::text
-    );
-
--- error: mcv / mcf type mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{2023-09-30,2024-10-31,3}'::text,
-    most_common_freqs => '{0.2,0.1}'::real[]
-    );
-
--- error: mcv cast failure
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{2,four,3}'::text,
-    most_common_freqs => '{0.3,0.25,0.05}'::real[]
-    );
-
--- ok: mcv+mcf
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{2,1,3}'::text,
-    most_common_freqs => '{0.3,0.25,0.05}'::real[]
-    );
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'id';
-
--- error: histogram elements null value
--- this generates no warnings, but perhaps it should
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    histogram_bounds => '{1,NULL,3,4}'::text
-    );
-
--- ok: histogram_bounds
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    histogram_bounds => '{1,2,3,4}'::text
-    );
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'id';
-
--- ok: correlation
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    correlation => 0.5::real);
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'id';
-
--- error: scalars can't have mcelem
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elems => '{1,3}'::text,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3,0.0}'::real[]
-    );
-
--- error: mcelem / mcelem mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elems => '{one,two}'::text
-    );
-
--- error: mcelem / mcelem null mismatch part 2
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3}'::real[]
-    );
-
--- ok: mcelem
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_elems => '{one,three}'::text,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3,0.0}'::real[]
-    );
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'tags';
-
--- error: scalars can't have elem_count_histogram
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::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[]
-    );
--- error: elem_count_histogram null element
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    elem_count_histogram => '{1,1,NULL,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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[]
-    );
--- ok: elem_count_histogram
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'tags'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::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[]
-    );
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'tags';
-
--- error: scalars can't have range stats
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_empty_frac => 0.5::real,
-    range_length_histogram => '{399,499,Infinity}'::text
-    );
--- error: range_empty_frac range_length_hist null mismatch
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_length_histogram => '{399,499,Infinity}'::text
-    );
--- error: range_empty_frac range_length_hist null mismatch part 2
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_empty_frac => 0.5::real
-    );
--- ok: range_empty_frac + range_length_hist
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_empty_frac => 0.5::real,
-    range_length_histogram => '{399,499,Infinity}'::text
-    );
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'arange';
-
--- error: scalars can't have range stats
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'id'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_bounds_histogram => '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
-    );
--- ok: range_bounds_histogram
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    range_bounds_histogram => '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
-    );
-
-SELECT *
-FROM pg_stats
-WHERE schemaname = 'stats_import'
-AND tablename = 'test'
-AND inherited = false
-AND attname = 'arange';
-
--- error: cannot set most_common_elems for range type
-SELECT pg_catalog.pg_set_attribute_stats(
-    relation => 'stats_import.test'::regclass,
-    attname => 'arange'::name,
-    inherited => false::boolean,
-    null_frac => 0.5::real,
-    avg_width => 2::integer,
-    n_distinct => -0.1::real,
-    most_common_vals => '{"[2,3)","[1,2)","[3,4)"}'::text,
-    most_common_freqs => '{0.3,0.25,0.05}'::real[],
-    histogram_bounds => '{"[1,2)","[2,3)","[3,4)","[4,5)"}'::text,
-    correlation => 1.1::real,
-    most_common_elems => '{3,1}'::text,
-    most_common_elem_freqs => '{0.3,0.2,0.2,0.3,0.0}'::real[],
-    range_empty_frac => -0.5::real,
-    range_length_histogram => '{399,499,Infinity}'::text,
-    range_bounds_histogram => '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
-    );
-
---
--- Clear attribute stats to try again with restore functions
--- (relation stats were already cleared).
---
-SELECT
-  pg_catalog.pg_clear_attribute_stats(
-        'stats_import.test'::regclass,
-        s.attname,
-        s.inherited)
-FROM pg_catalog.pg_stats AS s
-WHERE s.schemaname = 'stats_import'
-AND s.tablename = 'test'
-ORDER BY s.attname, s.inherited;
-
--- reject: argument name is NULL
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        NULL, '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible', 4::integer);
-
--- reject: argument name is an integer
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        17, '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible', 4::integer);
-
--- reject: odd number of variadic arguments cannot be pairs
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        'relpages', '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible');
-
--- reject: object doesn't exist
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        'relpages', '17'::integer,
-        'reltuples', 400::real,
-        'relallvisible', 4::integer);
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.part_parent'::regclass,
+        'relpages', -1::integer);
 
 -- ok: set all stats
 SELECT pg_restore_relation_stats(
@@ -657,14 +156,6 @@ SELECT relpages, reltuples, relallvisible
 FROM pg_class
 WHERE oid = 'stats_import.test'::regclass;
 
--- warn and error: unrecognized argument name
-SELECT pg_restore_relation_stats(
-        'relation', '0'::oid::regclass,
-        'version', 150000::integer,
-        'relpages', '17'::integer,
-        'reltuples', 400::real,
-        'nope', 4::integer);
-
 -- warn: bad relpages type
 SELECT pg_restore_relation_stats(
         'relation', 'stats_import.test'::regclass,
@@ -677,55 +168,15 @@ SELECT relpages, reltuples, relallvisible
 FROM pg_class
 WHERE oid = 'stats_import.test'::regclass;
 
--- error: object does not exist
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', '0'::oid::regclass,
-    'attname', 'id'::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-
--- error: relation null
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', NULL::oid,
-    'attname', 'id'::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-
--- error: attname null
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', NULL::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-
--- error: attname doesn't exist
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'nope'::name,
-    'inherited', false::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
-
--- error: inherited null
-SELECT pg_catalog.pg_restore_attribute_stats(
-    'relation', 'stats_import.test'::regclass,
-    'attname', 'id'::name,
-    'inherited', NULL::boolean,
-    'version', 150000::integer,
-    'null_frac', 0.1::real,
-    'avg_width', 2::integer,
-    'n_distinct', 0.3::real);
+-- invalid relkinds for statistics
+CREATE SEQUENCE stats_import.testseq;
+CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test;
+SELECT
+    pg_catalog.pg_clear_relation_stats(
+        'stats_import.testseq'::regclass);
+SELECT
+    pg_catalog.pg_clear_relation_stats(
+        'stats_import.testview'::regclass);
 
 -- ok: no stakinds
 SELECT pg_catalog.pg_restore_attribute_stats(
@@ -1050,227 +501,178 @@ AND tablename = 'test'
 AND inherited = false
 AND attname = 'arange';
 
--- warn: too many stat kinds
+-- warn: cannot set most_common_elems for range type
 SELECT pg_catalog.pg_restore_attribute_stats(
     'relation', 'stats_import.test'::regclass,
     'attname', 'arange'::name,
     'inherited', false::boolean,
-    'version', 150000::integer,
     'null_frac', 0.5::real,
     'avg_width', 2::integer,
     'n_distinct', -0.1::real,
-    'most_common_vals', '{"[2,3)","[1,3)","[3,9)"}'::text,
+    'most_common_vals', '{"[2,3)","[1,2)","[3,4)"}'::text,
     'most_common_freqs', '{0.3,0.25,0.05}'::real[],
-    'histogram_bounds', '{"[1,2)","[2,3)","[3,4)","[4,)"}'::text,
+    'histogram_bounds', '{"[1,2)","[2,3)","[3,4)","[4,5)"}'::text,
     'correlation', 1.1::real,
     'most_common_elems', '{3,1}'::text,
     'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[],
     'range_empty_frac', -0.5::real,
     'range_length_histogram', '{399,499,Infinity}'::text,
-    'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text);
-
---
--- Test the ability to exactly copy data from one table to an identical table,
--- correctly reconstructing the stakind order as well as the staopN and
--- stacollN values. Because oids are not stable across databases, we can only
--- test this when the source and destination are on the same database
--- instance. For that reason, we borrow and adapt a query found in fe_utils
--- and used by pg_dump/pg_upgrade.
---
-INSERT INTO stats_import.test
-SELECT 1, 'one', (1, 1.1, 'ONE', '2001-01-01', '{ "xkey": "xval" }')::stats_import.complex_type, int4range(1,4), array['red','green']
-UNION ALL
-SELECT 2, 'two', (2, 2.2, 'TWO', '2002-02-02', '[true, 4, "six"]')::stats_import.complex_type,  int4range(1,4), array['blue','yellow']
-UNION ALL
-SELECT 3, 'tre', (3, 3.3, 'TRE', '2003-03-03', NULL)::stats_import.complex_type, int4range(-1,1), array['"orange"', 'purple', 'cyan']
-UNION ALL
-SELECT 4, 'four', NULL, int4range(0,100), NULL;
+    'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text
+    );
 
-CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'arange';
 
--- restoring stats on index
-SELECT * FROM pg_catalog.pg_restore_relation_stats(
-    'relation', 'stats_import.is_odd'::regclass,
-    'version', '180000'::integer,
-    'relpages', '11'::integer,
-    'reltuples', '10000'::real,
-    'relallvisible', '0'::integer
-);
+-- warn: scalars can't have mcelem
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elems', '{1,3}'::text,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[]
+    );
 
--- Generate statistics on table with data
-ANALYZE stats_import.test;
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'id';
 
-CREATE TABLE stats_import.test_clone ( LIKE stats_import.test )
-    WITH (autovacuum_enabled = false);
+-- warn: mcelem / mcelem mismatch
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'tags'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elems', '{one,two}'::text
+    );
 
-CREATE INDEX is_odd_clone ON stats_import.test_clone(((comp).a % 2 = 1));
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'tags';
 
---
--- Copy stats from test to test_clone, and is_odd to is_odd_clone
---
-SELECT s.schemaname, s.tablename, s.attname, s.inherited
-FROM pg_catalog.pg_stats AS s
-CROSS JOIN LATERAL
-    pg_catalog.pg_set_attribute_stats(
-        relation => ('stats_import.' || s.tablename || '_clone')::regclass::oid,
-        attname => s.attname,
-        inherited => s.inherited,
-        null_frac => s.null_frac,
-        avg_width => s.avg_width,
-        n_distinct => s.n_distinct,
-        most_common_vals => s.most_common_vals::text,
-        most_common_freqs => s.most_common_freqs,
-        histogram_bounds => s.histogram_bounds::text,
-        correlation => s.correlation,
-        most_common_elems => s.most_common_elems::text,
-        most_common_elem_freqs => s.most_common_elem_freqs,
-        elem_count_histogram => s.elem_count_histogram,
-        range_bounds_histogram => s.range_bounds_histogram::text,
-        range_empty_frac => s.range_empty_frac,
-        range_length_histogram => s.range_length_histogram::text) AS r
-WHERE s.schemaname = 'stats_import'
-AND s.tablename IN ('test', 'is_odd')
-ORDER BY s.tablename, s.attname, s.inherited;
+-- warn: mcelem / mcelem null mismatch part 2
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'tags'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3}'::real[]
+    );
 
-SELECT c.relname, COUNT(*) AS num_stats
-FROM pg_class AS c
-JOIN pg_statistic s ON s.starelid = c.oid
-WHERE c.relnamespace = 'stats_import'::regnamespace
-AND c.relname IN ('test', 'test_clone', 'is_odd', 'is_odd_clone')
-GROUP BY c.relname
-ORDER BY c.relname;
+-- ok: mcelem
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'tags'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_elems', '{one,three}'::text,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[]
+    );
 
--- check test minus test_clone
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test_clone'::regclass;
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'tags';
 
--- check test_clone minus test
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test_clone'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'test_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.test'::regclass;
+-- warn: scalars can't have elem_count_histogram
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::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[]
+    );
 
--- check is_odd minus is_odd_clone
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd_clone'::regclass;
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'id';
 
--- check is_odd_clone minus is_odd
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd_clone'::regclass
-EXCEPT
-SELECT
-    a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct,
-    s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5,
-    s.staop1, s.staop2, s.staop3, s.staop4, s.staop5,
-    s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5,
-    s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5,
-    s.stavalues1::text AS sv1, s.stavalues2::text AS sv2,
-    s.stavalues3::text AS sv3, s.stavalues4::text AS sv4,
-    s.stavalues5::text AS sv5, 'is_odd_clone' AS direction
-FROM pg_statistic s
-JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
-WHERE s.starelid = 'stats_import.is_odd'::regclass;
+-- warn: too many stat kinds
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'arange'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.5::real,
+    'avg_width', 2::integer,
+    'n_distinct', -0.1::real,
+    'most_common_vals', '{"[2,3)","[1,3)","[3,9)"}'::text,
+    'most_common_freqs', '{0.3,0.25,0.05}'::real[],
+    'histogram_bounds', '{"[1,2)","[2,3)","[3,4)","[4,)"}'::text,
+    'correlation', 1.1::real,
+    'most_common_elems', '{3,1}'::text,
+    'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[],
+    'range_empty_frac', -0.5::real,
+    'range_length_histogram', '{399,499,Infinity}'::text,
+    'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text);
 
---
-SELECT relpages, reltuples, relallvisible
-FROM pg_class
-WHERE oid = 'stats_import.test'::regclass;
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'arange';
 
 --
--- Clear clone stats to try again with pg_restore_attribute_stats
+-- Test the ability to exactly copy data from one table to an identical table,
+-- correctly reconstructing the stakind order as well as the staopN and
+-- stacollN values. Because oids are not stable across databases, we can only
+-- test this when the source and destination are on the same database
+-- instance. For that reason, we borrow and adapt a query found in fe_utils
+-- and used by pg_dump/pg_upgrade.
 --
-SELECT
-  pg_catalog.pg_clear_attribute_stats(
-        ('stats_import.' || s.tablename)::regclass,
-        s.attname,
-        s.inherited)
-FROM pg_catalog.pg_stats AS s
-WHERE s.schemaname = 'stats_import'
-AND s.tablename IN ('test_clone', 'is_odd_clone')
-ORDER BY s.tablename, s.attname, s.inherited;
-SELECT
+INSERT INTO stats_import.test
+SELECT 1, 'one', (1, 1.1, 'ONE', '2001-01-01', '{ "xkey": "xval" }')::stats_import.complex_type, int4range(1,4), array['red','green']
+UNION ALL
+SELECT 2, 'two', (2, 2.2, 'TWO', '2002-02-02', '[true, 4, "six"]')::stats_import.complex_type,  int4range(1,4), array['blue','yellow']
+UNION ALL
+SELECT 3, 'tre', (3, 3.3, 'TRE', '2003-03-03', NULL)::stats_import.complex_type, int4range(-1,1), array['"orange"', 'purple', 'cyan']
+UNION ALL
+SELECT 4, 'four', NULL, int4range(0,100), NULL;
 
-SELECT COUNT(*)
-FROM pg_catalog.pg_stats AS s
-WHERE s.schemaname = 'stats_import'
-AND s.tablename IN ('test_clone', 'is_odd_clone');
+CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
+
+-- restoring stats on index
+SELECT * FROM pg_catalog.pg_restore_relation_stats(
+    'relation', 'stats_import.is_odd'::regclass,
+    'version', '180000'::integer,
+    'relpages', '11'::integer,
+    'reltuples', '10000'::real,
+    'relallvisible', '0'::integer
+);
+
+-- Generate statistics on table with data
+ANALYZE stats_import.test;
+
+CREATE TABLE stats_import.test_clone ( LIKE stats_import.test )
+    WITH (autovacuum_enabled = false);
+
+CREATE INDEX is_odd_clone ON stats_import.test_clone(((comp).a % 2 = 1));
 
 --
 -- Copy stats from test to test_clone, and is_odd to is_odd_clone
@@ -1416,4 +818,143 @@ FROM pg_statistic s
 JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
 WHERE s.starelid = 'stats_import.is_odd'::regclass;
 
+-- ok
+SELECT pg_catalog.pg_clear_attribute_stats(
+    relation => 'stats_import.test'::regclass,
+    attname => 'arange'::name,
+    inherited => false::boolean);
+
+--
+-- Negative tests
+--
+
+--- error: relation is wrong type
+SELECT pg_catalog.pg_restore_relation_stats(
+        'relation', 0::oid,
+        'relpages', 17::integer,
+        'reltuples', 400.0::real,
+        'relallvisible', 4::integer);
+
+--- error: relation not found
+SELECT pg_catalog.pg_restore_relation_stats(
+        'relation', 0::regclass,
+        'relpages', 17::integer,
+        'reltuples', 400.0::real,
+        'relallvisible', 4::integer);
+
+-- warn and error: unrecognized argument name
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        'relpages', '17'::integer,
+        'reltuples', 400::real,
+        'nope', 4::integer);
+
+-- error: argument name is NULL
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        NULL, '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible', 4::integer);
+
+-- error: argument name is an integer
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        17, '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible', 4::integer);
+
+-- error: odd number of variadic arguments cannot be pairs
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        'relpages', '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible');
+
+-- error: object doesn't exist
+SELECT pg_restore_relation_stats(
+        'relation', '0'::oid::regclass,
+        'version', 150000::integer,
+        'relpages', '17'::integer,
+        'reltuples', 400::real,
+        'relallvisible', 4::integer);
+
+-- error: object does not exist
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', '0'::oid::regclass,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+
+-- error: relation null
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', NULL::oid,
+    'attname', 'id'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+
+-- error: attname null
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', NULL::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+
+-- error: attname doesn't exist
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'nope'::name,
+    'inherited', false::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+
+-- error: attribute is system column
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'xmin'::name,
+    'inherited', false::boolean,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+
+-- error: inherited null
+SELECT pg_catalog.pg_restore_attribute_stats(
+    'relation', 'stats_import.test'::regclass,
+    'attname', 'id'::name,
+    'inherited', NULL::boolean,
+    'version', 150000::integer,
+    'null_frac', 0.1::real,
+    'avg_width', 2::integer,
+    'n_distinct', 0.3::real);
+
+-- error: relation not found
+SELECT pg_catalog.pg_clear_relation_stats(
+    relation => 'stats_import.nope'::regclass);
+
+-- error: attribute is system column
+SELECT pg_catalog.pg_clear_attribute_stats(
+    relation => 'stats_import.test'::regclass,
+    attname => 'ctid'::name,
+    inherited => false::boolean);
+
+-- error: attname doesn't exist
+SELECT pg_catalog.pg_clear_attribute_stats(
+    relation => 'stats_import.test'::regclass,
+    attname => 'nope'::name,
+    inherited => false::boolean);
+
 DROP SCHEMA stats_import CASCADE;