]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Ignore extended statistics for inheritance trees
authorTomas Vondra <tomas.vondra@postgresql.org>
Sat, 15 Jan 2022 01:15:23 +0000 (02:15 +0100)
committerTomas Vondra <tomas.vondra@postgresql.org>
Sat, 15 Jan 2022 02:14:55 +0000 (03:14 +0100)
Since commit 859b3003de we only build extended statistics for individual
relations, ignoring the child relations. This resolved the issue with
updating catalog tuple twice, but we still tried to use the statistics
when calculating estimates for the whole inheritance tree. When the
relations contain very distinct data, it may produce bogus estimates.

This is roughly the same issue 427c6b5b9 addressed ~15 years ago, and we
fix it the same way - by ignoring extended statistics when calculating
estimates for the inheritance tree as a whole. We still consider
extended statistics when calculating estimates for individual child
relations, of course.

This may result in plan changes due to different estimates, but if the
old statistics were not describing the inheritance tree particularly
well it's quite likely the new plans is actually better.

Report and patch by Justin Pryzby, minor fixes and cleanup by me.
Backpatch all the way back to PostgreSQL 10, where extended statistics
were introduced (same as 859b3003de).

Author: Justin Pryzby
Reported-by: Justin Pryzby
Backpatch-through: 10
Discussion: https://postgr.es/m/20210923212624.GI831%40telsasoft.com

src/backend/statistics/dependencies.c
src/backend/statistics/extended_stats.c
src/backend/utils/adt/selfuncs.c
src/test/regress/expected/stats_ext.out
src/test/regress/sql/stats_ext.sql

index bdc7e2504561cf1632c47033e5047175315f0d35..12f58e0ffd0c0d4f76990f7b3a83a3d2a2357c0c 100644 (file)
@@ -24,6 +24,7 @@
 #include "optimizer/optimizer.h"
 #include "nodes/nodes.h"
 #include "nodes/pathnodes.h"
+#include "parser/parsetree.h"
 #include "statistics/extended_stats_internal.h"
 #include "statistics/statistics.h"
 #include "utils/bytea.h"
@@ -963,6 +964,14 @@ dependencies_clauselist_selectivity(PlannerInfo *root,
        MVDependencies *dependencies;
        Bitmapset **list_attnums;
        int                     listidx;
+       RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
+
+       /*
+        * When dealing with inheritance trees, ignore extended stats (which were
+        * built without data from child rels, and thus do not represent them).
+        */
+       if (rte->inh)
+               return 1.0;
 
        /* check if there's any stats that might be useful for us. */
        if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES))
index e0aece6b51d419ab47d9526c4d682446daa14193..6593eb6c2dec347aea9655941f3120b941b7d202 100644 (file)
@@ -28,6 +28,7 @@
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/optimizer.h"
+#include "parser/parsetree.h"
 #include "postmaster/autovacuum.h"
 #include "statistics/extended_stats_internal.h"
 #include "statistics/statistics.h"
@@ -1075,6 +1076,14 @@ statext_mcv_clauselist_selectivity(PlannerInfo *root, List *clauses, int varReli
                                mcv_totalsel,
                                other_sel,
                                sel;
+       RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
+
+       /*
+        * When dealing with inheritance trees, ignore extended stats (which were
+        * built without data from child rels, and thus do not represent them).
+        */
+       if (rte->inh)
+               return 1.0;
 
        /* check if there's any stats that might be useful for us. */
        if (!has_stats_of_kind(rel->statlist, STATS_EXT_MCV))
index 1ce5eb268b70275bde3c9e78641f13126381da80..10a83e27ea0191b02424d8481cd595172c5d1258 100644 (file)
@@ -3641,6 +3641,14 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
        Oid                     statOid = InvalidOid;
        MVNDistinct *stats;
        Bitmapset  *matched = NULL;
+       RangeTblEntry           *rte = planner_rt_fetch(rel->relid, root);
+
+       /*
+        * When dealing with inheritance trees, ignore extended stats (which were
+        * built without data from child rels, and thus do not represent them).
+        */
+       if (rte->inh)
+               return false;
 
        /* bail out immediately if the table has no extended statistics */
        if (!rel->statlist)
index 06521a426a32648d2da42e02bb9e63c2b78ac1e8..3ad215679474490e1dea2aadb5d3a35aa04a8ee4 100644 (file)
@@ -124,6 +124,47 @@ CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
 ANALYZE ab1;
 DROP TABLE ab1 CASCADE;
 NOTICE:  drop cascades to table ab1c
+-- Tests for stats with inheritance
+CREATE TABLE stxdinh(a int, b int);
+CREATE TABLE stxdinh1() INHERITS(stxdinh);
+CREATE TABLE stxdinh2() INHERITS(stxdinh);
+INSERT INTO stxdinh SELECT mod(a,50), mod(a,100) FROM generate_series(0, 1999) a;
+INSERT INTO stxdinh1 SELECT mod(a,100), mod(a,100) FROM generate_series(0, 999) a;
+INSERT INTO stxdinh2 SELECT mod(a,100), mod(a,100) FROM generate_series(0, 999) a;
+VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2;
+-- Ensure non-inherited stats are not applied to inherited query
+-- Without stats object, it looks like this
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2');
+ estimated | actual 
+-----------+--------
+       400 |    150
+(1 row)
+
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0');
+ estimated | actual 
+-----------+--------
+         3 |     40
+(1 row)
+
+CREATE STATISTICS stxdinh ON a, b FROM stxdinh;
+VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2;
+-- Since the stats object does not include inherited stats, it should not
+-- affect the estimates
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2');
+ estimated | actual 
+-----------+--------
+       400 |    150
+(1 row)
+
+-- Dependencies are applied at individual relations (within append), so
+-- this estimate changes a bit because we improve estimates for the parent
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0');
+ estimated | actual 
+-----------+--------
+        22 |     40
+(1 row)
+
+DROP TABLE stxdinh, stxdinh1, stxdinh2;
 -- Verify supported object types for extended statistics
 CREATE schema tststats;
 CREATE TABLE tststats.t (a int, b int, c text);
index 06a2caff7baedc270feee5d5d8ebb0f4c8332673..4fd7ed1b81432a4c35c5dcf20b5f89b35c8330fe 100644 (file)
@@ -92,6 +92,28 @@ CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
 ANALYZE ab1;
 DROP TABLE ab1 CASCADE;
 
+-- Tests for stats with inheritance
+CREATE TABLE stxdinh(a int, b int);
+CREATE TABLE stxdinh1() INHERITS(stxdinh);
+CREATE TABLE stxdinh2() INHERITS(stxdinh);
+INSERT INTO stxdinh SELECT mod(a,50), mod(a,100) FROM generate_series(0, 1999) a;
+INSERT INTO stxdinh1 SELECT mod(a,100), mod(a,100) FROM generate_series(0, 999) a;
+INSERT INTO stxdinh2 SELECT mod(a,100), mod(a,100) FROM generate_series(0, 999) a;
+VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2;
+-- Ensure non-inherited stats are not applied to inherited query
+-- Without stats object, it looks like this
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2');
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0');
+CREATE STATISTICS stxdinh ON a, b FROM stxdinh;
+VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2;
+-- Since the stats object does not include inherited stats, it should not
+-- affect the estimates
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2');
+-- Dependencies are applied at individual relations (within append), so
+-- this estimate changes a bit because we improve estimates for the parent
+SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0');
+DROP TABLE stxdinh, stxdinh1, stxdinh2;
+
 -- Verify supported object types for extended statistics
 CREATE schema tststats;