Skip to content

Commit 491182e

Browse files
committed
Build inherited extended stats on partitioned tables
Commit 859b300 disabled building of extended stats for inheritance trees, to prevent updating the same catalog row twice. While that resolved the issue, it also means there are no extended stats for declaratively partitioned tables, because there are no data in the non-leaf relations. That also means declaratively partitioned tables were not affected by the issue 859b300 addressed, which means this is a regression affecting queries that calculate estimates for the whole inheritance tree as a whole (which includes e.g. GROUP BY queries). But because partitioned tables are empty, we can invert the condition and build statistics only for the case with inheritance, without losing anything. And we can consider them when calculating estimates. It may be necessary to run ANALYZE on partitioned tables, to collect proper statistics. For declarative partitioning there should no prior statistics, and it might take time before autoanalyze is triggered. For tables partitioned by inheritance the statistics may include data from child relations (if built 859b300), contradicting the current code. 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 859b300). Author: Justin Pryzby Reported-by: Justin Pryzby Backpatch-through: 10 Discussion: https://postgr.es/m/20210923212624.GI831%40telsasoft.com
1 parent b3cac25 commit 491182e

File tree

5 files changed

+65
-12
lines changed

5 files changed

+65
-12
lines changed

src/backend/commands/analyze.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
563563
{
564564
MemoryContext col_context,
565565
old_context;
566+
bool build_ext_stats;
566567

567568
col_context = AllocSetContextCreate(anl_context,
568569
"Analyze Column",
@@ -623,13 +624,28 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
623624
thisdata->attr_cnt, thisdata->vacattrstats);
624625
}
625626

627+
/*
628+
* Should we build extended statistics for this relation?
629+
*
630+
* The extended statistics catalog does not include an inheritance
631+
* flag, so we can't store statistics built both with and without
632+
* data from child relations. We can store just one set of statistics
633+
* per relation. For plain relations that's fine, but for inheritance
634+
* trees we have to pick whether to store statistics for just the
635+
* one relation or the whole tree. For plain inheritance we store
636+
* the (!inh) version, mostly for backwards compatibility reasons.
637+
* For partitioned tables that's pointless (the non-leaf tables are
638+
* always empty), so we store stats representing the whole tree.
639+
*/
640+
build_ext_stats = (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) ? inh : (!inh);
641+
626642
/*
627643
* Build extended statistics (if there are any).
628644
*
629645
* For now we only build extended statistics on individual relations,
630646
* not for relations representing inheritance trees.
631647
*/
632-
if (!inh)
648+
if (build_ext_stats)
633649
BuildRelationExtStatistics(onerel, totalrows, numrows, rows,
634650
attr_cnt, vacattrstats);
635651
}

src/backend/statistics/dependencies.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "access/htup_details.h"
1717
#include "access/sysattr.h"
18+
#include "catalog/pg_class.h"
1819
#include "catalog/pg_operator.h"
1920
#include "catalog/pg_statistic_ext.h"
2021
#include "lib/stringinfo.h"
@@ -968,10 +969,13 @@ dependencies_clauselist_selectivity(PlannerInfo *root,
968969
*estimatedclauses = NULL;
969970

970971
/*
971-
* When dealing with inheritance trees, ignore extended stats (which were
972-
* built without data from child rels, and thus do not represent them).
972+
* When dealing with regular inheritance trees, ignore extended stats
973+
* (which were built without data from child rels, and thus do not
974+
* represent them). For partitioned tables data there's no data in the
975+
* non-leaf relations, so we build stats only for the inheritance tree.
976+
* So for partitioned tables we do consider extended stats.
973977
*/
974-
if (rte->inh)
978+
if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE)
975979
return 1.0;
976980

977981
/* check if there's any stats that might be useful for us. */

src/backend/utils/adt/selfuncs.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3923,19 +3923,23 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
39233923
Oid statOid = InvalidOid;
39243924
MVNDistinct *stats;
39253925
Bitmapset *matched = NULL;
3926-
RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
3927-
3928-
/*
3929-
* When dealing with inheritance trees, ignore extended stats (which were
3930-
* built without data from child rels, and thus do not represent them).
3931-
*/
3932-
if (rte->inh)
3933-
return false;
3926+
RangeTblEntry *rte;
39343927

39353928
/* bail out immediately if the table has no extended statistics */
39363929
if (!rel->statlist)
39373930
return false;
39383931

3932+
/*
3933+
* When dealing with regular inheritance trees, ignore extended stats
3934+
* (which were built without data from child rels, and thus do not
3935+
* represent them). For partitioned tables data there's no data in the
3936+
* non-leaf relations, so we build stats only for the inheritance tree.
3937+
* So for partitioned tables we do consider extended stats.
3938+
*/
3939+
rte = planner_rt_fetch(rel->relid, root);
3940+
if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE)
3941+
return false;
3942+
39393943
/* Determine the attnums we're looking for */
39403944
foreach(lc, *varinfos)
39413945
{

src/test/regress/expected/stats_ext.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,25 @@ SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b
168168
(1 row)
169169

170170
DROP TABLE stxdinh, stxdinh1, stxdinh2;
171+
-- Ensure inherited stats ARE applied to inherited query in partitioned table
172+
CREATE TABLE stxdinp(i int, a int, b int) PARTITION BY RANGE (i);
173+
CREATE TABLE stxdinp1 PARTITION OF stxdinp FOR VALUES FROM (1) TO (100);
174+
INSERT INTO stxdinp SELECT 1, a/100, a/100 FROM generate_series(1, 999) a;
175+
CREATE STATISTICS stxdinp ON a, b FROM stxdinp;
176+
VACUUM ANALYZE stxdinp; -- partitions are processed recursively
177+
SELECT 1 FROM pg_statistic_ext WHERE stxrelid = 'stxdinp'::regclass;
178+
?column?
179+
----------
180+
1
181+
(1 row)
182+
183+
SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinp GROUP BY 1, 2');
184+
estimated | actual
185+
-----------+--------
186+
10 | 10
187+
(1 row)
188+
189+
DROP TABLE stxdinp;
171190
-- Verify supported object types for extended statistics
172191
CREATE schema tststats;
173192
CREATE TABLE tststats.t (a int, b int, c text);

src/test/regress/sql/stats_ext.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2');
117117
SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0');
118118
DROP TABLE stxdinh, stxdinh1, stxdinh2;
119119

120+
-- Ensure inherited stats ARE applied to inherited query in partitioned table
121+
CREATE TABLE stxdinp(i int, a int, b int) PARTITION BY RANGE (i);
122+
CREATE TABLE stxdinp1 PARTITION OF stxdinp FOR VALUES FROM (1) TO (100);
123+
INSERT INTO stxdinp SELECT 1, a/100, a/100 FROM generate_series(1, 999) a;
124+
CREATE STATISTICS stxdinp ON a, b FROM stxdinp;
125+
VACUUM ANALYZE stxdinp; -- partitions are processed recursively
126+
SELECT 1 FROM pg_statistic_ext WHERE stxrelid = 'stxdinp'::regclass;
127+
SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinp GROUP BY 1, 2');
128+
DROP TABLE stxdinp;
129+
120130
-- Verify supported object types for extended statistics
121131
CREATE schema tststats;
122132

0 commit comments

Comments
 (0)