diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c index 18814093f50..80247cbac21 100644 --- a/src/backend/statistics/extended_stats_funcs.c +++ b/src/backend/statistics/extended_stats_funcs.c @@ -24,6 +24,8 @@ #include "catalog/pg_statistic_ext_data.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/optimizer.h" #include "statistics/extended_stats_internal.h" #include "statistics/stat_utils.h" #include "utils/acl.h" @@ -248,6 +250,8 @@ extended_statistics_update(FunctionCallInfo fcinfo) bool nulls[Natts_pg_statistic_ext_data] = {0}; bool replaces[Natts_pg_statistic_ext_data] = {0}; bool success = true; + Datum exprdatum; + bool isnull; int numexprs = 0; /* arrays of type info, if we need them */ @@ -341,6 +345,38 @@ extended_statistics_update(FunctionCallInfo fcinfo) /* Find out what extended statistics kinds we should expect. */ expand_stxkind(tup, &enabled); + /* decode expression (if any) */ + exprdatum = SysCacheGetAttr(STATEXTOID, + tup, + Anum_pg_statistic_ext_stxexprs, + &isnull); + if (!isnull) + { + char *s; + List *exprs; + + s = TextDatumGetCString(exprdatum); + exprs = (List *) stringToNode(s); + pfree(s); + + /* + * Run the expressions through eval_const_expressions(). This is not + * just an optimization, but is necessary, because the planner will be + * comparing them to similarly-processed qual clauses, and may fail to + * detect valid matches without this. + * + * We must not use canonicalize_qual(), however, since these are not + * qual expressions. + */ + exprs = (List *) eval_const_expressions(NULL, (Node *) exprs); + + /* May as well fix opfuncids too */ + fix_opfuncids((Node *) exprs); + + /* Compute the number of expression, for input validation. */ + numexprs = list_length(exprs); + } + /* * If the object cannot support ndistinct, we should not have data for it. */ @@ -422,7 +458,8 @@ extended_statistics_update(FunctionCallInfo fcinfo) bytea *data = DatumGetByteaPP(dependencies_datum); MVDependencies *dependencies = statext_dependencies_deserialize(data); - if (statext_dependencies_validate(dependencies, &stxform->stxkeys, numexprs, WARNING)) + if (statext_dependencies_validate(dependencies, &stxform->stxkeys, + numexprs, WARNING)) { values[Anum_pg_statistic_ext_data_stxddependencies - 1] = dependencies_datum; nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = false; diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out index 3c9e7e0d369..0709f19a5e4 100644 --- a/src/test/regress/expected/stats_import.out +++ b/src/test/regress/expected/stats_import.out @@ -1147,6 +1147,12 @@ CREATE STATISTICS stats_import.test_stat_ndistinct (ndistinct) CREATE STATISTICS stats_import.test_stat_dependencies (dependencies) 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; -- Generate statistics on table with data ANALYZE stats_import.test; CREATE TABLE stats_import.test_clone ( LIKE stats_import.test ) @@ -1814,6 +1820,62 @@ SELECT pg_catalog.pg_restore_extended_stats( t (1 row) +-- 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); +WARNING: could not validate "pg_ndistinct" object: invalid attribute number 1 found + pg_restore_extended_stats +--------------------------- + f +(1 row) + +-- 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); + pg_restore_extended_stats +--------------------------- + t +(1 row) + +-- 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); +WARNING: could not validate "pg_dependencies" object: invalid attribute number 1 found + pg_restore_extended_stats +--------------------------- + f +(1 row) + +-- 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); + pg_restore_extended_stats +--------------------------- + t +(1 row) + -- 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 @@ -1836,6 +1898,27 @@ WHERE e.statistics_schemaname = 'stats_import' AND {"attributes": [3], "dependency": 2, "degree": 1.000000}] (1 row) +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; + n_distinct +-------------------------------------------- + [{"attributes": [-1, -2], "ndistinct": 4}] +(1 row) + +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; + dependencies +-------------------------------------------------------------- + [{"attributes": [-1], "dependency": -2, "degree": 1.000000},+ + {"attributes": [-2], "dependency": -1, "degree": 1.000000}] +(1 row) + DROP SCHEMA stats_import CASCADE; NOTICE: drop cascades to 7 other objects DETAIL: drop cascades to type stats_import.complex_type diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql index 1161fcffe81..f08743dd002 100644 --- a/src/test/regress/sql/stats_import.sql +++ b/src/test/regress/sql/stats_import.sql @@ -819,6 +819,14 @@ CREATE STATISTICS stats_import.test_stat_dependencies (dependencies) 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; + -- Generate statistics on table with data ANALYZE stats_import.test; @@ -1297,6 +1305,44 @@ SELECT pg_catalog.pg_restore_extended_stats( '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); + -- 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 @@ -1310,4 +1356,16 @@ 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; + DROP SCHEMA stats_import CASCADE;