CREATE INDEX: don't update table stats if autovacuum=off.
authorJeff Davis <jdavis@postgresql.org>
Fri, 7 Mar 2025 03:36:34 +0000 (19:36 -0800)
committerJeff Davis <jdavis@postgresql.org>
Fri, 7 Mar 2025 03:39:14 +0000 (19:39 -0800)
We previously fixed this for binary upgrade in 71b66171d0, but a
similar problem remained when dumping statistics without data.

Fix by not opportunistically updating table stats during CREATE INDEX
when autovacuum is disabled. For stats to be stable at all, the server
needs to be aware that it should not take every opportunity to update
stats. Per discussion, autovacuum=off is a signal that the user
expects stats to be stable; though if necessary, we could create
a more specific mode in the future.

Reported-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Discussion: https://postgr.es/m/CAExHW5vf9D+8-a5_BEX3y=2y_xY9hiCxV1=C+FnxDvfprWvkng@mail.gmail.com
Discussion: https://postgr.es/m/ca81cbf6e6ea2af838df972801ad4da52640a503.camel%40j-davis.com

src/backend/catalog/index.c
src/test/regress/expected/stats_import.out
src/test/regress/sql/stats_import.sql

index 8e1741c81f5b308106d169a8476ba641c3b8a7f2..022b9b99b13dfb2855ae5f305a83d3b38074d39f 100644 (file)
@@ -63,6 +63,7 @@
 #include "optimizer/optimizer.h"
 #include "parser/parser.h"
 #include "pgstat.h"
+#include "postmaster/autovacuum.h"
 #include "rewrite/rewriteManip.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
@@ -2840,6 +2841,27 @@ index_update_stats(Relation rel,
     */
    update_stats = reltuples >= 0 && !IsBinaryUpgrade;
 
+   /*
+    * If autovacuum is off, user may not be expecting table relstats to
+    * change.  This can be important when restoring a dump that includes
+    * statistics, as the table statistics may be restored before the index is
+    * created, and we want to preserve the restored table statistics.
+    */
+   if (AutoVacuumingActive())
+   {
+       if (rel->rd_rel->relkind == RELKIND_RELATION ||
+           rel->rd_rel->relkind == RELKIND_TOASTVALUE ||
+           rel->rd_rel->relkind == RELKIND_MATVIEW)
+       {
+           StdRdOptions *options = (StdRdOptions *) rel->rd_options;
+
+           if (options != NULL && !options->autovacuum.enabled)
+               update_stats = false;
+       }
+   }
+   else
+       update_stats = false;
+
    /*
     * Finish I/O and visibility map buffer locks before
     * systable_inplace_update_begin() locks the pg_class buffer.  The rd_rel
index ebba14c6a1dd74a6c1d546295499248ae3a0acb8..1f46d5e7854720db03c7892f9dadafa169d84dbd 100644 (file)
@@ -12,7 +12,36 @@ CREATE TABLE stats_import.test(
     arange int4range,
     tags text[]
 ) WITH (autovacuum_enabled = false);
+SELECT
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.test'::regclass,
+        'relpages', 18::integer,
+        'reltuples', 21::real,
+        'relallvisible', 24::integer,
+   'relallfrozen', 27::integer);
+ pg_restore_relation_stats 
+---------------------------
+ t
+(1 row)
+
+-- CREATE INDEX on a table with autovac disabled should not overwrite
+-- stats
 CREATE INDEX test_i ON stats_import.test(id);
+SELECT relname, relpages, reltuples, relallvisible, relallfrozen
+FROM pg_class
+WHERE oid = 'stats_import.test'::regclass
+ORDER BY relname;
+ relname | relpages | reltuples | relallvisible | relallfrozen 
+---------+----------+-----------+---------------+--------------
+ test    |       18 |        21 |            24 |           27
+(1 row)
+
+SELECT pg_clear_relation_stats('stats_import.test'::regclass);
+ pg_clear_relation_stats 
+-------------------------
+(1 row)
+
 --
 -- relstats tests
 --
index 8d04ff4f378c769be4ef0a0c2105061eeb87615c..0ec590688c20eee6bc45bc269126a8fba4ffc349 100644 (file)
@@ -15,8 +15,25 @@ CREATE TABLE stats_import.test(
     tags text[]
 ) WITH (autovacuum_enabled = false);
 
+SELECT
+    pg_catalog.pg_restore_relation_stats(
+        'relation', 'stats_import.test'::regclass,
+        'relpages', 18::integer,
+        'reltuples', 21::real,
+        'relallvisible', 24::integer,
+   'relallfrozen', 27::integer);
+
+-- CREATE INDEX on a table with autovac disabled should not overwrite
+-- stats
 CREATE INDEX test_i ON stats_import.test(id);
 
+SELECT relname, relpages, reltuples, relallvisible, relallfrozen
+FROM pg_class
+WHERE oid = 'stats_import.test'::regclass
+ORDER BY relname;
+
+SELECT pg_clear_relation_stats('stats_import.test'::regclass);
+
 --
 -- relstats tests
 --