From 5c2245a2144f5218b4a104043052ae4f76be2bca Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Thu, 24 Sep 2026 13:04:53 +0900 Subject: [PATCH] Fix import of statistics for domains over [multi]range types pg_restore_attribute_stats() rejected range_length_histogram, range_empty_frac and range_bounds_histogram for a column whose type is a domain over a range or a multirange type. ANALYZE is able to generate such stats, incorporating the knowledge to handle domains in the in-core typanalyze callbacks. pg_restore_extended_stats() had the same problem for expressions whose type is a domain over a range or a multirange type, assuming that it was safe to rely on a hardcoded TYPTYPE_RANGE or TYPTYPE_MULTIRANGE. This problem is resolved with the introduction of a new routine that englobes the decision of the type to use, statatt_get_range_type(), able to work for domains, range and multirange types. The base type is resolved first. Tests are included in a fashion consistent with the surroundings. The consequence of this issue was the rejection of stats that ANALYZE is able to build, not critical, still annoying. Reported-by: Qifan Liu Discussion: https://postgr.es/m/19715-b8be35083016f289@postgresql.org Backpatch-through: 18 --- src/include/statistics/stat_utils.h | 1 + src/backend/statistics/attribute_stats.c | 12 +- src/backend/statistics/extended_stats_funcs.c | 12 +- src/backend/statistics/stat_utils.c | 30 +++ src/test/regress/expected/stats_import.out | 224 +++++++++++++++++- src/test/regress/sql/stats_import.sql | 163 +++++++++++++ 6 files changed, 424 insertions(+), 18 deletions(-) diff --git a/src/include/statistics/stat_utils.h b/src/include/statistics/stat_utils.h index 15e962dbb7c8..8ade6b140aa9 100644 --- a/src/include/statistics/stat_utils.h +++ b/src/include/statistics/stat_utils.h @@ -57,6 +57,7 @@ extern Datum statatt_build_stavalues(const char *staname, FmgrInfo *array_in, Da Oid typid, int32 typmod, bool *ok); extern bool statatt_get_elem_type(Oid atttypid, char atttyptype, Oid *elemtypid, Oid *elem_eq_opr); +extern bool statatt_get_range_type(Oid atttypid, Oid *rangetypid); extern bool statatt_check_bounds_histogram(Datum arrayval); diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c index c35892ce6d0b..d5596d50f802 100644 --- a/src/backend/statistics/attribute_stats.c +++ b/src/backend/statistics/attribute_stats.c @@ -229,6 +229,8 @@ attribute_statistics_update_internal(Oid reloid, Oid elemtypid = InvalidOid; Oid elem_eq_opr = InvalidOid; + Oid bounds_typid = InvalidOid; + FmgrInfo array_in_fn; bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) && @@ -334,7 +336,7 @@ attribute_statistics_update_internal(Oid reloid, /* only range types can have range stats */ if ((do_range_length_histogram || do_bounds_histogram) && - !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE)) + !statatt_get_range_type(atttypid, &bounds_typid)) { ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -498,14 +500,8 @@ attribute_statistics_update_internal(Oid reloid, { bool converted = false; Datum stavalues; - Oid bounds_typid = atttypid; - /* - * If it's a multirange, step down to the range type, as is done by - * multirange_typanalyze(). - */ - if (type_is_multirange(atttypid)) - bounds_typid = get_multirange_range(atttypid); + Assert(OidIsValid(bounds_typid)); stavalues = statatt_build_stavalues("range_bounds_histogram", &array_in_fn, diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c index cb965fdb8680..37013883d465 100644 --- a/src/backend/statistics/extended_stats_funcs.c +++ b/src/backend/statistics/extended_stats_funcs.c @@ -1123,6 +1123,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont, Datum pgstdat = (Datum) 0; Oid elemtypid = InvalidOid; Oid elemeqopr = InvalidOid; + Oid rtypid = InvalidOid; bool found[NUM_ATTRIBUTE_STATS_ELEMS] = {0}; JsonbValue val[NUM_ATTRIBUTE_STATS_ELEMS] = {0}; @@ -1259,8 +1260,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont, found[RANGE_EMPTY_FRAC_ELEM] || found[RANGE_BOUNDS_HISTOGRAM_ELEM]) { - if (typcache->typtype != TYPTYPE_RANGE && - typcache->typtype != TYPTYPE_MULTIRANGE) + if (!statatt_get_range_type(typid, &rtypid)) { ereport(WARNING, errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1476,14 +1476,8 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont, Datum stavalues; bool val_ok = false; char *s; - Oid rtypid = typid; - /* - * If it's a multirange, step down to the range type, as is done by - * multirange_typanalyze(). - */ - if (type_is_multirange(typid)) - rtypid = get_multirange_range(typid); + Assert(OidIsValid(rtypid)); s = jbv_string_get_cstr(&val[RANGE_BOUNDS_HISTOGRAM_ELEM]); diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c index f4ff9ab9b20c..5e0e8b9b3256 100644 --- a/src/backend/statistics/stat_utils.c +++ b/src/backend/statistics/stat_utils.c @@ -551,6 +551,36 @@ statatt_get_elem_type(Oid atttypid, char atttyptype, return true; } +/* + * Derive the range type to use from the attribute type, returning false if + * the attribute cannot have range statistics at all. + * + * The attribute type may be a domain, in which case its base type decides + * whether range statistics apply (see also range_typanalyze() and + * multirange_typanalyze()). For a multirange type, we step down to its + * range type, because compute_range_stats() stores range bounds even when + * analyzing a multirange column. + * + * The atttypid should be derived from a previous call to statatt_get_type(). + */ +bool +statatt_get_range_type(Oid atttypid, Oid *rangetypid) +{ + Oid basetypid = getBaseType(atttypid); + + if (type_is_multirange(basetypid)) + *rangetypid = get_multirange_range(basetypid); + else if (type_is_range(basetypid)) + *rangetypid = basetypid; + else + { + *rangetypid = InvalidOid; + return false; + } + + return true; +} + /* * Build an array with element type typid from a text datum, used as * value of an attribute in a tuple to-be-inserted into pg_statistic. diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out index 4ce176c26678..e1de8d00d5b4 100644 --- a/src/test/regress/expected/stats_import.out +++ b/src/test/regress/expected/stats_import.out @@ -1495,6 +1495,158 @@ SELECT pg_catalog.pg_restore_attribute_stats( t (1 row) +-- test for domains over range and multirange types +CREATE DOMAIN stats_import.dom_int4 AS int4; +CREATE DOMAIN stats_import.dom_range AS int4range; +CREATE DOMAIN stats_import.dom_mrange AS int4multirange; +CREATE TABLE stats_import.test_dom( + id stats_import.dom_int4, + drange stats_import.dom_range, + dmrange stats_import.dom_mrange +) WITH (autovacuum_enabled = false); +INSERT INTO stats_import.test_dom +VALUES (1, '[1,3)', '{[1,3),[5,9),[20,30)}'), + (2, '[5,9)', '{[11,13),[15,19),[20,30)}'), + (3, '[11,15)', '{[21,23),[25,29),[120,130)}'); +-- warn: domain a scalar type cannot have range stats +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'id', + 'inherited', false, + 'null_frac', 0.25::real, + 'range_length_histogram', '{2,4,4}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"[1,3)","[5,9)","[11,15)"}'::text +); +WARNING: column "id" is not a range type +DETAIL: Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM. + pg_restore_attribute_stats +---------------------------- + f +(1 row) + +SELECT * +FROM stats_import.pg_stats_stable +WHERE schemaname = 'stats_import' +AND tablename = 'test_dom' +AND inherited = false +AND attname = 'id'; + schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram +--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------ + stats_import | test_dom | id | f | 0.25 | 0 | 0 | | | | | | | | | | +(1 row) + +-- ok: range stats for a domain over a range type +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'drange', + 'inherited', false, + 'range_length_histogram', '{2,4,4}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"[1,3)","[5,9)","[11,15)"}'::text +); + pg_restore_attribute_stats +---------------------------- + t +(1 row) + +SELECT * +FROM stats_import.pg_stats_stable +WHERE schemaname = 'stats_import' +AND tablename = 'test_dom' +AND inherited = false +AND attname = 'drange'; + schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram +--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+----------------------------- + stats_import | test_dom | drange | f | 0 | 0 | 0 | | | | | | | | {2,4,4} | 0 | {"[1,3)","[5,9)","[11,15)"} +(1 row) + +-- ok: range stats for a domain over a multirange type. +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'dmrange', + 'inherited', false, + 'range_length_histogram', '{29,29,109}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"[1,30)","[11,30)","[21,130)"}'::text +); + pg_restore_attribute_stats +---------------------------- + t +(1 row) + +SELECT * +FROM stats_import.pg_stats_stable +WHERE schemaname = 'stats_import' +AND tablename = 'test_dom' +AND inherited = false +AND attname = 'dmrange'; + schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram +--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+--------------------------------- + stats_import | test_dom | dmrange | f | 0 | 0 | 0 | | | | | | | | {29,29,109} | 0 | {"[1,30)","[11,30)","[21,130)"} +(1 row) + +-- warn: multirange values in the bounds histogram of a domain. These +-- must be ranges. +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'dmrange', + 'inherited', false, + 'range_length_histogram', '{29,29,109}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"{[1,30)}","{[11,30)}"}'::text +); +WARNING: malformed range literal: "{[1,30)}" +DETAIL: Missing left parenthesis or bracket. + pg_restore_attribute_stats +---------------------------- + f +(1 row) + +-- +-- Check that the range stats that ANALYZE generates for domains over range +-- and multirange types can be restored exactly. +-- +ANALYZE stats_import.test_dom; +CREATE TABLE stats_import.test_dom_clone ( LIKE stats_import.test_dom ) + WITH (autovacuum_enabled = false); +SELECT 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', 'test_dom_clone', + 'attname', s.attname::text, + 'inherited', s.inherited, + '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, + '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 = 'test_dom' +ORDER BY s.attname, s.inherited; + attname | inherited | r +---------+-----------+--- + dmrange | f | t + drange | f | t + id | f | t +(3 rows) + +SELECT relname, (stats).* +FROM stats_import.pg_statistic_get_difference('test_dom', 'test_dom_clone') +\gx +(0 rows) + -- -- 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 @@ -2737,6 +2889,71 @@ range_length_histogram | {10179,10189,10199} range_empty_frac | 0 range_bounds_histogram | {"[1,10200)","[11,10200)","[21,10200)"} +-- Check import of range stats for expressions whose type is a domain over a +-- range or a multirange type. +CREATE STATISTICS stats_import.test_dom_stat + ON id, + (range_merge(drange, drange)::stats_import.dom_range), + ((dmrange + '{}'::int4multirange)::stats_import.dom_mrange) + FROM stats_import.test_dom; +-- warn: reject multirange values in the bounds histogram of a domain. These +-- must be ranges. +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_dom_stat', + 'inherited', false, + 'exprs', '[{"range_length_histogram": "{2,4,4}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"[1,3)\",\"[5,9)\",\"[11,15)\"}"}, + {"range_length_histogram": "{29,29,109}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"{[1,30)}\",\"{[11,30)}\"}"}]'::jsonb); +WARNING: malformed range literal: "{[1,30)}" +DETAIL: Missing left parenthesis or bracket. +HINT: Element "range_bounds_histogram" in expression -2 could not be parsed. + pg_restore_extended_stats +--------------------------- + f +(1 row) + +-- ok: range stats for domains over range and multirange types +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_dom_stat', + 'inherited', false, + 'exprs', '[{"range_length_histogram": "{2,4,4}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"[1,3)\",\"[5,9)\",\"[11,15)\"}"}, + {"range_length_histogram": "{29,29,109}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"[1,30)\",\"[11,30)\",\"[21,130)\"}"}]'::jsonb); + pg_restore_extended_stats +--------------------------- + t +(1 row) + +SELECT e.expr, 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_dom_stat' AND + e.inherited = false +\gx +-[ RECORD 1 ]----------+-------------------------------------------------------------------------------- +expr | (range_merge((drange)::int4range, (drange)::int4range))::stats_import.dom_range +range_length_histogram | {2,4,4} +range_empty_frac | 0 +range_bounds_histogram | {"[1,3)","[5,9)","[11,15)"} +-[ RECORD 2 ]----------+-------------------------------------------------------------------------------- +expr | (((dmrange)::int4multirange + '{}'::int4multirange))::stats_import.dom_mrange +range_length_histogram | {29,29,109} +range_empty_frac | 0 +range_bounds_histogram | {"[1,30)","[11,30)","[21,130)"} + -- Incorrect extended stats kind, exprs not supported SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', @@ -3828,7 +4045,7 @@ SELECT COUNT(*) FROM stats_import.test_range_expr_null (1 row) DROP SCHEMA stats_import CASCADE; -NOTICE: drop cascades to 19 other objects +NOTICE: drop cascades to 24 other objects DETAIL: drop cascades to view stats_import.pg_stats_stable drop cascades to view stats_import.pg_statistic_flat_t drop cascades to function stats_import.pg_statistic_flat(text) @@ -3845,6 +4062,11 @@ drop cascades to table stats_import.test_mr drop cascades to table stats_import.part_parent drop cascades to sequence stats_import.testseq drop cascades to view stats_import.testview +drop cascades to type stats_import.dom_int4 +drop cascades to type stats_import.dom_range +drop cascades to type stats_import.dom_mrange +drop cascades to table stats_import.test_dom +drop cascades to table stats_import.test_dom_clone drop cascades to table stats_import.test_clone drop cascades to table stats_import.test_mr_clone drop cascades to table stats_import.test_range_expr_null diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql index 748c9a2e0000..417bcb025339 100644 --- a/src/test/regress/sql/stats_import.sql +++ b/src/test/regress/sql/stats_import.sql @@ -1082,6 +1082,124 @@ SELECT pg_catalog.pg_restore_attribute_stats( 'range_bounds_histogram', '{"[1,30)","[11,30)","[21,130)"}'::text ); +-- test for domains over range and multirange types +CREATE DOMAIN stats_import.dom_int4 AS int4; +CREATE DOMAIN stats_import.dom_range AS int4range; +CREATE DOMAIN stats_import.dom_mrange AS int4multirange; + +CREATE TABLE stats_import.test_dom( + id stats_import.dom_int4, + drange stats_import.dom_range, + dmrange stats_import.dom_mrange +) WITH (autovacuum_enabled = false); + +INSERT INTO stats_import.test_dom +VALUES (1, '[1,3)', '{[1,3),[5,9),[20,30)}'), + (2, '[5,9)', '{[11,13),[15,19),[20,30)}'), + (3, '[11,15)', '{[21,23),[25,29),[120,130)}'); + +-- warn: domain a scalar type cannot have range stats +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'id', + 'inherited', false, + 'null_frac', 0.25::real, + 'range_length_histogram', '{2,4,4}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"[1,3)","[5,9)","[11,15)"}'::text +); + +SELECT * +FROM stats_import.pg_stats_stable +WHERE schemaname = 'stats_import' +AND tablename = 'test_dom' +AND inherited = false +AND attname = 'id'; + +-- ok: range stats for a domain over a range type +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'drange', + 'inherited', false, + 'range_length_histogram', '{2,4,4}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"[1,3)","[5,9)","[11,15)"}'::text +); + +SELECT * +FROM stats_import.pg_stats_stable +WHERE schemaname = 'stats_import' +AND tablename = 'test_dom' +AND inherited = false +AND attname = 'drange'; + +-- ok: range stats for a domain over a multirange type. +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'dmrange', + 'inherited', false, + 'range_length_histogram', '{29,29,109}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"[1,30)","[11,30)","[21,130)"}'::text +); + +SELECT * +FROM stats_import.pg_stats_stable +WHERE schemaname = 'stats_import' +AND tablename = 'test_dom' +AND inherited = false +AND attname = 'dmrange'; + +-- warn: multirange values in the bounds histogram of a domain. These +-- must be ranges. +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'attname', 'dmrange', + 'inherited', false, + 'range_length_histogram', '{29,29,109}'::text, + 'range_empty_frac', '0'::real, + 'range_bounds_histogram', '{"{[1,30)}","{[11,30)}"}'::text +); + +-- +-- Check that the range stats that ANALYZE generates for domains over range +-- and multirange types can be restored exactly. +-- +ANALYZE stats_import.test_dom; + +CREATE TABLE stats_import.test_dom_clone ( LIKE stats_import.test_dom ) + WITH (autovacuum_enabled = false); + +SELECT 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', 'test_dom_clone', + 'attname', s.attname::text, + 'inherited', s.inherited, + '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, + '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 = 'test_dom' +ORDER BY s.attname, s.inherited; + +SELECT relname, (stats).* +FROM stats_import.pg_statistic_get_difference('test_dom', 'test_dom_clone') +\gx + -- -- 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 @@ -1934,6 +2052,51 @@ WHERE e.statistics_schemaname = 'stats_import' AND e.inherited = false \gx +-- Check import of range stats for expressions whose type is a domain over a +-- range or a multirange type. +CREATE STATISTICS stats_import.test_dom_stat + ON id, + (range_merge(drange, drange)::stats_import.dom_range), + ((dmrange + '{}'::int4multirange)::stats_import.dom_mrange) + FROM stats_import.test_dom; + +-- warn: reject multirange values in the bounds histogram of a domain. These +-- must be ranges. +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_dom_stat', + 'inherited', false, + 'exprs', '[{"range_length_histogram": "{2,4,4}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"[1,3)\",\"[5,9)\",\"[11,15)\"}"}, + {"range_length_histogram": "{29,29,109}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"{[1,30)}\",\"{[11,30)}\"}"}]'::jsonb); + +-- ok: range stats for domains over range and multirange types +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_dom', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_dom_stat', + 'inherited', false, + 'exprs', '[{"range_length_histogram": "{2,4,4}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"[1,3)\",\"[5,9)\",\"[11,15)\"}"}, + {"range_length_histogram": "{29,29,109}", + "range_empty_frac": "0", + "range_bounds_histogram": "{\"[1,30)\",\"[11,30)\",\"[21,130)\"}"}]'::jsonb); + +SELECT e.expr, 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_dom_stat' AND + e.inherited = false +\gx + -- Incorrect extended stats kind, exprs not supported SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', -- 2.55.0