Create functions pg_set_relation_stats, pg_clear_relation_stats.
authorJeff Davis <[email protected]>
Fri, 11 Oct 2024 23:55:11 +0000 (16:55 -0700)
committerJeff Davis <[email protected]>
Fri, 11 Oct 2024 23:55:11 +0000 (16:55 -0700)
These functions are used to tweak statistics on any relation, provided
that the user has MAINTAIN privilege on the relation, or is the database
owner.

Bump catalog version.

Author: Corey Huinker
Discussion: https://postgr.es/m/CADkLM=eErgzn7ECDpwFcptJKOk9SxZEk5Pot4d94eVTZsvj3gw@mail.gmail.com

12 files changed:
doc/src/sgml/func.sgml
src/backend/catalog/system_functions.sql
src/backend/statistics/Makefile
src/backend/statistics/meson.build
src/backend/statistics/relation_stats.c [new file with mode: 0644]
src/backend/statistics/stat_utils.c [new file with mode: 0644]
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/include/statistics/stat_utils.h [new file with mode: 0644]
src/test/regress/expected/stats_import.out [new file with mode: 0644]
src/test/regress/parallel_schedule
src/test/regress/sql/stats_import.sql [new file with mode: 0644]

index 9656d1891e8c23c46088380e55d0d77d8d261007..f8a0d76d12bf143473da2740399cd3f93fe5dc52 100644 (file)
@@ -30135,6 +30135,100 @@ DETAIL:  Make sure pg_wal_replay_wait() isn't called within a transaction with a
     </tgroup>
    </table>
 
+   <para>
+    <xref linkend="functions-admin-statsmod"/> lists functions used to
+    manipulate statistics.
+    <warning>
+     <para>
+      Changes made by these statistics manipulation functions are likely to be
+      overwritten by <link linkend="autovacuum">autovacuum</link> (or manual
+      <command>VACUUM</command> or <command>ANALYZE</command>) and should be
+      considered temporary.
+     </para>
+    </warning>
+   </para>
+
+   <table id="functions-admin-statsmod">
+    <title>Database Object Statistics Manipulation Functions</title>
+    <tgroup cols="1">
+     <thead>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        Function
+       </para>
+       <para>
+        Description
+       </para></entry>
+      </row>
+     </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>boolean</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. Returns <literal>true</literal> if a change was made;
+         <literal>false</literal> otherwise.
+        </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>0</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>boolean</returnvalue>
+        </para>
+        <para>
+         Clears table-level statistics for the given relation, as though the
+         table was newly created. Returns <literal>true</literal> if a change
+         was made; <literal>false</literal> otherwise.
+        </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>
+    </tgroup>
+   </table>
+
    <para>
     <xref linkend="functions-info-partition"/> lists functions that provide
     information about the structure of partitioned tables.
index fd6b606ae90babf0f21be80f23b609e2afa3a992..92b6aefe7aaaddde7fe4bb7f4dea6adc08ef5b1d 100644 (file)
@@ -639,6 +639,16 @@ 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 bool
+LANGUAGE INTERNAL
+CALLED ON NULL INPUT VOLATILE
+AS 'pg_set_relation_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 89cf8c27973db21eb0a8f1dda8d28b6b5be333d7..041f5f8a5814eeaeade9950afffaa1b6036959eb 100644 (file)
@@ -16,6 +16,8 @@ OBJS = \
        dependencies.o \
        extended_stats.o \
        mcv.o \
-       mvdistinct.o
+       mvdistinct.o \
+       relation_stats.o \
+       stat_utils.o
 
 include $(top_srcdir)/src/backend/common.mk
index 73b29a3d50aeda6c0947a59001115bae9eea02f6..23648b3775f18ebaf8186acbca1370eed3f1e346 100644 (file)
@@ -5,4 +5,6 @@ backend_sources += files(
   'extended_stats.c',
   'mcv.c',
   'mvdistinct.c',
+  'relation_stats.c',
+  'stat_utils.c'
 )
diff --git a/src/backend/statistics/relation_stats.c b/src/backend/statistics/relation_stats.c
new file mode 100644 (file)
index 0000000..ffa3d83
--- /dev/null
@@ -0,0 +1,210 @@
+/*-------------------------------------------------------------------------
+ * relation_stats.c
+ *
+ *       PostgreSQL relation statistics manipulation
+ *
+ * Code supporting the direct import of relation statistics, similar to
+ * what is done by the ANALYZE command.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       src/backend/statistics/relation_stats.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/indexing.h"
+#include "statistics/stat_utils.h"
+#include "utils/fmgrprotos.h"
+#include "utils/syscache.h"
+
+#define DEFAULT_RELPAGES Int32GetDatum(0)
+#define DEFAULT_RELTUPLES Float4GetDatum(-1.0)
+#define DEFAULT_RELALLVISIBLE Int32GetDatum(0)
+
+/*
+ * Positional argument numbers, names, and types for
+ * relation_statistics_update().
+ */
+
+enum relation_stats_argnum
+{
+       RELATION_ARG = 0,
+       RELPAGES_ARG,
+       RELTUPLES_ARG,
+       RELALLVISIBLE_ARG,
+       NUM_RELATION_STATS_ARGS
+};
+
+static struct StatsArgInfo relarginfo[] =
+{
+       [RELATION_ARG] = {"relation", REGCLASSOID},
+       [RELPAGES_ARG] = {"relpages", INT4OID},
+       [RELTUPLES_ARG] = {"reltuples", FLOAT4OID},
+       [RELALLVISIBLE_ARG] = {"relallvisible", INT4OID},
+       [NUM_RELATION_STATS_ARGS] = {0}
+};
+
+static bool relation_statistics_update(FunctionCallInfo fcinfo, int elevel);
+
+/*
+ * Internal function for modifying statistics for a relation.
+ */
+static bool
+relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
+{
+       Oid                     reloid;
+       Relation        crel;
+       HeapTuple       ctup;
+       Form_pg_class pgcform;
+       int                     replaces[3] = {0};
+       Datum           values[3] = {0};
+       bool            nulls[3] = {0};
+       int                     ncols = 0;
+       TupleDesc       tupdesc;
+       HeapTuple       newtup;
+
+
+       stats_check_required_arg(fcinfo, relarginfo, RELATION_ARG);
+       reloid = PG_GETARG_OID(RELATION_ARG);
+
+       stats_lock_check_privileges(reloid);
+
+       /*
+        * Take RowExclusiveLock on pg_class, consistent with
+        * vac_update_relstats().
+        */
+       crel = table_open(RelationRelationId, RowExclusiveLock);
+
+       tupdesc = RelationGetDescr(crel);
+       ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
+       if (!HeapTupleIsValid(ctup))
+       {
+               ereport(elevel,
+                               (errcode(ERRCODE_OBJECT_IN_USE),
+                                errmsg("pg_class entry for relid %u not found", reloid)));
+               table_close(crel, RowExclusiveLock);
+               return false;
+       }
+
+       pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+       /* relpages */
+       if (!PG_ARGISNULL(RELPAGES_ARG))
+       {
+               int32           relpages = PG_GETARG_INT32(RELPAGES_ARG);
+
+               if (relpages < -1)
+               {
+                       ereport(elevel,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("relpages cannot be < -1")));
+                       table_close(crel, RowExclusiveLock);
+                       return false;
+               }
+
+               if (relpages != pgcform->relpages)
+               {
+                       replaces[ncols] = Anum_pg_class_relpages;
+                       values[ncols] = Int32GetDatum(relpages);
+                       ncols++;
+               }
+       }
+
+       if (!PG_ARGISNULL(RELTUPLES_ARG))
+       {
+               float           reltuples = PG_GETARG_FLOAT4(RELTUPLES_ARG);
+
+               if (reltuples < -1.0)
+               {
+                       ereport(elevel,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("reltuples cannot be < -1.0")));
+                       table_close(crel, RowExclusiveLock);
+                       return false;
+               }
+
+               if (reltuples != pgcform->reltuples)
+               {
+                       replaces[ncols] = Anum_pg_class_reltuples;
+                       values[ncols] = Float4GetDatum(reltuples);
+                       ncols++;
+               }
+       }
+
+       if (!PG_ARGISNULL(RELALLVISIBLE_ARG))
+       {
+               int32           relallvisible = PG_GETARG_INT32(RELALLVISIBLE_ARG);
+
+               if (relallvisible < 0)
+               {
+                       ereport(elevel,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("relallvisible cannot be < 0")));
+                       table_close(crel, RowExclusiveLock);
+                       return false;
+               }
+
+               if (relallvisible != pgcform->relallvisible)
+               {
+                       replaces[ncols] = Anum_pg_class_relallvisible;
+                       values[ncols] = Int32GetDatum(relallvisible);
+                       ncols++;
+               }
+       }
+
+       /* only update pg_class if there is a meaningful change */
+       if (ncols == 0)
+       {
+               table_close(crel, RowExclusiveLock);
+               return false;
+       }
+
+       newtup = heap_modify_tuple_by_cols(ctup, tupdesc, ncols, replaces, values,
+                                                                          nulls);
+
+       CatalogTupleUpdate(crel, &newtup->t_self, newtup);
+       heap_freetuple(newtup);
+
+       /* release the lock, consistent with vac_update_relstats() */
+       table_close(crel, RowExclusiveLock);
+
+       return true;
+}
+
+/*
+ * Set statistics for a given pg_class entry.
+ */
+Datum
+pg_set_relation_stats(PG_FUNCTION_ARGS)
+{
+       PG_RETURN_BOOL(relation_statistics_update(fcinfo, ERROR));
+}
+
+/*
+ * Clear statistics for a given pg_class entry; that is, set back to initial
+ * stats for a newly-created table.
+ */
+Datum
+pg_clear_relation_stats(PG_FUNCTION_ARGS)
+{
+       LOCAL_FCINFO(newfcinfo, 4);
+
+       InitFunctionCallInfoData(*newfcinfo, NULL, 4, InvalidOid, NULL, NULL);
+
+       newfcinfo->args[0].value = PG_GETARG_OID(0);
+       newfcinfo->args[0].isnull = PG_ARGISNULL(0);
+       newfcinfo->args[1].value = DEFAULT_RELPAGES;
+       newfcinfo->args[1].isnull = false;
+       newfcinfo->args[2].value = DEFAULT_RELTUPLES;
+       newfcinfo->args[2].isnull = false;
+       newfcinfo->args[3].value = DEFAULT_RELALLVISIBLE;
+       newfcinfo->args[3].isnull = false;
+
+       PG_RETURN_BOOL(relation_statistics_update(newfcinfo, ERROR));
+}
diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c
new file mode 100644 (file)
index 0000000..4babed2
--- /dev/null
@@ -0,0 +1,94 @@
+/*-------------------------------------------------------------------------
+ * stat_utils.c
+ *
+ *       PostgreSQL statistics manipulation utilities.
+ *
+ * Code supporting the direct manipulation of statistics.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       src/backend/statistics/stat_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/relation.h"
+#include "catalog/pg_database.h"
+#include "miscadmin.h"
+#include "statistics/stat_utils.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/acl.h"
+#include "utils/rel.h"
+
+/*
+ * Ensure that a given argument is not null.
+ */
+void
+stats_check_required_arg(FunctionCallInfo fcinfo,
+                                                struct StatsArgInfo *arginfo,
+                                                int argnum)
+{
+       if (PG_ARGISNULL(argnum))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("\"%s\" cannot be NULL",
+                                               arginfo[argnum].argname)));
+}
+
+/*
+ * Lock relation in ShareUpdateExclusive mode, check privileges, and close the
+ * relation (but retain the lock).
+ *
+ * A role has privileges to set statistics on the relation if any of the
+ * following are true:
+ *   - the role owns the current database and the relation is not shared
+ *   - the role has the MAINTAIN privilege on the relation
+ */
+void
+stats_lock_check_privileges(Oid reloid)
+{
+       Relation        rel = relation_open(reloid, ShareUpdateExclusiveLock);
+       const char      relkind = rel->rd_rel->relkind;
+
+       /* All of the types that can be used with ANALYZE, plus indexes */
+       switch (relkind)
+       {
+               case RELKIND_RELATION:
+               case RELKIND_INDEX:
+               case RELKIND_MATVIEW:
+               case RELKIND_FOREIGN_TABLE:
+               case RELKIND_PARTITIONED_TABLE:
+               case RELKIND_PARTITIONED_INDEX:
+                       break;
+               default:
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("cannot modify statistics for relation \"%s\"",
+                                                       RelationGetRelationName(rel)),
+                                        errdetail_relkind_not_supported(rel->rd_rel->relkind)));
+       }
+
+       if (rel->rd_rel->relisshared)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("cannot modify statistics for shared relation")));
+
+       if (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()))
+       {
+               AclResult       aclresult = pg_class_aclcheck(RelationGetRelid(rel),
+                                                                                                 GetUserId(),
+                                                                                                 ACL_MAINTAIN);
+
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult,
+                                                  get_relkind_objtype(rel->rd_rel->relkind),
+                                                  NameStr(rel->rd_rel->relname));
+       }
+
+       relation_close(rel, NoLock);
+}
index ca7dc84f48d184bba895103ee6c39665aa155f40..38f0f9c1588f31f8c44419140b802fa0e8b285c3 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202410113
+#define CATALOG_VERSION_NO     202410114
 
 #endif
index bcfb92528ee17a46ad33a51265b86d59694a5877..7c0b74fe0551048ec52a875b642ef72dc0195ce1 100644 (file)
   proallargtypes => '{int8,pg_lsn,pg_lsn,int4}', proargmodes => '{o,o,o,o}',
   proargnames => '{summarized_tli,summarized_lsn,pending_lsn,summarizer_pid}',
   prosrc => 'pg_get_wal_summarizer_state' },
+  +# Statistics Import
+{ oid => '9944',
+  descr => 'set statistics on relation',
+  proname => 'pg_set_relation_stats', provolatile => 'v', proisstrict => 'f',
+  proparallel => 'u', prorettype => 'bool',
+  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 => 'bool',
+  proargtypes => 'regclass',
+  proargnames => '{relation}',
+  prosrc => 'pg_clear_relation_stats' },
 
 # GiST stratnum implementations
 { oid => '8047', descr => 'GiST support',
diff --git a/src/include/statistics/stat_utils.h b/src/include/statistics/stat_utils.h
new file mode 100644 (file)
index 0000000..46057c6
--- /dev/null
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * stat_utils.h
+ *       Extended statistics and selectivity estimation functions.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/statistics/stat_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STATS_UTILS_H
+#define STATS_UTILS_H
+
+#include "fmgr.h"
+
+struct StatsArgInfo
+{
+       const char *argname;
+       Oid                     argtype;
+};
+
+extern void stats_check_required_arg(FunctionCallInfo fcinfo,
+                                                                        struct StatsArgInfo *arginfo,
+                                                                        int argnum);
+extern void stats_lock_check_privileges(Oid reloid);
+
+#endif                                                 /* STATS_UTILS_H */
diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out
new file mode 100644 (file)
index 0000000..cd1b80a
--- /dev/null
@@ -0,0 +1,143 @@
+CREATE SCHEMA stats_import;
+CREATE TYPE stats_import.complex_type AS (
+    a integer,
+    b real,
+    c text,
+    d date,
+    e jsonb);
+CREATE TABLE stats_import.test(
+    id INTEGER PRIMARY KEY,
+    name text,
+    comp stats_import.complex_type,
+    arange int4range,
+    tags text[]
+);
+-- starting stats
+SELECT relpages, reltuples, relallvisible
+FROM pg_class
+WHERE oid = 'stats_import.test'::regclass;
+ relpages | reltuples | relallvisible 
+----------+-----------+---------------
+        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 
+-----------------------
+ t
+(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 
+-----------------------
+ t
+(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 
+-----------------------
+ f
+(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 
+-----------------------
+ f
+(1 row)
+
+SELECT relpages, reltuples, relallvisible
+FROM pg_class
+WHERE oid = 'stats_import.test'::regclass;
+ relpages | reltuples | relallvisible 
+----------+-----------+---------------
+       17 |       400 |             4
+(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 
+-----------------------
+ t
+(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(
+        'stats_import.test'::regclass);
+ pg_clear_relation_stats 
+-------------------------
+ t
+(1 row)
+
+SELECT relpages, reltuples, relallvisible
+FROM pg_class
+WHERE oid = 'stats_import.test'::regclass;
+ relpages | reltuples | relallvisible 
+----------+-----------+---------------
+        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.
+DROP SCHEMA stats_import CASCADE;
+NOTICE:  drop cascades to 4 other objects
+DETAIL:  drop cascades to type stats_import.complex_type
+drop cascades to table stats_import.test
+drop cascades to sequence stats_import.testseq
+drop cascades to view stats_import.testview
index 4f38104ba0173abdc9499083aaa1fb72a49d6482..81e4222d26a1522af0f5fb86cac9a4bf08324df7 100644 (file)
@@ -28,7 +28,7 @@ test: strings md5 numerology point lseg line box path polygon circle date time t
 # geometry depends on point, lseg, line, box, path, polygon, circle
 # horology depends on date, time, timetz, timestamp, timestamptz, interval
 # ----------
-test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database
+test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database stats_import
 
 # ----------
 # Load huge amounts of data
diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql
new file mode 100644 (file)
index 0000000..3e9f6d9
--- /dev/null
@@ -0,0 +1,98 @@
+CREATE SCHEMA stats_import;
+
+CREATE TYPE stats_import.complex_type AS (
+    a integer,
+    b real,
+    c text,
+    d date,
+    e jsonb);
+
+CREATE TABLE stats_import.test(
+    id INTEGER PRIMARY KEY,
+    name text,
+    comp stats_import.complex_type,
+    arange int4range,
+    tags text[]
+);
+
+-- 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);
+
+SELECT relpages, reltuples, relallvisible
+FROM pg_class
+WHERE oid = 'stats_import.test'::regclass;
+
+-- 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;
+
+-- clear
+SELECT
+    pg_catalog.pg_clear_relation_stats(
+        'stats_import.test'::regclass);
+
+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);
+
+DROP SCHEMA stats_import CASCADE;