Tighten up relation kind checks for extended statistics
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 17 Apr 2017 20:55:17 +0000 (17:55 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 17 Apr 2017 20:55:55 +0000 (17:55 -0300)
We were accepting creation of extended statistics only for regular
tables, but they can usefully be created for foreign tables, partitioned
tables, and materialized views, too.  Allow those cases.

While at it, make sure all the rejected cases throw a consistent error
message, and add regression tests for the whole thing.

Author: David Rowley, Álvaro Herrera
Discussion: https://postgr.es/m/CAKJS1f-BmGo410bh5RSPZUvOO0LhmHL2NYmdrC_Jm8pk_FfyCA@mail.gmail.com

doc/src/sgml/ref/create_statistics.sgml
src/backend/commands/statscmds.c
src/bin/pg_dump/pg_dump.c
src/test/regress/expected/stats_ext.out
src/test/regress/sql/stats_ext.sql

index dbe28d668575071bffae4ef3a4b6ed7ff2b6e39e..edbcf5840bb0d79b1095931d7080042b10d9d136 100644 (file)
@@ -34,7 +34,7 @@ CREATE STATISTICS [ IF NOT EXISTS ] <replaceable class="PARAMETER">statistics_na
 
   <para>
    <command>CREATE STATISTICS</command> will create a new extended statistics
-   object on the specified table.
+   object on the specified table, foreign table or materialized view.
    The statistics will be created in the current database and
    will be owned by the user issuing the command.
   </para>
index 46abadcc811f753242d0eb870c0a130dd5ff40be..2dd32d9318a823e6023b36399321d9344c6e97d2 100644 (file)
@@ -102,14 +102,16 @@ CreateStatistics(CreateStatsStmt *stmt)
     * take only ShareUpdateExclusiveLock on relation, conflicting with
     * ANALYZE and other DDL that sets statistical information.
     */
-   rel = heap_openrv(stmt->relation, ShareUpdateExclusiveLock);
+   rel = relation_openrv(stmt->relation, ShareUpdateExclusiveLock);
    relid = RelationGetRelid(rel);
 
    if (rel->rd_rel->relkind != RELKIND_RELATION &&
-       rel->rd_rel->relkind != RELKIND_MATVIEW)
+       rel->rd_rel->relkind != RELKIND_MATVIEW &&
+       rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
+       rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("relation \"%s\" is not a table or materialized view",
+                errmsg("relation \"%s\" is not a table, foreign table, or materialized view",
                        RelationGetRelationName(rel))));
 
    /*
@@ -248,7 +250,7 @@ CreateStatistics(CreateStatsStmt *stmt)
    CatalogTupleInsert(statrel, htup);
    statoid = HeapTupleGetOid(htup);
    heap_freetuple(htup);
-   heap_close(statrel, RowExclusiveLock);
+   relation_close(statrel, RowExclusiveLock);
 
    /*
     * Invalidate relcache so that others see the new statistics.
index 22b5f784dc02b2db8e6367e46e27814ee189ff31..88240187866380f29a8307354db4e4b146e3d6e8 100644 (file)
@@ -6676,9 +6676,14 @@ getExtendedStatistics(Archive *fout, TableInfo tblinfo[], int numTables)
    {
        TableInfo  *tbinfo = &tblinfo[i];
 
-       /* Only plain tables and materialized views can have extended statistics. */
+       /*
+        * Only plain tables, materialized views, foreign tables and
+        * partitioned tables can have extended statistics.
+        */
        if (tbinfo->relkind != RELKIND_RELATION &&
-           tbinfo->relkind != RELKIND_MATVIEW)
+           tbinfo->relkind != RELKIND_MATVIEW &&
+           tbinfo->relkind != RELKIND_FOREIGN_TABLE &&
+           tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
            continue;
 
        /*
index 6a3345548a21d73c6c103a45bd5fc7296b140acf..658d28576958fa27eef94660de0a99162d236185 100644 (file)
@@ -47,6 +47,46 @@ ANALYZE ab1 (a);
 WARNING:  extended statistics "public.ab1_a_b_stats" could not be collected for relation public.ab1
 ANALYZE ab1;
 DROP TABLE ab1;
+-- Verify supported object types for extended statistics
+CREATE schema tststats;
+CREATE TABLE tststats.t (a int, b int, c text);
+CREATE INDEX ti ON tststats.t (a, b);
+CREATE SEQUENCE tststats.s;
+CREATE VIEW tststats.v AS SELECT * FROM tststats.t;
+CREATE MATERIALIZED VIEW tststats.mv AS SELECT * FROM tststats.t;
+CREATE TYPE tststats.ty AS (a int, b int, c text);
+CREATE FOREIGN DATA WRAPPER extstats_dummy_fdw;
+CREATE SERVER extstats_dummy_srv FOREIGN DATA WRAPPER extstats_dummy_fdw;
+CREATE FOREIGN TABLE tststats.f (a int, b int, c text) SERVER extstats_dummy_srv;
+CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b);
+CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10);
+CREATE STATISTICS tststats.s1 ON (a, b) FROM tststats.t;
+CREATE STATISTICS tststats.s2 ON (a, b) FROM tststats.ti;
+ERROR:  relation "ti" is not a table, foreign table, or materialized view
+CREATE STATISTICS tststats.s3 ON (a, b) FROM tststats.s;
+ERROR:  relation "s" is not a table, foreign table, or materialized view
+CREATE STATISTICS tststats.s4 ON (a, b) FROM tststats.v;
+ERROR:  relation "v" is not a table, foreign table, or materialized view
+CREATE STATISTICS tststats.s5 ON (a, b) FROM tststats.mv;
+CREATE STATISTICS tststats.s6 ON (a, b) FROM tststats.ty;
+ERROR:  relation "ty" is not a table, foreign table, or materialized view
+CREATE STATISTICS tststats.s7 ON (a, b) FROM tststats.f;
+CREATE STATISTICS tststats.s8 ON (a, b) FROM tststats.pt;
+CREATE STATISTICS tststats.s9 ON (a, b) FROM tststats.pt1;
+DO $$
+DECLARE
+   relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass;
+BEGIN
+   EXECUTE 'CREATE STATISTICS tststats.s10 ON (a, b) FROM ' || relname;
+EXCEPTION WHEN wrong_object_type THEN
+   RAISE NOTICE 'stats on toast table not created';
+END;
+$$;
+NOTICE:  stats on toast table not created
+SET client_min_messages TO warning;
+DROP SCHEMA tststats CASCADE;
+DROP FOREIGN DATA WRAPPER extstats_dummy_fdw CASCADE;
+RESET client_min_messages;
 -- n-distinct tests
 CREATE TABLE ndistinct (
     filler1 TEXT,
index ebb6a78383e573c942f0dd6e11dc71c9348af6b7..3c7e0684d3ec148186889eda549ab363bc603a90 100644 (file)
@@ -40,6 +40,44 @@ ANALYZE ab1 (a);
 ANALYZE ab1;
 DROP TABLE ab1;
 
+-- Verify supported object types for extended statistics
+CREATE schema tststats;
+
+CREATE TABLE tststats.t (a int, b int, c text);
+CREATE INDEX ti ON tststats.t (a, b);
+CREATE SEQUENCE tststats.s;
+CREATE VIEW tststats.v AS SELECT * FROM tststats.t;
+CREATE MATERIALIZED VIEW tststats.mv AS SELECT * FROM tststats.t;
+CREATE TYPE tststats.ty AS (a int, b int, c text);
+CREATE FOREIGN DATA WRAPPER extstats_dummy_fdw;
+CREATE SERVER extstats_dummy_srv FOREIGN DATA WRAPPER extstats_dummy_fdw;
+CREATE FOREIGN TABLE tststats.f (a int, b int, c text) SERVER extstats_dummy_srv;
+CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b);
+CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10);
+
+CREATE STATISTICS tststats.s1 ON (a, b) FROM tststats.t;
+CREATE STATISTICS tststats.s2 ON (a, b) FROM tststats.ti;
+CREATE STATISTICS tststats.s3 ON (a, b) FROM tststats.s;
+CREATE STATISTICS tststats.s4 ON (a, b) FROM tststats.v;
+CREATE STATISTICS tststats.s5 ON (a, b) FROM tststats.mv;
+CREATE STATISTICS tststats.s6 ON (a, b) FROM tststats.ty;
+CREATE STATISTICS tststats.s7 ON (a, b) FROM tststats.f;
+CREATE STATISTICS tststats.s8 ON (a, b) FROM tststats.pt;
+CREATE STATISTICS tststats.s9 ON (a, b) FROM tststats.pt1;
+DO $$
+DECLARE
+   relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass;
+BEGIN
+   EXECUTE 'CREATE STATISTICS tststats.s10 ON (a, b) FROM ' || relname;
+EXCEPTION WHEN wrong_object_type THEN
+   RAISE NOTICE 'stats on toast table not created';
+END;
+$$;
+
+SET client_min_messages TO warning;
+DROP SCHEMA tststats CASCADE;
+DROP FOREIGN DATA WRAPPER extstats_dummy_fdw CASCADE;
+RESET client_min_messages;
 
 -- n-distinct tests
 CREATE TABLE ndistinct (