/
githubmirror
/
postgres
Обзор
Документация
Войти
/
githubmirror
/
postgres
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/test/regress/sql/stats_import.sql
2 554 строки
89 KB
Michael Paquier
Fix multirange type handling in pg_restore_attribute_stats()
10 авг 2026, 16:37
10 авг 2026, 16:37
5783593
Код
Авторство
О чём код?
CREATE SCHEMA stats_import; -- -- Convenience view for columns of pg_stats that are stable across test runs. -- CREATE VIEW stats_import.pg_stats_stable AS SELECT schemaname, tablename, attname, inherited, null_frac, avg_width, n_distinct, most_common_vals::text as most_common_vals, most_common_freqs, histogram_bounds::text AS histogram_bounds, correlation, most_common_elems::text AS most_common_elems, most_common_elem_freqs, elem_count_histogram, range_length_histogram::text AS range_length_histogram, range_empty_frac, range_bounds_histogram::text AS range_bounds_histogram FROM pg_stats; -- -- Setup functions for set-difference convenience functions -- -- Test to detect any new columns added to pg_statistic. If any columns -- are added, we may need to update pg_statistic_flat() and the facilities -- we are testing. SELECT COUNT(*) FROM pg_attribute WHERE attrelid = 'pg_catalog.pg_statistic'::regclass AND attnum > 0; -- Create a view that is used purely for the type based on pg_statistic. CREATE VIEW stats_import.pg_statistic_flat_t AS SELECT a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, s.stavalues5::text AS sv5 FROM pg_statistic s JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum WHERE FALSE; -- Function to retrieve data used for diff comparisons between two -- relations based on the contents of pg_statistic. CREATE FUNCTION stats_import.pg_statistic_flat(p_relname text) RETURNS SETOF stats_import.pg_statistic_flat_t BEGIN ATOMIC SELECT a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, s.stavalues1::text, s.stavalues2::text, s.stavalues3::text, s.stavalues4::text, s.stavalues5::text FROM pg_statistic s JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum JOIN pg_class c ON c.oid = a.attrelid WHERE c.relnamespace = 'stats_import'::regnamespace AND c.relname = p_relname; END; -- Comparison function for pg_statistic. The two relations defined by -- the function caller are compared. CREATE FUNCTION stats_import.pg_statistic_get_difference(a text, b text) RETURNS TABLE (relname text, stats stats_import.pg_statistic_flat_t) BEGIN ATOMIC WITH aset AS (SELECT * FROM stats_import.pg_statistic_flat(a)), bset AS (SELECT * FROM stats_import.pg_statistic_flat(b)) SELECT a AS relname, a_minus_b::stats_import.pg_statistic_flat_t FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b UNION ALL SELECT b AS relname, b_minus_a::stats_import.pg_statistic_flat_t FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a; END; -- Test to detect any new columns added to pg_stats_ext. If any columns -- are added, we may need to update pg_stats_ext_flat() and the facilities -- we are testing. SELECT COUNT(*) FROM pg_attribute WHERE attrelid = 'pg_catalog.pg_stats_ext'::regclass AND attnum > 0; -- Create a view that is used purely for the type based on pg_stats_ext. CREATE VIEW stats_import.pg_stats_ext_flat_t AS SELECT inherited, n_distinct, dependencies, most_common_vals, most_common_freqs, most_common_base_freqs FROM pg_stats_ext WHERE FALSE; -- Function to retrieve data used for diff comparisons between two -- relations based on the contents of pg_stats_ext. CREATE FUNCTION stats_import.pg_stats_ext_flat(p_statname text) RETURNS SETOF stats_import.pg_stats_ext_flat_t BEGIN ATOMIC SELECT inherited, n_distinct, dependencies, most_common_vals, most_common_freqs, most_common_base_freqs FROM pg_stats_ext WHERE statistics_schemaname = 'stats_import' AND statistics_name = p_statname; END; -- Comparison function for pg_stats_ext. The two relations defined by -- the function caller are compared. CREATE FUNCTION stats_import.pg_stats_ext_get_difference(a text, b text) RETURNS TABLE (statname text, stats stats_import.pg_stats_ext_flat_t) BEGIN ATOMIC WITH aset AS (SELECT * FROM stats_import.pg_stats_ext_flat(a)), bset AS (SELECT * FROM stats_import.pg_stats_ext_flat(b)) SELECT a AS relname, a_minus_b::stats_import.pg_stats_ext_flat_t FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b UNION ALL SELECT b AS relname, b_minus_a::stats_import.pg_stats_ext_flat_t FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a; END; -- Test to detect any new columns added to pg_stats_ext_exprs. If any columns -- are added, we may need to update pg_stats_ext_exprs_flat() and the facilities -- we are testing. SELECT COUNT(*) FROM pg_attribute WHERE attrelid = 'pg_catalog.pg_stats_ext_exprs'::regclass AND attnum > 0; -- Create a view that is used purely for the type based on pg_stats_ext_exprs. CREATE VIEW stats_import.pg_stats_ext_exprs_flat_t AS SELECT inherited, null_frac, avg_width, n_distinct, most_common_vals::text AS most_common_vals, most_common_freqs, histogram_bounds::text AS histogram_bounds, correlation, most_common_elems::text AS most_common_elems, most_common_elem_freqs, elem_count_histogram, range_length_histogram::text AS range_length_histogram, range_empty_frac, range_bounds_histogram::text AS range_bounds_histogram FROM pg_stats_ext_exprs AS n WHERE FALSE; -- Function to retrieve data used for diff comparisons between two -- relations based on the contents of pg_stats_ext_exprs. CREATE FUNCTION stats_import.pg_stats_ext_exprs_flat(p_statname text) RETURNS SETOF stats_import.pg_stats_ext_exprs_flat_t BEGIN ATOMIC SELECT inherited, null_frac, avg_width, n_distinct, most_common_vals::text AS most_common_vals, most_common_freqs, histogram_bounds::text AS histogram_bounds, correlation, most_common_elems::text AS most_common_elems, most_common_elem_freqs, elem_count_histogram, range_length_histogram::text AS range_length_histogram, range_empty_frac, range_bounds_histogram::text AS range_bounds_histogram FROM pg_stats_ext_exprs AS n WHERE n.statistics_schemaname = 'stats_import' AND n.statistics_name = p_statname; END; -- Comparison function for pg_stats_ext_exprs. The two relations defined by -- the function caller are compared. CREATE FUNCTION stats_import.pg_stats_ext_exprs_get_difference(a text, b text) RETURNS TABLE (statname text, stats stats_import.pg_stats_ext_exprs_flat_t) BEGIN ATOMIC WITH aset AS (SELECT * FROM stats_import.pg_stats_ext_exprs_flat(a)), bset AS (SELECT * FROM stats_import.pg_stats_ext_exprs_flat(b)) SELECT a AS relname, a_minus_b::stats_import.pg_stats_ext_exprs_flat_t FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b UNION ALL SELECT b AS relname, b_minus_a::stats_import.pg_stats_ext_exprs_flat_t FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a; END; -- -- Schema setup. -- CREATE TYPE stats_import.complex_type AS ( a integer, b real, c text, d date, e jsonb); CREATE TABLE stats_import.test( id INTEGER PRIMARY KEY, name text, comp stats_import.complex_type, arange int4range, tags text[] ) WITH (autovacuum_enabled = false); CREATE TABLE stats_import.test_mr( id INTEGER PRIMARY KEY, name text, mrange int4multirange ) WITH (autovacuum_enabled = false); SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', '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'); -- -- relstats tests -- -- error: schemaname missing SELECT pg_catalog.pg_restore_relation_stats( 'relname', 'test', 'relpages', 17::integer); -- error: relname missing SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 'stats_import', 'relpages', 17::integer); --- error: schemaname is wrong type SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 3.6::float, 'relname', 'test', 'relpages', 17::integer); --- error: relname is wrong type SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 0::oid, 'relpages', 17::integer); -- error: relation not found SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'nope', 'relpages', 17::integer); -- error: odd number of variadic arguments cannot be pairs SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'relallvisible'); -- error: argument name is NULL SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', NULL, '17'::integer); -- starting stats SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test_i'::regclass; -- regular indexes have special case locking rules BEGIN; SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test_i', 'relpages', 18::integer); SELECT mode FROM pg_locks WHERE relation = 'stats_import.test'::regclass AND pid = pg_backend_pid() AND granted; SELECT mode FROM pg_locks WHERE relation = 'stats_import.test_i'::regclass AND pid = pg_backend_pid() AND granted; COMMIT; -- relpages may be -1 for partitioned tables CREATE TABLE stats_import.part_parent ( i integer ) PARTITION BY RANGE(i); CREATE TABLE stats_import.part_child_1 PARTITION OF stats_import.part_parent FOR VALUES FROM (0) TO (10) WITH (autovacuum_enabled = false); -- This ensures the presence of extended statistics marked with -- inherited = true. CREATE STATISTICS stats_import.part_parent_stat ON i, (i % 2) FROM stats_import.part_parent; CREATE INDEX part_parent_i ON stats_import.part_parent(i); INSERT INTO stats_import.part_parent SELECT g.g FROM generate_series(0,9) AS g(g); SELECT COUNT(*) FROM stats_import.part_parent; SELECT COUNT(*) FROM stats_import.part_child_1; ANALYZE stats_import.part_parent; SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'part_parent_stat' GROUP BY e.inherited; SELECT relpages FROM pg_class WHERE oid = 'stats_import.part_parent'::regclass; -- -- Partitioned indexes aren't analyzed but it is possible to set -- stats. The locking rules are different from normal indexes due to -- the rules for in-place updates: both the partitioned table and the -- partitioned index are locked in ShareUpdateExclusive mode. -- BEGIN; SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'part_parent_i', 'relpages', 2::integer); SELECT mode FROM pg_locks WHERE relation = 'stats_import.part_parent'::regclass AND pid = pg_backend_pid() AND granted; SELECT mode FROM pg_locks WHERE relation = 'stats_import.part_parent_i'::regclass AND pid = pg_backend_pid() AND granted; COMMIT; SELECT relpages FROM pg_class WHERE oid = 'stats_import.part_parent_i'::regclass; -- ok: set all relstats, with version, no bounds checking SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'version', 150000::integer, 'relpages', '-17'::integer, 'reltuples', 400::real, 'relallvisible', 4::integer, 'relallfrozen', 2::integer); SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- ok: set just relpages, rest stay same SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'relpages', '16'::integer); SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- ok: set just reltuples, rest stay same SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'reltuples', '500'::real); SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- error: reltuples must be finite (rejected with WARNING, returns false) SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'reltuples', 'Infinity'::real); SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'reltuples', '-Infinity'::real); SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'reltuples', 'NaN'::real); -- error: reltuples must not be less than -1.0 (rejected with WARNING, returns false) SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'reltuples', '-5'::real); -- reltuples is unchanged (still 500) after the rejected values above SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- ok: -1 (the "unknown" sentinel) is still accepted SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'reltuples', '-1'::real); SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- restore reltuples to 500 for the following tests SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'reltuples', '500'::real); -- ok: set just relallvisible, rest stay same SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'relallvisible', 5::integer); SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- ok: just relallfrozen SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'version', 150000::integer, 'relallfrozen', 3::integer); SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- warn: bad relpages type, rest updated SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'relpages', 'nope'::text, 'reltuples', 400.0::real, 'relallvisible', 4::integer, 'relallfrozen', 3::integer); SELECT relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- unrecognized argument name, rest ok SELECT pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'test', 'relpages', '171'::integer, 'nope', 10::integer); SELECT relpages, reltuples, relallvisible FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- ok: clear stats SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'test'); SELECT relpages, reltuples, relallvisible FROM pg_class WHERE oid = 'stats_import.test'::regclass; -- invalid relkinds for statistics CREATE SEQUENCE stats_import.testseq; SELECT pg_catalog.pg_restore_relation_stats( 'schemaname', 'stats_import', 'relname', 'testseq'); SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testseq'); CREATE VIEW stats_import.testview AS SELECT * FROM stats_import.test; SELECT pg_catalog.pg_clear_relation_stats(schemaname => 'stats_import', relname => 'testview'); -- -- attribute stats -- -- error: schemaname missing SELECT pg_catalog.pg_restore_attribute_stats( 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: schema does not exist SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'nope', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: relname missing SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: relname does not exist SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'nope', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: relname null SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', NULL, 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: NULL attname SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', NULL, 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: attname doesn't exist SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'nope', 'inherited', false::boolean, 'null_frac', 0.1::real, 'avg_width', 2::integer, 'n_distinct', 0.3::real); -- error: both attname and attnum SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'attnum', 1::smallint, 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: neither attname nor attnum SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: attribute is system column SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'xmin', 'inherited', false::boolean, 'null_frac', 0.1::real); -- error: inherited null SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', NULL::boolean, 'null_frac', 0.1::real); -- ok: just the fixed values, with version, no stakinds SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'version', 150000::integer, 'null_frac', 0.2::real, 'avg_width', 5::integer, 'n_distinct', 0.6::real); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- -- ok: restore by attnum, we normally reserve this for -- indexes, but there is no reason it shouldn't work -- for any stat-having relation. -- SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attnum', 1::smallint, 'inherited', false::boolean, 'null_frac', 0.4::real); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: unrecognized argument name, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.2::real, 'nope', 0.5::real); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: mcv / mcf null mismatch part 1, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.21::real, 'most_common_freqs', '{0.1,0.2,0.3}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: mcv / mcf null mismatch part 2, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.21::real, 'most_common_vals', '{1,2,3}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: mcf type mismatch, mcv-pair fails, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.22::real, 'most_common_vals', '{2,1,3}'::text, 'most_common_freqs', '{0.2,0.1}'::double precision[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: mcv cast failure, mcv-pair fails, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.23::real, 'most_common_vals', '{2,four,3}'::text, 'most_common_freqs', '{0.3,0.25,0.05}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: mcv / mcf array length mismatch (more vals), mcv-pair fails, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.24::real, 'most_common_vals', '{2,1,3}'::text, 'most_common_freqs', '{0.3,0.25}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: mcv / mcf array length mismatch (more freqs), mcv-pair fails, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.25::real, 'most_common_vals', '{2,1}'::text, 'most_common_freqs', '{0.3,0.25,0.05}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: most_common_vals is multi-dimensional, mcv-pair fails, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.26::real, 'most_common_vals', '{{2,1},{3,4}}'::text, 'most_common_freqs', '{0.3,0.25,0.05,0.04}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- ok: mcv+mcf SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'most_common_vals', '{2,1,3}'::text, 'most_common_freqs', '{0.3,0.25,0.05}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: NULL in histogram array, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.24::real, 'histogram_bounds', '{1,NULL,3,4}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- ok: histogram_bounds SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'histogram_bounds', '{1,2,3,4}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: elem_count_histogram null element, rest get set SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'tags', 'inherited', false::boolean, 'null_frac', 0.25::real, 'elem_count_histogram', '{1,1,NULL,1,1,1,1,1}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'tags'; -- ok: elem_count_histogram SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'tags', 'inherited', false::boolean, 'null_frac', 0.26::real, 'elem_count_histogram', '{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'tags'; -- warn: range stats on a scalar type, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.27::real, 'range_empty_frac', 0.5::real, 'range_length_histogram', '{399,499,Infinity}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: range_empty_frac range_length_hist null mismatch, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'arange', 'inherited', false::boolean, 'null_frac', 0.28::real, 'range_length_histogram', '{399,499,Infinity}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'arange'; -- warn: range_empty_frac range_length_hist null mismatch part 2, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'arange', 'inherited', false::boolean, 'null_frac', 0.29::real, 'range_empty_frac', 0.5::real ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'arange'; -- ok: range_empty_frac + range_length_hist SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'arange', 'inherited', false::boolean, 'range_empty_frac', 0.5::real, 'range_length_histogram', '{399,499,Infinity}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'arange'; -- warn: range bounds histogram on scalar, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.31::real, 'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- ok: range_bounds_histogram SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'arange', 'inherited', false::boolean, 'range_bounds_histogram', '{"[-1,1)","[0,4)","[1,4)","[1,100)"}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'arange'; -- warn: range bounds histogram with unsorted elements SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'arange', 'inherited', false::boolean, 'range_bounds_histogram', '{"[50,60)","[1,2)","[90,100)","[5,6)"}'::text ); -- warn: range bounds histogram with empty range SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'arange', 'inherited', false::boolean, 'range_bounds_histogram', '{empty,"[1,2)","[3,4)"}'::text ); -- warn: cannot set most_common_elems for range type, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'arange', 'inherited', false::boolean, 'null_frac', 0.32::real, 'most_common_elems', '{3,1}'::text, 'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'arange'; -- warn: scalars can't have mcelem, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.33::real, 'most_common_elems', '{1,3}'::text, 'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- warn: mcelem / mcelem mismatch, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'tags', 'inherited', false::boolean, 'null_frac', 0.34::real, 'most_common_elems', '{one,two}'::text ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'tags'; -- warn: mcelem / mcelem null mismatch part 2, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'tags', 'inherited', false::boolean, 'null_frac', 0.35::real, 'most_common_elem_freqs', '{0.3,0.2,0.2,0.3}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'tags'; -- ok: mcelem SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'tags', 'inherited', false::boolean, 'most_common_elems', '{one,three}'::text, 'most_common_elem_freqs', '{0.3,0.2,0.2,0.3,0.0}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'tags'; -- warn: scalars can't have elem_count_histogram, rest ok SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test', 'attname', 'id', 'inherited', false::boolean, 'null_frac', 0.36::real, 'elem_count_histogram', '{1,1,1,1,1,1,1,1,1,1}'::real[] ); SELECT * FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'id'; -- test for multiranges INSERT INTO stats_import.test_mr VALUES (1, 'red', '{[1,3),[5,9),[20,30)}'::int4multirange), (2, 'red', '{[11,13),[15,19),[20,30)}'::int4multirange), (3, 'red', '{[21,23),[25,29),[120,130)}'::int4multirange); -- warn: reject range values as ordinary multirange statistics SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'attname', 'mrange', 'inherited', false, 'most_common_vals', ARRAY['[1,3)']::text, 'most_common_freqs', ARRAY[1.0]::real[] ); -- ensure that we set attribute stats for a multirange -- MCVs and histograms retain the multirange type. SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'attname', 'mrange', 'inherited', false, 'most_common_vals', ARRAY['{[1,3),[5,9)}', '{[11,13),[15,19)}']::text, 'most_common_freqs', ARRAY[0.6, 0.4]::real[], 'histogram_bounds', ARRAY['{[1,3)}', '{[11,13)}', '{[21,23)}']::text, 'range_length_histogram', '{19,29,109}'::text, 'range_empty_frac', '0'::real, 'range_bounds_histogram', '{"[1,30)","[11,30)","[21,130)"}'::text ); -- -- Test the ability to exactly copy data from one table to an identical table, -- correctly reconstructing the stakind order as well as the staopN and -- stacollN values. Because oids are not stable across databases, we can only -- test this when the source and destination are on the same database -- instance. For that reason, we borrow and adapt a query found in fe_utils -- and used by pg_dump/pg_upgrade. -- INSERT INTO stats_import.test SELECT 1, 'one', (1, 1.1, 'ONE', '2001-01-01', '{ "xkey": "xval" }')::stats_import.complex_type, int4range(1,4), array['red','green'] UNION ALL SELECT 2, 'two', (2, 2.2, 'TWO', '2002-02-02', '[true, 4, "six"]')::stats_import.complex_type, int4range(1,4), array['blue','yellow'] UNION ALL SELECT 3, 'tre', (3, 3.3, 'TRE', '2003-03-03', NULL)::stats_import.complex_type, int4range(-1,1), array['"orange"', 'purple', 'cyan'] UNION ALL SELECT 4, 'four', NULL, int4range(0,100), NULL; CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1)); CREATE STATISTICS stats_import.test_stat ON name, comp, lower(arange), array_length(tags,1) FROM stats_import.test; CREATE STATISTICS stats_import.test_stat_ndistinct (ndistinct) ON name, comp FROM stats_import.test; CREATE STATISTICS stats_import.test_stat_dependencies (dependencies) ON name, comp FROM stats_import.test; CREATE STATISTICS stats_import.test_stat_mcv (mcv) ON name, comp FROM stats_import.test; CREATE STATISTICS stats_import.test_stat_ndistinct_exprs (ndistinct) ON lower(name), upper(name) FROM stats_import.test; CREATE STATISTICS stats_import.test_stat_dependencies_exprs (dependencies) ON lower(name), upper(name) FROM stats_import.test; CREATE STATISTICS stats_import.test_stat_mcv_exprs (mcv) ON lower(name), upper(name) FROM stats_import.test; -- Generate statistics on table with data ANALYZE stats_import.test; CREATE TABLE stats_import.test_clone ( LIKE stats_import.test ) WITH (autovacuum_enabled = false); CREATE INDEX is_odd_clone ON stats_import.test_clone(((comp).a % 2 = 1)); CREATE STATISTICS stats_import.test_stat_clone ON name, comp, lower(arange), array_length(tags,1) FROM stats_import.test_clone; -- -- Copy stats from test to test_clone, and is_odd to is_odd_clone -- SELECT s.schemaname, s.tablename, s.attname, s.inherited, r.* FROM pg_catalog.pg_stats AS s CROSS JOIN LATERAL pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 'relname', s.tablename::text || '_clone', 'attname', s.attname::text, 'inherited', s.inherited, 'version', 150000, 'null_frac', s.null_frac, 'avg_width', s.avg_width, 'n_distinct', s.n_distinct, 'most_common_vals', s.most_common_vals::text, 'most_common_freqs', s.most_common_freqs, 'histogram_bounds', s.histogram_bounds::text, 'correlation', s.correlation, 'most_common_elems', s.most_common_elems::text, 'most_common_elem_freqs', s.most_common_elem_freqs, 'elem_count_histogram', s.elem_count_histogram, 'range_bounds_histogram', s.range_bounds_histogram::text, 'range_empty_frac', s.range_empty_frac, 'range_length_histogram', s.range_length_histogram::text) AS r WHERE s.schemaname = 'stats_import' AND s.tablename IN ('test', 'is_odd') ORDER BY s.tablename, s.attname, s.inherited; SELECT c.relname, COUNT(*) AS num_stats FROM pg_class AS c JOIN pg_statistic s ON s.starelid = c.oid WHERE c.relnamespace = 'stats_import'::regnamespace AND c.relname IN ('test', 'test_clone', 'is_odd', 'is_odd_clone') GROUP BY c.relname ORDER BY c.relname; SELECT relname, (stats).* FROM stats_import.pg_statistic_get_difference('test', 'test_clone') \gx SELECT relname, (stats).* FROM stats_import.pg_statistic_get_difference('is_odd', 'is_odd_clone') \gx -- attribute stats exist before a clear, but not after SELECT COUNT(*) FROM pg_stats WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'arange'; SELECT pg_catalog.pg_clear_attribute_stats( schemaname => 'stats_import', relname => 'test', attname => 'arange', inherited => false); SELECT COUNT(*) FROM pg_stats WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false AND attname = 'arange'; -- temp tables CREATE TEMP TABLE stats_temp(i int); SELECT pg_restore_relation_stats( 'schemaname', 'pg_temp', 'relname', 'stats_temp', 'relpages', '-19'::integer, 'reltuples', 401::real, 'relallvisible', 5::integer, 'relallfrozen', 3::integer); SELECT relname, relpages, reltuples, relallvisible, relallfrozen FROM pg_class WHERE oid = 'pg_temp.stats_temp'::regclass ORDER BY relname; SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'pg_temp', 'relname', 'stats_temp', 'attname', 'i', 'inherited', false::boolean, 'null_frac', 0.0123::real ); SELECT tablename, null_frac FROM pg_stats WHERE schemaname like 'pg_temp%' AND tablename = 'stats_temp' AND inherited = false AND attname = 'i'; DROP TABLE stats_temp; -- Tests for pg_clear_extended_stats(). -- Invalid argument values. SELECT pg_clear_extended_stats(schemaname => NULL, relname => 'rel_foo', statistics_schemaname => 'schema_foo', statistics_name => 'stat_bar', inherited => false); SELECT pg_clear_extended_stats(schemaname => 'schema_foo', relname => NULL, statistics_schemaname => 'schema_foo', statistics_name => 'stat_bar', inherited => false); SELECT pg_clear_extended_stats(schemaname => 'schema_foo', relname => 'rel_foo', statistics_schemaname => NULL, statistics_name => 'stat_bar', inherited => false); SELECT pg_clear_extended_stats(schemaname => 'schema_foo', relname => 'rel_foo', statistics_schemaname => 'schema_foo', statistics_name => NULL, inherited => false); SELECT pg_clear_extended_stats(schemaname => 'schema_foo', relname => 'rel_foo', statistics_schemaname => 'schema_foo', statistics_name => 'stat_bar', inherited => NULL); -- Missing objects SELECT pg_clear_extended_stats(schemaname => 'schema_not_exist', relname => 'test', statistics_schemaname => 'schema_not_exist', statistics_name => 'test_stat', inherited => false); SELECT pg_clear_extended_stats(schemaname => 'stats_import', relname => 'table_not_exist', statistics_schemaname => 'stats_import', statistics_name => 'test_stat', inherited => false); SELECT pg_clear_extended_stats(schemaname => 'stats_import', relname => 'test', statistics_schemaname => 'schema_not_exist', statistics_name => 'test_stat', inherited => false); SELECT pg_clear_extended_stats(schemaname => 'stats_import', relname => 'test', statistics_schemaname => 'stats_import', statistics_name => 'ext_stats_not_exist', inherited => false); -- Incorrect relation/extended stats combination SELECT pg_clear_extended_stats(schemaname => 'stats_import', relname => 'test', statistics_schemaname => 'stats_import', statistics_name => 'test_stat_clone', inherited => false); -- Check that records are removed after a valid clear call. SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat' GROUP BY e.inherited; SELECT COUNT(*), e.inherited FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat' GROUP BY e.inherited; BEGIN; SELECT pg_catalog.pg_clear_extended_stats( schemaname => 'stats_import', relname => 'test', statistics_schemaname => 'stats_import', statistics_name => 'test_stat', inherited => false); SELECT mode FROM pg_locks WHERE locktype = 'relation' AND relation = 'stats_import.test'::regclass AND pid = pg_backend_pid(); COMMIT; SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat' GROUP BY e.inherited; SELECT COUNT(*), e.inherited FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat' GROUP BY e.inherited; -- And before/after on inherited stats SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'part_parent_stat' GROUP BY e.inherited; SELECT pg_catalog.pg_clear_extended_stats( schemaname => 'stats_import', relname => 'part_parent', statistics_schemaname => 'stats_import', statistics_name => 'part_parent_stat', inherited => true); SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'part_parent_stat' GROUP BY e.inherited; -- Check that MAINTAIN is required when clearing statistics. CREATE ROLE regress_test_extstat_clear; GRANT ALL ON SCHEMA stats_import TO regress_test_extstat_clear; SET ROLE regress_test_extstat_clear; SELECT pg_catalog.pg_clear_extended_stats( schemaname => 'stats_import', relname => 'test', statistics_schemaname => 'stats_import', statistics_name => 'test_stat', inherited => false); RESET ROLE; GRANT MAINTAIN ON stats_import.test TO regress_test_extstat_clear; SET ROLE regress_test_extstat_clear; SELECT pg_catalog.pg_clear_extended_stats( schemaname => 'stats_import', relname => 'test', statistics_schemaname => 'stats_import', statistics_name => 'test_stat', inherited => false); RESET ROLE; REVOKE MAINTAIN ON stats_import.test FROM regress_test_extstat_clear; REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_clear; DROP ROLE regress_test_extstat_clear; -- Tests for pg_restore_extended_stats(). -- Invalid argument values. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', NULL, 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', NULL, 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', NULL, 'statistics_name', 'test_stat_clone', 'inherited', false); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', NULL, 'inherited', false); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', NULL); -- Missing objects SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'schema_not_exist', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'table_not_exist', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'schema_not_exist', 'statistics_name', 'test_stat_clone', 'inherited', false); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'ext_stats_not_exist', 'inherited', false); -- Incorrect relation/extended stats combination SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false); -- Check that MAINTAIN is required when restoring statistics. CREATE ROLE regress_test_extstat_restore; GRANT ALL ON SCHEMA stats_import TO regress_test_extstat_restore; SET ROLE regress_test_extstat_restore; -- No data to restore; this fails on a permission failure. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false); RESET ROLE; GRANT MAINTAIN ON stats_import.test_clone TO regress_test_extstat_restore; SET ROLE regress_test_extstat_restore; -- This works, check the lock on the relation while on it. BEGIN; SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'n_distinct', '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct); SELECT mode FROM pg_locks WHERE locktype = 'relation' AND relation = 'stats_import.test_clone'::regclass AND pid = pg_backend_pid(); COMMIT; RESET ROLE; REVOKE MAINTAIN ON stats_import.test_clone FROM regress_test_extstat_restore; REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_restore; DROP ROLE regress_test_extstat_restore; -- ndistinct value doesn't match object definition SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_ndistinct', 'inherited', false, 'n_distinct', '[{"attributes" : [1,3], "ndistinct" : 4}]'::pg_ndistinct); -- Incorrect extended stats kind, ndistinct not supported SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_dependencies', 'inherited', false, 'n_distinct', '[{"attributes" : [1,3], "ndistinct" : 4}]'::pg_ndistinct); -- Incorrect extended stats kind, dependencies not supported SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_ndistinct', 'inherited', false, 'dependencies', '[{"attributes": [2], "dependency": 3, "degree": 1.000000}, {"attributes": [3], "dependency": 2, "degree": 1.000000}]'::pg_dependencies); -- ok: ndistinct SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_ndistinct', 'inherited', false, 'n_distinct', '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct); -- dependencies value doesn't match definition SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_dependencies', 'inherited', false, 'dependencies', '[{"attributes": [1], "dependency": 3, "degree": 1.000000}, {"attributes": [3], "dependency": 1, "degree": 1.000000}]'::pg_dependencies); -- ok: dependencies SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_dependencies', 'inherited', false, 'dependencies', '[{"attributes": [2], "dependency": 3, "degree": 1.000000}, {"attributes": [3], "dependency": 2, "degree": 1.000000}]'::pg_dependencies); -- ndistinct with expressions, invalid attributes. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_ndistinct_exprs', 'inherited', false, 'n_distinct', '[{"attributes" : [1,-1], "ndistinct" : 4}]'::pg_ndistinct); -- ok: ndistinct with expressions. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_ndistinct_exprs', 'inherited', false, 'n_distinct', '[{"attributes" : [-1,-2], "ndistinct" : 4}]'::pg_ndistinct); -- dependencies with expressions, invalid attributes. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_dependencies_exprs', 'inherited', false, 'dependencies', '[{"attributes": [-1], "dependency": 1, "degree": 1.000000}, {"attributes": [1], "dependency": -1, "degree": 1.000000}]'::pg_dependencies); -- ok: dependencies with expressions SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_dependencies_exprs', 'inherited', false, 'dependencies', '[{"attributes": [-1], "dependency": -2, "degree": 1.000000}, {"attributes": [-2], "dependency": -1, "degree": 1.000000}]'::pg_dependencies); -- ok: MCV with expressions SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv_exprs', 'inherited', false, 'most_common_vals', '{{four,FOUR},{one,NULL},{NULL,TRE},{two,TWO}}'::text[], 'most_common_freqs', '{0.25,0.25,0.25,0.99}'::double precision[], 'most_common_base_freqs', '{0.0625,0.0625,0.023,0.087}'::double precision[]); -- Check the presence of the restored stats, for each object. SELECT replace(e.n_distinct, '}, ', E'},\n') AS n_distinct FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_ndistinct' AND e.inherited = false; SELECT replace(e.dependencies, '}, ', E'},\n') AS dependencies FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_dependencies' AND e.inherited = false; SELECT replace(e.n_distinct, '}, ', E'},\n') AS n_distinct FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_ndistinct_exprs' AND e.inherited = false; SELECT replace(e.dependencies, '}, ', E'},\n') AS dependencies FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_dependencies_exprs' AND e.inherited = false; SELECT e.most_common_vals, e.most_common_val_nulls, e.most_common_freqs, e.most_common_base_freqs FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_mcv_exprs' AND e.inherited = false \gx -- Incorrect extended stats kind, mcv not supported SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_dependencies', 'inherited', false, 'most_common_vals', '{{four,NULL}, {one,"(1,1.1,ONE,01-01-2001,\"{\"\"xkey\"\": \"\"xval\"\"}\")"}, {tre,"(3,3.3,TRE,03-03-2003,)"}, {two,"(2,2.2,TWO,02-02-2002,\"[true, 4, \"\"six\"\"]\")"}}'::text[], 'most_common_freqs', '{0.25,0.25,0.25,0.25}'::double precision[], 'most_common_base_freqs', '{0.0625,0.0625,0.0625,0.0625}'::double precision[]); -- MCV requires all three parameters SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_freqs', '{0.25,0.25,0.25,0.25}'::double precision[], 'most_common_base_freqs', '{0.0625,0.0625,0.0625,0.0625}'::double precision[]); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', '{{four,NULL}, {one,"(1,1.1,ONE,01-01-2001,\"{\"\"xkey\"\": \"\"xval\"\"}\")"}, {tre,"(3,3.3,TRE,03-03-2003,)"}, {two,"(2,2.2,TWO,02-02-2002,\"[true, 4, \"\"six\"\"]\")"}}'::text[], 'most_common_base_freqs', '{0.0625,0.0625,0.0625,0.0625}'::double precision[]); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', '{{four,NULL}, {one,"(1,1.1,ONE,01-01-2001,\"{\"\"xkey\"\": \"\"xval\"\"}\")"}, {tre,"(3,3.3,TRE,03-03-2003,)"}, {two,"(2,2.2,TWO,02-02-2002,\"[true, 4, \"\"six\"\"]\")"}}'::text[], 'most_common_freqs', '{0.25,0.25,0.25,0.25}'::double precision[]); -- most_common_vals that is not 2-D SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', '{four,NULL}'::text[], 'most_common_freqs', '{0.25,0.25,0.25,0.25}'::double precision[], 'most_common_base_freqs', '{0.0625,0.0625,0.0625,0.0625}'::double precision[]); -- most_common_freqs with length not matching with most_common_vals. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', '{{four,NULL}, {one,"(1,1.1,ONE,01-01-2001,\"{\"\"xkey\"\": \"\"xval\"\"}\")"}, {tre,"(3,3.3,TRE,03-03-2003,)"}, {two,"(2,2.2,TWO,02-02-2002,\"[true, 4, \"\"six\"\"]\")"}}'::text[], 'most_common_freqs', '{0.25,0.25,0.25}'::double precision[], 'most_common_base_freqs', '{0.0625,0.0625,0.0625,0.0625}'::double precision[]); -- most_common_base_freqs with length not matching most_common_vals. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', '{{four,NULL}, {one,"(1,1.1,ONE,01-01-2001,\"{\"\"xkey\"\": \"\"xval\"\"}\")"}, {tre,"(3,3.3,TRE,03-03-2003,)"}, {two,"(2,2.2,TWO,02-02-2002,\"[true, 4, \"\"six\"\"]\")"}}'::text[], 'most_common_freqs', '{0.25,0.25,0.25,0.25}'::double precision[], 'most_common_base_freqs', '{0.0625,0.0625,0.0625}'::double precision[]); -- mcv attributes not matching object definition SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', '{{four,NULL,0,NULL}, {one,"(1,1.1,ONE,01-01-2001,\"{\"\"xkey\"\": \"\"xval\"\"}\")",1,2}, {tre,"(3,3.3,TRE,03-03-2003,)",-1,3}, {two,"(2,2.2,TWO,02-02-2002,\"[true, 4, \"\"six\"\"]\")",1,2}}'::text[], 'most_common_freqs', '{0.25,0.25,0.25,0.25}'::double precision[], 'most_common_base_freqs', '{0.00390625,0.015625,0.00390625,0.015625}'::double precision[]); -- warn: more MCV items than can be handled. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', (SELECT array_agg(ARRAY[g::text, g::text]) FROM generate_series(1, 10001) g), 'most_common_freqs', (SELECT array_agg((1.0 / 10001)::double precision) FROM generate_series(1, 10001) g), 'most_common_base_freqs', (SELECT array_agg((1.0 / 10001)::double precision) FROM generate_series(1, 10001) g)); -- ok: mcv SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcv', 'inherited', false, 'most_common_vals', '{{four,NULL}, {one,"(1,1.1,ONE,01-01-2001,\"{\"\"xkey\"\": \"\"xval\"\"}\")"}, {tre,"(3,3.3,TRE,03-03-2003,)"}, {two,"(2,2.2,TWO,02-02-2002,\"[true, 4, \"\"six\"\"]\")"}}'::text[], 'most_common_freqs', '{0.25,0.25,0.25,0.25}'::double precision[], 'most_common_base_freqs', '{0.0625,0.0625,0.0625,0.0625}'::double precision[]); SELECT replace(e.most_common_vals::text, '},', E'},\n ') AS mcvs, e.most_common_val_nulls, e.most_common_freqs, e.most_common_base_freqs FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_mcv' AND e.inherited = false \gx -- Check import of all kinds for multirange. CREATE STATISTICS stats_import.test_mr_stat ON name, mrange, ( mrange + '{[10000,10200)}'::int4multirange) FROM stats_import.test_mr; CREATE TABLE stats_import.test_mr_clone ( LIKE stats_import.test_mr ) WITH (autovacuum_enabled = false); CREATE STATISTICS stats_import.test_mr_stat_clone ON name, mrange, ( mrange + '{[10000,10200)}'::int4multirange) FROM stats_import.test_mr_clone; -- Check for invalid value combinations for range types. -- Only range_bounds_histogram (other two missing) SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_mr_stat', 'inherited', false, 'exprs', '[{"range_bounds_histogram": "{\"[1,10200)\",\"[11,10200)\"}"}]'::jsonb); -- Only range_length_histogram and range_empty_frac -- (range_bounds_histogram missing) SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_mr_stat', 'inherited', false, 'exprs', '[{"range_length_histogram": "{10179,10189}", "range_empty_frac": "0"}]'::jsonb); -- Only range_bounds_histogram and range_empty_frac -- (range_length_histogram missing) SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_mr_stat', 'inherited', false, 'exprs', '[{"range_bounds_histogram": "{\"[1,10200)\"}", "range_empty_frac": "0"}]'::jsonb); -- Only range_bounds_histogram and range_length_histogram -- (range_empty_frac missing) SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_mr_stat', 'inherited', false, 'exprs', '[{"range_bounds_histogram": "{\"[1,10200)\"}", "range_length_histogram": "{10179}"}]'::jsonb); -- warn: range bounds histogram with unsorted elements SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_mr_stat', 'inherited', false, 'exprs', '[{"range_length_histogram": "{10179,10189,10199}", "range_empty_frac": "0", "range_bounds_histogram": "{\"[50,60)\",\"[1,2)\",\"[90,100)\"}"}]'::jsonb); -- warn: range bounds histogram with empty range SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_mr_stat', 'inherited', false, 'exprs', '[{"range_length_histogram": "{10179,10189,10199}", "range_empty_frac": "0", "range_bounds_histogram": "{empty,\"[1,2)\",\"[3,4)\"}"}]'::jsonb); -- ok: multirange stats SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_mr', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_mr_stat', 'inherited', false, 'n_distinct', '[{"attributes": [2, 3], "ndistinct": 3}, {"attributes": [2, -1], "ndistinct": 3}, {"attributes": [3, -1], "ndistinct": 3}, {"attributes": [2, 3, -1], "ndistinct": 3}]'::pg_catalog.pg_ndistinct, 'dependencies', '[{"attributes": [3], "dependency": 2, "degree": 1.000000}, {"attributes": [3], "dependency": -1, "degree": 1.000000}, {"attributes": [-1], "dependency": 2, "degree": 1.000000}, {"attributes": [-1], "dependency": 3, "degree": 1.000000}, {"attributes": [2, 3], "dependency": -1, "degree": 1.000000}, {"attributes": [2, -1], "dependency": 3, "degree": 1.000000}, {"attributes": [3, -1], "dependency": 2, "degree": 1.000000}]'::pg_catalog.pg_dependencies, 'most_common_vals', '{{red,"{[1,3),[5,9),[20,30)}","{[1,3),[5,9),[20,30),[10000,10200)}"}, {red,"{[11,13),[15,19),[20,30)}","{[11,13),[15,19),[20,30),[10000,10200)}"}, {red,"{[21,23),[25,29),[120,130)}","{[21,23),[25,29),[120,130),[10000,10200)}"}}'::text[], 'most_common_freqs', '{0.3333333333333333,0.3333333333333333,0.3333333333333333}'::double precision[], 'most_common_base_freqs', '{0.1111111111111111,0.1111111111111111,0.1111111111111111}'::double precision[], 'exprs', '[{ "avg_width": "60", "null_frac": "0", "n_distinct": "-1", "range_length_histogram": "{10179,10189,10199}", "range_empty_frac": "0", "range_bounds_histogram": "{\"[1,10200)\",\"[11,10200)\",\"[21,10200)\"}" }]'::jsonb); SELECT replace(e.n_distinct, '}, ', E'},\n') AS n_distinct, replace(e.dependencies, '}, ', E'},\n') AS dependencies, replace(e.most_common_vals::text, '},', E'},\n ') AS mcvs, e.most_common_val_nulls, e.most_common_freqs, e.most_common_base_freqs FROM pg_stats_ext AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_mr_stat' AND e.inherited = false \gx SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals, e.most_common_freqs, e.histogram_bounds, e.correlation, e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram, e.range_length_histogram, e.range_empty_frac, e.range_bounds_histogram FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_mr_stat' AND e.inherited = false \gx -- Incorrect extended stats kind, exprs not supported SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_ndistinct', 'inherited', false, 'exprs', '[ { "avg_width": "4" } ]'::jsonb); -- Invalid exprs, not an array SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '{ "avg_width": "4", "null_frac": "0" }'::jsonb); -- wrong number of exprs, too few SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "avg_width": "4" } ]'::jsonb); -- wrong number of exprs, too many SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "avg_width": "4" }, { "avg_width": "4" }, { "avg_width": "4" } ]'::jsonb); -- incorrect type of value: should be a string or a NULL. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "null_frac": 1 }, { "null_frac": "0.25" } ]'::jsonb); -- exprs null_frac not a float SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "null_frac": "BADNULLFRAC" }, { "null_frac": "0.25" } ]'::jsonb); -- exprs avg_width not an integer SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "avg_width": "BADAVGWIDTH" }, { "avg_width": "4" } ]'::jsonb); -- exprs n_dinstinct not a float SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "n_distinct": "BADNDISTINCT" }, { "n_distinct": "-0.5" } ]'::jsonb); -- MCV not null, MCF null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "most_common_vals": "{1}", "most_common_elems": null }, { "most_common_vals": "{2}", "most_common_freqs": "{0.5}" } ]'::jsonb); -- MCV not null, MCF missing SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "most_common_vals": "{1}" }, { "most_common_vals": "{2}", "most_common_freqs": "{0.5}" } ]'::jsonb); -- MCV null, MCF not null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "most_common_vals": null, "most_common_freqs": "{0.5}" }, { "most_common_vals": "{2}", "most_common_freqs": "{0.5}" } ]'::jsonb); -- MCV missing, MCF not null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "most_common_freqs": "{0.5}" }, { "most_common_vals": "{2}", "most_common_freqs": "{0.5}" } ]'::jsonb); -- exprs most_common_vals is multi-dimensional SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "most_common_vals": "{{1,2},{3,4}}", "most_common_freqs": "{0.3,0.25,0.05,0.04}" }, { "most_common_vals": "{2}", "most_common_freqs": "{0.5}" } ]'::jsonb); -- exprs most_common_vals element wrong type SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "avg_width": "4", "null_frac": "0", "n_distinct": "-0.75", "correlation": "-0.6", "histogram_bounds": "{-1,0}", "most_common_vals": "{BADMCV}", "most_common_elems": null, "most_common_freqs": "{0.5}", "elem_count_histogram": null, "most_common_elem_freqs": null }, { "avg_width": "4", "null_frac": "0.25", "n_distinct": "-0.5", "correlation": "1", "histogram_bounds": null, "most_common_vals": "{2}", "most_common_elems": null, "most_common_freqs": "{0.5}", "elem_count_histogram": null, "most_common_elem_freqs": null } ]'::jsonb); -- exprs most_common_freqs element wrong type SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "most_common_vals": "{1}", "most_common_freqs": "{BADMCF}" }, { "most_common_vals": "{2}", "most_common_freqs": "{0.5}" } ]'::jsonb); -- exprs most_common_vals / most_common_freqs array length mismatch SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "most_common_vals": "{1,3}", "most_common_freqs": "{0.5}" }, { "most_common_vals": "{2}", "most_common_freqs": "{0.5}" } ]'::jsonb); -- exprs histogram wrong type SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "histogram_bounds": "{BADHIST,0}" }, { "histogram_bounds": null } ]'::jsonb); -- exprs correlation wrong type SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "correlation": "BADCORR" }, { "correlation": "1" } ]'::jsonb); -- invalid element type in array SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[1, null]'::jsonb); -- invalid element in array, as valid jbvBinary. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[null, [{"avg_width" : [1]}]]'::jsonb); -- only range types can have range_length_histogram, range_empty_frac -- or range_bounds_histogram. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ null, { "range_length_histogram": "{10179,10189,10199}", "range_empty_frac": "0", "range_bounds_histogram": "{10200,10200,10200}" } ]'::jsonb); -- only array types can have most_common_elems or elem_count_histogram. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ null, { "most_common_elems": "{1,2,3}", "most_common_elem_freqs": "{0.3,0.3,0.4}" } ]'::jsonb); SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ null, { "elem_count_histogram": "{1,2,3,4,5}" } ]'::jsonb); -- ok: exprs first null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ null, { "avg_width": "4", "null_frac": "0.25", "n_distinct": "-0.5", "correlation": "1", "histogram_bounds": null, "most_common_vals": "{2}", "most_common_elems": null, "most_common_freqs": "{0.5}", "elem_count_histogram": null, "most_common_elem_freqs": null } ]'::jsonb); SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals, e.most_common_freqs, e.histogram_bounds, e.correlation, e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram, e.range_length_histogram, e.range_empty_frac, e.range_bounds_histogram FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_clone' AND e.inherited = false \gx -- ok: exprs last null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "avg_width": "4", "null_frac": "0", "n_distinct": "-0.75", "correlation": "-0.6", "histogram_bounds": "{-1,0}", "most_common_vals": "{1}", "most_common_elems": null, "most_common_freqs": "{0.5}", "elem_count_histogram": null, "most_common_elem_freqs": null }, null ]'::jsonb); SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals, e.most_common_freqs, e.histogram_bounds, e.correlation, e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_clone' AND e.inherited = false \gx -- ok: both exprs SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_clone', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_clone', 'inherited', false, 'exprs', '[ { "avg_width": "4", "null_frac": "0", "n_distinct": "-0.75", "correlation": "-0.6", "histogram_bounds": "{-1,0}", "most_common_vals": "{1}", "most_common_elems": null, "most_common_freqs": "{0.5}", "elem_count_histogram": null, "most_common_elem_freqs": null }, { "avg_width": "4", "null_frac": "0.25", "n_distinct": "-0.5", "correlation": "1", "histogram_bounds": null, "most_common_vals": "{2}", "most_common_elems": null, "most_common_freqs": "{0.5}", "elem_count_histogram": null, "most_common_elem_freqs": null } ]'::jsonb); SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals, e.most_common_freqs, e.histogram_bounds, e.correlation, e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_clone' AND e.inherited = false \gx -- A statistics object for testing MCELEM values in expressions CREATE STATISTICS stats_import.test_stat_mcelem ON name, (ARRAY[(comp).a, lower(arange)]) FROM stats_import.test; -- MCEV not null, MCEF null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "most_common_elems": "{-1,0,1,2,3}", "most_common_elem_freqs": null } ]'::jsonb); -- MCEV not null, MCEF missing SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "most_common_elems": "{-1,0,1,2,3}" } ]'::jsonb); -- MCEV null, MCEF not null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "most_common_elems": null, "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}" } ]'::jsonb); -- MCEV missing, MCEF not null SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}" } ]'::jsonb); -- exprs most_common_elems element wrong type SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "most_common_elems": "{-1,BADELEM,1,2,3}", "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}" } ]'::jsonb); -- exprs most_common_elem_freqs element wrong type SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "most_common_elems": "{-1,0,1,2,3}", "most_common_elem_freqs": "{BADELEMFREQ,0.25,0.5,0.25,0.25,0.25,0.5,0.25}" } ]'::jsonb); -- exprs histogram bounds element wrong type SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "elem_count_histogram": "{BADELEMHIST,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}" } ]'::jsonb); -- ok: exprs mcelem SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "avg_width": "33", "null_frac": "0", "n_distinct": "-1", "correlation": "1", "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}", "most_common_vals": null, "most_common_elems": "{-1,0,1,2,3}", "most_common_freqs": null, "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}", "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}" } ]'::jsonb); SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals, e.most_common_freqs, e.histogram_bounds, e.correlation, e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_mcelem' AND e.inherited = false \gx -- ok, with warning: extra exprs param SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[ { "avg_width": "33", "null_frac": "0", "n_distinct": "-1", "correlation": "1", "histogram_bounds": "{\"{1,1}\",\"{2,1}\",\"{3,-1}\",\"{NULL,0}\"}", "most_common_vals": null, "most_common_elems": "{-1,0,1,2,3}", "most_common_freqs": null, "elem_count_histogram": "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1.5}", "most_common_elem_freqs": "{0.25,0.25,0.5,0.25,0.25,0.25,0.5,0.25}", "bad_param": "text no one will ever parse" } ]'::jsonb); SELECT e.expr, e.null_frac, e.avg_width, e.n_distinct, e.most_common_vals, e.most_common_freqs, e.histogram_bounds, e.correlation, e.most_common_elems, e.most_common_elem_freqs, e.elem_count_histogram FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_mcelem' AND e.inherited = false \gx -- bad: exprs param which is a prefix of a valid key name SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_mcelem', 'inherited', false, 'exprs', '[{ "n": "-1" }]'::jsonb); -- ok: tsvector exceptions, test just the collation exceptions CREATE STATISTICS stats_import.test_stat_tsvec ON (length(name)), (to_tsvector(name)) FROM stats_import.test; SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test', 'statistics_schemaname', 'stats_import', 'statistics_name', 'test_stat_tsvec', 'inherited', false, 'exprs', '[null, { "most_common_elems": "{one,tre,two,four}", "most_common_elem_freqs": "{0.25,0.25,0.25,0.25,0.25,0.25}" } ]'::jsonb); SELECT e.expr, e.most_common_elems, e.most_common_elem_freqs FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat_tsvec' AND e.inherited = false \gx -- Test the ability of pg_restore_extended_stats() to import all of the -- statistic values from an extended statistic object that has been -- populated via a regular ANALYZE. This checks after the statistics -- kinds supported by pg_restore_extended_stats(). -- -- Note: Keep this test at the bottom of the file, so as the amount of -- statistics data handled is maximized. ANALYZE stats_import.test; -- Copy stats from test_stat to test_stat_clone SELECT e.statistics_name, pg_catalog.pg_restore_extended_stats( 'schemaname', e.statistics_schemaname::text, 'relname', 'test_clone', 'statistics_schemaname', e.statistics_schemaname::text, 'statistics_name', 'test_stat_clone', 'inherited', e.inherited, 'n_distinct', e.n_distinct, 'dependencies', e.dependencies, 'most_common_vals', e.most_common_vals, 'most_common_freqs', e.most_common_freqs, 'most_common_base_freqs', e.most_common_base_freqs, 'exprs', x.exprs) FROM pg_stats_ext AS e CROSS JOIN LATERAL ( SELECT jsonb_agg(jsonb_strip_nulls(jsonb_build_object( 'null_frac', ee.null_frac::text, 'avg_width', ee.avg_width::text, 'n_distinct', ee.n_distinct::text, 'most_common_vals', ee.most_common_vals::text, 'most_common_freqs', ee.most_common_freqs::text, 'histogram_bounds', ee.histogram_bounds::text, 'correlation', ee.correlation::text, 'most_common_elems', ee.most_common_elems::text, 'most_common_elem_freqs', ee.most_common_elem_freqs::text, 'elem_count_histogram', ee.elem_count_histogram::text, 'range_length_histogram', ee.range_length_histogram::text, 'range_empty_frac', ee.range_empty_frac::text, 'range_bounds_histogram', ee.range_bounds_histogram::text))) FROM pg_stats_ext_exprs AS ee WHERE ee.statistics_schemaname = e.statistics_schemaname AND ee.statistics_name = e.statistics_name AND ee.inherited = e.inherited ) AS x(exprs) WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat'; SELECT statname, (stats).* FROM stats_import.pg_stats_ext_get_difference('test_stat', 'test_stat_clone') \gx SELECT statname, (stats).* FROM stats_import.pg_stats_ext_exprs_get_difference('test_stat', 'test_stat_clone') \gx ANALYZE stats_import.test_mr; -- Copy stats from test_mr_stat to test_mr_stat_clone SELECT e.statistics_name, pg_catalog.pg_restore_extended_stats( 'schemaname', e.statistics_schemaname::text, 'relname', 'test_mr_clone', 'statistics_schemaname', e.statistics_schemaname::text, 'statistics_name', 'test_mr_stat_clone', 'inherited', e.inherited, 'n_distinct', e.n_distinct, 'dependencies', e.dependencies, 'most_common_vals', e.most_common_vals, 'most_common_freqs', e.most_common_freqs, 'most_common_base_freqs', e.most_common_base_freqs, 'exprs', x.exprs) FROM pg_stats_ext AS e CROSS JOIN LATERAL ( SELECT jsonb_agg(jsonb_strip_nulls(jsonb_build_object( 'null_frac', ee.null_frac::text, 'avg_width', ee.avg_width::text, 'n_distinct', ee.n_distinct::text, 'most_common_vals', ee.most_common_vals::text, 'most_common_freqs', ee.most_common_freqs::text, 'histogram_bounds', ee.histogram_bounds::text, 'correlation', ee.correlation::text, 'most_common_elems', ee.most_common_elems::text, 'most_common_elem_freqs', ee.most_common_elem_freqs::text, 'elem_count_histogram', ee.elem_count_histogram::text, 'range_length_histogram', ee.range_length_histogram::text, 'range_empty_frac', ee.range_empty_frac::text, 'range_bounds_histogram', ee.range_bounds_histogram::text))) FROM pg_stats_ext_exprs AS ee WHERE ee.statistics_schemaname = e.statistics_schemaname AND ee.statistics_name = e.statistics_name AND ee.inherited = e.inherited ) AS x(exprs) WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_mr_stat'; SELECT statname, (stats).* FROM stats_import.pg_stats_ext_get_difference('test_mr_stat', 'test_mr_stat_clone') \gx SELECT statname, (stats).* FROM stats_import.pg_stats_ext_exprs_get_difference('test_mr_stat', 'test_mr_stat_clone') \gx -- range_length_histogram, range_empty_frac, and range_bounds_histogram -- have been added to pg_stats_ext_exprs in PostgreSQL 19. When dumping -- expression statistics in a cluster with an older version, these fields -- are dumped as NULL, pg_restore_extended_stats() authorizing the partial -- restore state of the extended statistics data. This test emulates such -- a case by calling pg_restore_extended_stats() with NULL values for all -- the three range fields, then checks the statistics loaded with some -- queries. CREATE TABLE stats_import.test_range_expr_null( id INTEGER PRIMARY KEY, name TEXT, rng int4range NOT NULL ); INSERT INTO stats_import.test_range_expr_null SELECT i, 'name_' || (i % 10), int4range(i, i + 10) FROM generate_series(1, 100) i; -- Create statistics with a range expression CREATE STATISTICS stats_import.stat_range_expr_null ON name, (rng * int4range(50, 150)) FROM stats_import.test_range_expr_null; ANALYZE stats_import.test_range_expr_null; -- Verify range statistics were created SELECT e.expr, e.range_length_histogram IS NOT NULL AS has_range_len, e.range_empty_frac IS NOT NULL AS has_range_empty, e.range_bounds_histogram IS NOT NULL AS has_range_bounds FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'stat_range_expr_null'; -- Import statistics with NULL range fields, simulating dump from -- older version. SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', 'relname', 'test_range_expr_null', 'statistics_schemaname', 'stats_import', 'statistics_name', 'stat_range_expr_null', 'inherited', false, 'exprs', '[ { "avg_width": "14", "null_frac": "0", "n_distinct": "-1" } ]'::jsonb); -- Verify that range fields are now NULL. SELECT e.expr, e.null_frac, e.range_length_histogram IS NOT NULL AS has_range_len, e.range_empty_frac IS NOT NULL AS has_range_empty, e.range_bounds_histogram IS NOT NULL AS has_range_bounds FROM pg_stats_ext_exprs AS e WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'stat_range_expr_null'; -- Trigger statistics loading through some queries. EXPLAIN (COSTS OFF) SELECT * FROM stats_import.test_range_expr_null WHERE (rng * int4range(50, 150)) && '[60,70)'::int4range; SELECT COUNT(*) FROM stats_import.test_range_expr_null WHERE (rng * int4range(50, 150)) && '[60,70)'::int4range; DROP SCHEMA stats_import CASCADE;