mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-06 06:29:21 +02:00
Ignore extended statistics for inheritance trees
Since commit859b3003de
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 issue427c6b5b9
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 as859b3003de
). Author: Justin Pryzby Reported-by: Justin Pryzby Backpatch-through: 10 Discussion: https://postgr.es/m/20210923212624.GI831%40telsasoft.com
This commit is contained in:
parent
3a1bfe2565
commit
b3cac25f4d
@ -23,6 +23,7 @@
|
|||||||
#include "optimizer/var.h"
|
#include "optimizer/var.h"
|
||||||
#include "nodes/nodes.h"
|
#include "nodes/nodes.h"
|
||||||
#include "nodes/relation.h"
|
#include "nodes/relation.h"
|
||||||
|
#include "parser/parsetree.h"
|
||||||
#include "statistics/extended_stats_internal.h"
|
#include "statistics/extended_stats_internal.h"
|
||||||
#include "statistics/statistics.h"
|
#include "statistics/statistics.h"
|
||||||
#include "utils/bytea.h"
|
#include "utils/bytea.h"
|
||||||
@ -961,10 +962,18 @@ dependencies_clauselist_selectivity(PlannerInfo *root,
|
|||||||
MVDependencies *dependencies;
|
MVDependencies *dependencies;
|
||||||
AttrNumber *list_attnums;
|
AttrNumber *list_attnums;
|
||||||
int listidx;
|
int listidx;
|
||||||
|
RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
|
||||||
|
|
||||||
/* initialize output argument */
|
/* initialize output argument */
|
||||||
*estimatedclauses = NULL;
|
*estimatedclauses = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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. */
|
/* check if there's any stats that might be useful for us. */
|
||||||
if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES))
|
if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES))
|
||||||
return 1.0;
|
return 1.0;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "catalog/pg_collation.h"
|
#include "catalog/pg_collation.h"
|
||||||
#include "catalog/pg_statistic_ext.h"
|
#include "catalog/pg_statistic_ext.h"
|
||||||
#include "nodes/relation.h"
|
#include "nodes/relation.h"
|
||||||
|
#include "parser/parsetree.h"
|
||||||
#include "postmaster/autovacuum.h"
|
#include "postmaster/autovacuum.h"
|
||||||
#include "statistics/extended_stats_internal.h"
|
#include "statistics/extended_stats_internal.h"
|
||||||
#include "statistics/statistics.h"
|
#include "statistics/statistics.h"
|
||||||
|
@ -3923,6 +3923,14 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
Oid statOid = InvalidOid;
|
Oid statOid = InvalidOid;
|
||||||
MVNDistinct *stats;
|
MVNDistinct *stats;
|
||||||
Bitmapset *matched = NULL;
|
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 */
|
/* bail out immediately if the table has no extended statistics */
|
||||||
if (!rel->statlist)
|
if (!rel->statlist)
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
-- Generic extended statistics support
|
-- Generic extended statistics support
|
||||||
|
-- check the number of estimated/actual rows in the top node
|
||||||
|
create function check_estimated_rows(text) returns table (estimated int, actual int)
|
||||||
|
language plpgsql as
|
||||||
|
$$
|
||||||
|
declare
|
||||||
|
ln text;
|
||||||
|
tmp text[];
|
||||||
|
first_row bool := true;
|
||||||
|
begin
|
||||||
|
for ln in
|
||||||
|
execute format('explain analyze %s', $1)
|
||||||
|
loop
|
||||||
|
if first_row then
|
||||||
|
first_row := false;
|
||||||
|
tmp := regexp_match(ln, 'rows=(\d*) .* rows=(\d*)');
|
||||||
|
return query select tmp[1]::int, tmp[2]::int;
|
||||||
|
end if;
|
||||||
|
end loop;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
-- We will be checking execution plans without/with statistics, so
|
-- We will be checking execution plans without/with statistics, so
|
||||||
-- let's make sure we get simple non-parallel plans. Also set the
|
-- let's make sure we get simple non-parallel plans. Also set the
|
||||||
-- work_mem low so that we can use small amounts of data.
|
-- work_mem low so that we can use small amounts of data.
|
||||||
@ -107,6 +127,47 @@ CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
|
|||||||
ANALYZE ab1;
|
ANALYZE ab1;
|
||||||
DROP TABLE ab1 CASCADE;
|
DROP TABLE ab1 CASCADE;
|
||||||
NOTICE: drop cascades to table ab1c
|
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
|
-- Verify supported object types for extended statistics
|
||||||
CREATE schema tststats;
|
CREATE schema tststats;
|
||||||
CREATE TABLE tststats.t (a int, b int, c text);
|
CREATE TABLE tststats.t (a int, b int, c text);
|
||||||
|
@ -1,5 +1,26 @@
|
|||||||
-- Generic extended statistics support
|
-- Generic extended statistics support
|
||||||
|
|
||||||
|
-- check the number of estimated/actual rows in the top node
|
||||||
|
create function check_estimated_rows(text) returns table (estimated int, actual int)
|
||||||
|
language plpgsql as
|
||||||
|
$$
|
||||||
|
declare
|
||||||
|
ln text;
|
||||||
|
tmp text[];
|
||||||
|
first_row bool := true;
|
||||||
|
begin
|
||||||
|
for ln in
|
||||||
|
execute format('explain analyze %s', $1)
|
||||||
|
loop
|
||||||
|
if first_row then
|
||||||
|
first_row := false;
|
||||||
|
tmp := regexp_match(ln, 'rows=(\d*) .* rows=(\d*)');
|
||||||
|
return query select tmp[1]::int, tmp[2]::int;
|
||||||
|
end if;
|
||||||
|
end loop;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
-- We will be checking execution plans without/with statistics, so
|
-- We will be checking execution plans without/with statistics, so
|
||||||
-- let's make sure we get simple non-parallel plans. Also set the
|
-- let's make sure we get simple non-parallel plans. Also set the
|
||||||
-- work_mem low so that we can use small amounts of data.
|
-- work_mem low so that we can use small amounts of data.
|
||||||
@ -74,6 +95,28 @@ CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
|
|||||||
ANALYZE ab1;
|
ANALYZE ab1;
|
||||||
DROP TABLE ab1 CASCADE;
|
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
|
-- Verify supported object types for extended statistics
|
||||||
CREATE schema tststats;
|
CREATE schema tststats;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user