diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/catalog/system_views.sql | 2 | ||||
-rw-r--r-- | src/backend/commands/analyze.c | 28 | ||||
-rw-r--r-- | src/backend/commands/statscmds.c | 72 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 146 | ||||
-rw-r--r-- | src/backend/statistics/dependencies.c | 22 | ||||
-rw-r--r-- | src/backend/statistics/extended_stats.c | 75 | ||||
-rw-r--r-- | src/backend/statistics/mcv.c | 8 | ||||
-rw-r--r-- | src/backend/statistics/mvdistinct.c | 5 | ||||
-rw-r--r-- | src/backend/utils/adt/selfuncs.c | 37 | ||||
-rw-r--r-- | src/backend/utils/cache/syscache.c | 6 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_statistic_ext_data.h | 4 | ||||
-rw-r--r-- | src/include/commands/defrem.h | 1 | ||||
-rw-r--r-- | src/include/nodes/pathnodes.h | 1 | ||||
-rw-r--r-- | src/include/statistics/statistics.h | 11 | ||||
-rw-r--r-- | src/test/regress/expected/rules.out | 2 | ||||
-rw-r--r-- | src/test/regress/expected/stats_ext.out | 31 | ||||
-rw-r--r-- | src/test/regress/sql/stats_ext.sql | 13 |
18 files changed, 229 insertions, 237 deletions
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 701ff38f761..3cb69b1f87b 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -266,6 +266,7 @@ CREATE VIEW pg_stats_ext WITH (security_barrier) AS ) AS attnames, pg_get_statisticsobjdef_expressions(s.oid) as exprs, s.stxkind AS kinds, + sd.stxdinherit AS inherited, sd.stxdndistinct AS n_distinct, sd.stxddependencies AS dependencies, m.most_common_vals, @@ -298,6 +299,7 @@ CREATE VIEW pg_stats_ext_exprs WITH (security_barrier) AS s.stxname AS statistics_name, pg_get_userbyid(s.stxowner) AS statistics_owner, stat.expr, + sd.stxdinherit AS inherited, (stat.a).stanullfrac AS null_frac, (stat.a).stawidth AS avg_width, (stat.a).stadistinct AS n_distinct, diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index d447bc9b436..a0da998c2ea 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -549,7 +549,6 @@ do_analyze_rel(Relation onerel, VacuumParams *params, { MemoryContext col_context, old_context; - bool build_ext_stats; pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE, PROGRESS_ANALYZE_PHASE_COMPUTE_STATS); @@ -613,30 +612,9 @@ do_analyze_rel(Relation onerel, VacuumParams *params, thisdata->attr_cnt, thisdata->vacattrstats); } - /* - * Should we build extended statistics for this relation? - * - * The extended statistics catalog does not include an inheritance - * flag, so we can't store statistics built both with and without - * data from child relations. We can store just one set of statistics - * per relation. For plain relations that's fine, but for inheritance - * trees we have to pick whether to store statistics for just the - * one relation or the whole tree. For plain inheritance we store - * the (!inh) version, mostly for backwards compatibility reasons. - * For partitioned tables that's pointless (the non-leaf tables are - * always empty), so we store stats representing the whole tree. - */ - build_ext_stats = (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) ? inh : (!inh); - - /* - * Build extended statistics (if there are any). - * - * For now we only build extended statistics on individual relations, - * not for relations representing inheritance trees. - */ - if (build_ext_stats) - BuildRelationExtStatistics(onerel, totalrows, numrows, rows, - attr_cnt, vacattrstats); + /* Build extended statistics (if there are any). */ + BuildRelationExtStatistics(onerel, inh, totalrows, numrows, rows, + attr_cnt, vacattrstats); } pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE, diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index f9d3f9c7b88..f7419b8f562 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -75,13 +75,10 @@ CreateStatistics(CreateStatsStmt *stmt) HeapTuple htup; Datum values[Natts_pg_statistic_ext]; bool nulls[Natts_pg_statistic_ext]; - Datum datavalues[Natts_pg_statistic_ext_data]; - bool datanulls[Natts_pg_statistic_ext_data]; int2vector *stxkeys; List *stxexprs = NIL; Datum exprsDatum; Relation statrel; - Relation datarel; Relation rel = NULL; Oid relid; ObjectAddress parentobject, @@ -514,28 +511,10 @@ CreateStatistics(CreateStatsStmt *stmt) relation_close(statrel, RowExclusiveLock); /* - * Also build the pg_statistic_ext_data tuple, to hold the actual - * statistics data. + * We used to create the pg_statistic_ext_data tuple too, but it's not clear + * what value should the stxdinherit flag have (it depends on whether the rel + * is partitioned, contains data, etc.) */ - datarel = table_open(StatisticExtDataRelationId, RowExclusiveLock); - - memset(datavalues, 0, sizeof(datavalues)); - memset(datanulls, false, sizeof(datanulls)); - - datavalues[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(statoid); - - /* no statistics built yet */ - datanulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true; - datanulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true; - datanulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true; - datanulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true; - - /* insert it into pg_statistic_ext_data */ - htup = heap_form_tuple(datarel->rd_att, datavalues, datanulls); - CatalogTupleInsert(datarel, htup); - heap_freetuple(htup); - - relation_close(datarel, RowExclusiveLock); InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0); @@ -717,32 +696,49 @@ AlterStatistics(AlterStatsStmt *stmt) } /* - * Guts of statistics object deletion. + * Delete entry in pg_statistic_ext_data catalog. We don't know if the row + * exists, so don't error out. */ void -RemoveStatisticsById(Oid statsOid) +RemoveStatisticsDataById(Oid statsOid, bool inh) { Relation relation; HeapTuple tup; - Form_pg_statistic_ext statext; - Oid relid; - /* - * First delete the pg_statistic_ext_data tuple holding the actual - * statistical data. - */ relation = table_open(StatisticExtDataRelationId, RowExclusiveLock); - tup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid)); - - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "cache lookup failed for statistics data %u", statsOid); + tup = SearchSysCache2(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid), + BoolGetDatum(inh)); - CatalogTupleDelete(relation, &tup->t_self); + /* We don't know if the data row for inh value exists. */ + if (HeapTupleIsValid(tup)) + { + CatalogTupleDelete(relation, &tup->t_self); - ReleaseSysCache(tup); + ReleaseSysCache(tup); + } table_close(relation, RowExclusiveLock); +} + +/* + * Guts of statistics object deletion. + */ +void +RemoveStatisticsById(Oid statsOid) +{ + Relation relation; + HeapTuple tup; + Form_pg_statistic_ext statext; + Oid relid; + + /* + * First delete the pg_statistic_ext_data tuples holding the actual + * statistical data. There might be data with/without inheritance, so + * attempt deleting both. + */ + RemoveStatisticsDataById(statsOid, true); + RemoveStatisticsDataById(statsOid, false); /* * Delete the pg_statistic_ext tuple. Also send out a cache inval on the diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 535fa041ada..a5002ad8955 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -30,6 +30,7 @@ #include "catalog/pg_am.h" #include "catalog/pg_proc.h" #include "catalog/pg_statistic_ext.h" +#include "catalog/pg_statistic_ext_data.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -1277,6 +1278,87 @@ get_relation_constraints(PlannerInfo *root, } /* + * Try loading data for the statistics object. + * + * We don't know if the data (specified by statOid and inh value) exist. + * The result is stored in stainfos list. + */ +static void +get_relation_statistics_worker(List **stainfos, RelOptInfo *rel, + Oid statOid, bool inh, + Bitmapset *keys, List *exprs) +{ + Form_pg_statistic_ext_data dataForm; + HeapTuple dtup; + + dtup = SearchSysCache2(STATEXTDATASTXOID, + ObjectIdGetDatum(statOid), BoolGetDatum(inh)); + if (!HeapTupleIsValid(dtup)) + return; + + dataForm = (Form_pg_statistic_ext_data) GETSTRUCT(dtup); + + /* add one StatisticExtInfo for each kind built */ + if (statext_is_kind_built(dtup, STATS_EXT_NDISTINCT)) + { + StatisticExtInfo *info = makeNode(StatisticExtInfo); + + info->statOid = statOid; + info->inherit = dataForm->stxdinherit; + info->rel = rel; + info->kind = STATS_EXT_NDISTINCT; + info->keys = bms_copy(keys); + info->exprs = exprs; + + *stainfos = lappend(*stainfos, info); + } + + if (statext_is_kind_built(dtup, STATS_EXT_DEPENDENCIES)) + { + StatisticExtInfo *info = makeNode(StatisticExtInfo); + + info->statOid = statOid; + info->inherit = dataForm->stxdinherit; + info->rel = rel; + info->kind = STATS_EXT_DEPENDENCIES; + info->keys = bms_copy(keys); + info->exprs = exprs; + + *stainfos = lappend(*stainfos, info); + } + + if (statext_is_kind_built(dtup, STATS_EXT_MCV)) + { + StatisticExtInfo *info = makeNode(StatisticExtInfo); + + info->statOid = statOid; + info->inherit = dataForm->stxdinherit; + info->rel = rel; + info->kind = STATS_EXT_MCV; + info->keys = bms_copy(keys); + info->exprs = exprs; + + *stainfos = lappend(*stainfos, info); + } + + if (statext_is_kind_built(dtup, STATS_EXT_EXPRESSIONS)) + { + StatisticExtInfo *info = makeNode(StatisticExtInfo); + + info->statOid = statOid; + info->inherit = dataForm->stxdinherit; + info->rel = rel; + info->kind = STATS_EXT_EXPRESSIONS; + info->keys = bms_copy(keys); + info->exprs = exprs; + + *stainfos = lappend(*stainfos, info); + } + + ReleaseSysCache(dtup); +} + +/* * get_relation_statistics * Retrieve extended statistics defined on the table. * @@ -1299,7 +1381,6 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) Oid statOid = lfirst_oid(l); Form_pg_statistic_ext staForm; HeapTuple htup; - HeapTuple dtup; Bitmapset *keys = NULL; List *exprs = NIL; int i; @@ -1309,10 +1390,6 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) elog(ERROR, "cache lookup failed for statistics object %u", statOid); staForm = (Form_pg_statistic_ext) GETSTRUCT(htup); - dtup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statOid)); - if (!HeapTupleIsValid(dtup)) - elog(ERROR, "cache lookup failed for statistics object %u", statOid); - /* * First, build the array of columns covered. This is ultimately * wasted if no stats within the object have actually been built, but @@ -1324,6 +1401,11 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) /* * Preprocess expressions (if any). We read the expressions, run them * through eval_const_expressions, and fix the varnos. + * + * XXX We don't know yet if there are any data for this stats object, + * with either stxdinherit value. But it's reasonable to assume there + * is at least one of those, possibly both. So it's better to process + * keys and expressions here. */ { bool isnull; @@ -1364,61 +1446,13 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) } } - /* add one StatisticExtInfo for each kind built */ - if (statext_is_kind_built(dtup, STATS_EXT_NDISTINCT)) - { - StatisticExtInfo *info = makeNode(StatisticExtInfo); - - info->statOid = statOid; - info->rel = rel; - info->kind = STATS_EXT_NDISTINCT; - info->keys = bms_copy(keys); - info->exprs = exprs; - - stainfos = lappend(stainfos, info); - } - - if (statext_is_kind_built(dtup, STATS_EXT_DEPENDENCIES)) - { - StatisticExtInfo *info = makeNode(StatisticExtInfo); + /* extract statistics for possible values of stxdinherit flag */ - info->statOid = statOid; - info->rel = rel; - info->kind = STATS_EXT_DEPENDENCIES; - info->keys = bms_copy(keys); - info->exprs = exprs; + get_relation_statistics_worker(&stainfos, rel, statOid, true, keys, exprs); - stainfos = lappend(stainfos, info); - } - - if (statext_is_kind_built(dtup, STATS_EXT_MCV)) - { - StatisticExtInfo *info = makeNode(StatisticExtInfo); - - info->statOid = statOid; - info->rel = rel; - info->kind = STATS_EXT_MCV; - info->keys = bms_copy(keys); - info->exprs = exprs; - - stainfos = lappend(stainfos, info); - } - - if (statext_is_kind_built(dtup, STATS_EXT_EXPRESSIONS)) - { - StatisticExtInfo *info = makeNode(StatisticExtInfo); - - info->statOid = statOid; - info->rel = rel; - info->kind = STATS_EXT_EXPRESSIONS; - info->keys = bms_copy(keys); - info->exprs = exprs; - - stainfos = lappend(stainfos, info); - } + get_relation_statistics_worker(&stainfos, rel, statOid, false, keys, exprs); ReleaseSysCache(htup); - ReleaseSysCache(dtup); bms_free(keys); } diff --git a/src/backend/statistics/dependencies.c b/src/backend/statistics/dependencies.c index bbc29b67117..34326d55619 100644 --- a/src/backend/statistics/dependencies.c +++ b/src/backend/statistics/dependencies.c @@ -619,14 +619,16 @@ dependency_is_fully_matched(MVDependency *dependency, Bitmapset *attnums) * Load the functional dependencies for the indicated pg_statistic_ext tuple */ MVDependencies * -statext_dependencies_load(Oid mvoid) +statext_dependencies_load(Oid mvoid, bool inh) { MVDependencies *result; bool isnull; Datum deps; HeapTuple htup; - htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid)); + htup = SearchSysCache2(STATEXTDATASTXOID, + ObjectIdGetDatum(mvoid), + BoolGetDatum(inh)); if (!HeapTupleIsValid(htup)) elog(ERROR, "cache lookup failed for statistics object %u", mvoid); @@ -1417,16 +1419,6 @@ dependencies_clauselist_selectivity(PlannerInfo *root, Node **unique_exprs; int unique_exprs_cnt; - /* - * When dealing with regular inheritance trees, ignore extended stats - * (which were built without data from child rels, and thus do not - * represent them). For partitioned tables data there's no data in the - * non-leaf relations, so we build stats only for the inheritance tree. - * So for partitioned tables we do consider extended stats. - */ - if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE) - return 1.0; - /* check if there's any stats that might be useful for us. */ if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES)) return 1.0; @@ -1610,6 +1602,10 @@ dependencies_clauselist_selectivity(PlannerInfo *root, if (stat->kind != STATS_EXT_DEPENDENCIES) continue; + /* skip statistics with mismatching stxdinherit value */ + if (stat->inherit != rte->inh) + continue; + /* * Count matching attributes - we have to undo the attnum offsets. The * input attribute numbers are not offset (expressions are not @@ -1656,7 +1652,7 @@ dependencies_clauselist_selectivity(PlannerInfo *root, if (nmatched + nexprs < 2) continue; - deps = statext_dependencies_load(stat->statOid); + deps = statext_dependencies_load(stat->statOid, rte->inh); /* * The expressions may be represented by different attnums in the diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 5762621673b..87fe82ed114 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -25,6 +25,7 @@ #include "catalog/pg_statistic_ext.h" #include "catalog/pg_statistic_ext_data.h" #include "executor/executor.h" +#include "commands/defrem.h" #include "commands/progress.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" @@ -78,7 +79,7 @@ typedef struct StatExtEntry static List *fetch_statentries_for_relation(Relation pg_statext, Oid relid); static VacAttrStats **lookup_var_attr_stats(Relation rel, Bitmapset *attrs, List *exprs, int nvacatts, VacAttrStats **vacatts); -static void statext_store(Oid statOid, +static void statext_store(Oid statOid, bool inh, MVNDistinct *ndistinct, MVDependencies *dependencies, MCVList *mcv, Datum exprs, VacAttrStats **stats); static int statext_compute_stattarget(int stattarget, @@ -111,7 +112,7 @@ static StatsBuildData *make_build_data(Relation onerel, StatExtEntry *stat, * requested stats, and serializes them back into the catalog. */ void -BuildRelationExtStatistics(Relation onerel, double totalrows, +BuildRelationExtStatistics(Relation onerel, bool inh, double totalrows, int numrows, HeapTuple *rows, int natts, VacAttrStats **vacattrstats) { @@ -231,7 +232,8 @@ BuildRelationExtStatistics(Relation onerel, double totalrows, } /* store the statistics in the catalog */ - statext_store(stat->statOid, ndistinct, dependencies, mcv, exprstats, stats); + statext_store(stat->statOid, inh, + ndistinct, dependencies, mcv, exprstats, stats); /* for reporting progress */ pgstat_progress_update_param(PROGRESS_ANALYZE_EXT_STATS_COMPUTED, @@ -782,23 +784,27 @@ lookup_var_attr_stats(Relation rel, Bitmapset *attrs, List *exprs, * tuple. */ static void -statext_store(Oid statOid, +statext_store(Oid statOid, bool inh, MVNDistinct *ndistinct, MVDependencies *dependencies, MCVList *mcv, Datum exprs, VacAttrStats **stats) { Relation pg_stextdata; - HeapTuple stup, - oldtup; + HeapTuple stup; Datum values[Natts_pg_statistic_ext_data]; bool nulls[Natts_pg_statistic_ext_data]; - bool replaces[Natts_pg_statistic_ext_data]; pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock); memset(nulls, true, sizeof(nulls)); - memset(replaces, false, sizeof(replaces)); memset(values, 0, sizeof(values)); + /* basic info */ + values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(statOid); + nulls[Anum_pg_statistic_ext_data_stxoid - 1] = false; + + values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inh); + nulls[Anum_pg_statistic_ext_data_stxdinherit - 1] = false; + /* * Construct a new pg_statistic_ext_data tuple, replacing the calculated * stats. @@ -831,25 +837,15 @@ statext_store(Oid statOid, values[Anum_pg_statistic_ext_data_stxdexpr - 1] = exprs; } - /* always replace the value (either by bytea or NULL) */ - replaces[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true; - replaces[Anum_pg_statistic_ext_data_stxddependencies - 1] = true; - replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true; - replaces[Anum_pg_statistic_ext_data_stxdexpr - 1] = true; - - /* there should already be a pg_statistic_ext_data tuple */ - oldtup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statOid)); - if (!HeapTupleIsValid(oldtup)) - elog(ERROR, "cache lookup failed for statistics object %u", statOid); - - /* replace it */ - stup = heap_modify_tuple(oldtup, - RelationGetDescr(pg_stextdata), - values, - nulls, - replaces); - ReleaseSysCache(oldtup); - CatalogTupleUpdate(pg_stextdata, &stup->t_self, stup); + /* + * Delete the old tuple if it exists, and insert a new one. It's easier + * than trying to update or insert, based on various conditions. + */ + RemoveStatisticsDataById(statOid, inh); + + /* form and insert a new tuple */ + stup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls); + CatalogTupleInsert(pg_stextdata, stup); heap_freetuple(stup); @@ -1235,7 +1231,7 @@ stat_covers_expressions(StatisticExtInfo *stat, List *exprs, * further tiebreakers are needed. */ StatisticExtInfo * -choose_best_statistics(List *stats, char requiredkind, +choose_best_statistics(List *stats, char requiredkind, bool inh, Bitmapset **clause_attnums, List **clause_exprs, int nclauses) { @@ -1257,6 +1253,10 @@ choose_best_statistics(List *stats, char requiredkind, if (info->kind != requiredkind) continue; + /* skip statistics with mismatching inheritance flag */ + if (info->inherit != inh) + continue; + /* * Collect attributes and expressions in remaining (unestimated) * clauses fully covered by this statistic object. @@ -1697,16 +1697,6 @@ statext_mcv_clauselist_selectivity(PlannerInfo *root, List *clauses, int varReli Selectivity sel = (is_or) ? 0.0 : 1.0; RangeTblEntry *rte = planner_rt_fetch(rel->relid, root); - /* - * When dealing with regular inheritance trees, ignore extended stats - * (which were built without data from child rels, and thus do not - * represent them). For partitioned tables data there's no data in the - * non-leaf relations, so we build stats only for the inheritance tree. - * So for partitioned tables we do consider extended stats. - */ - if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE) - return sel; - /* check if there's any stats that might be useful for us. */ if (!has_stats_of_kind(rel->statlist, STATS_EXT_MCV)) return sel; @@ -1758,7 +1748,7 @@ statext_mcv_clauselist_selectivity(PlannerInfo *root, List *clauses, int varReli Bitmapset *simple_clauses; /* find the best suited statistics object for these attnums */ - stat = choose_best_statistics(rel->statlist, STATS_EXT_MCV, + stat = choose_best_statistics(rel->statlist, STATS_EXT_MCV, rte->inh, list_attnums, list_exprs, list_length(clauses)); @@ -1847,7 +1837,7 @@ statext_mcv_clauselist_selectivity(PlannerInfo *root, List *clauses, int varReli MCVList *mcv_list; /* Load the MCV list stored in the statistics object */ - mcv_list = statext_mcv_load(stat->statOid); + mcv_list = statext_mcv_load(stat->statOid, rte->inh); /* * Compute the selectivity of the ORed list of clauses covered by @@ -2408,7 +2398,7 @@ serialize_expr_stats(AnlExprData *exprdata, int nexprs) * identified by the supplied index. */ HeapTuple -statext_expressions_load(Oid stxoid, int idx) +statext_expressions_load(Oid stxoid, bool inh, int idx) { bool isnull; Datum value; @@ -2418,7 +2408,8 @@ statext_expressions_load(Oid stxoid, int idx) HeapTupleData tmptup; HeapTuple tup; - htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(stxoid)); + htup = SearchSysCache2(STATEXTDATASTXOID, + ObjectIdGetDatum(stxoid), BoolGetDatum(inh)); if (!HeapTupleIsValid(htup)) elog(ERROR, "cache lookup failed for statistics object %u", stxoid); diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c index 65fa87b1c79..bad1787cfb2 100644 --- a/src/backend/statistics/mcv.c +++ b/src/backend/statistics/mcv.c @@ -559,12 +559,13 @@ build_column_frequencies(SortItem *groups, int ngroups, * Load the MCV list for the indicated pg_statistic_ext tuple. */ MCVList * -statext_mcv_load(Oid mvoid) +statext_mcv_load(Oid mvoid, bool inh) { MCVList *result; bool isnull; Datum mcvlist; - HeapTuple htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid)); + HeapTuple htup = SearchSysCache2(STATEXTDATASTXOID, + ObjectIdGetDatum(mvoid), BoolGetDatum(inh)); if (!HeapTupleIsValid(htup)) elog(ERROR, "cache lookup failed for statistics object %u", mvoid); @@ -2038,12 +2039,13 @@ mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat, int i; MCVList *mcv; Selectivity s = 0.0; + RangeTblEntry *rte = root->simple_rte_array[rel->relid]; /* match/mismatch bitmap for each MCV item */ bool *matches = NULL; /* load the MCV list stored in the statistics object */ - mcv = statext_mcv_load(stat->statOid); + mcv = statext_mcv_load(stat->statOid, rte->inh); /* build a match bitmap for the clauses */ matches = mcv_get_match_bitmap(root, clauses, stat->keys, stat->exprs, diff --git a/src/backend/statistics/mvdistinct.c b/src/backend/statistics/mvdistinct.c index 55b831d4f50..6ade5eff78c 100644 --- a/src/backend/statistics/mvdistinct.c +++ b/src/backend/statistics/mvdistinct.c @@ -146,14 +146,15 @@ statext_ndistinct_build(double totalrows, StatsBuildData *data) * Load the ndistinct value for the indicated pg_statistic_ext tuple */ MVNDistinct * -statext_ndistinct_load(Oid mvoid) +statext_ndistinct_load(Oid mvoid, bool inh) { MVNDistinct *result; bool isnull; Datum ndist; HeapTuple htup; - htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid)); + htup = SearchSysCache2(STATEXTDATASTXOID, + ObjectIdGetDatum(mvoid), BoolGetDatum(inh)); if (!HeapTupleIsValid(htup)) elog(ERROR, "cache lookup failed for statistics object %u", mvoid); diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 5d56748f0a7..1fbb0b28c3b 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -3919,17 +3919,6 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel, if (!rel->statlist) return false; - /* - * When dealing with regular inheritance trees, ignore extended stats - * (which were built without data from child rels, and thus do not - * represent them). For partitioned tables data there's no data in the - * non-leaf relations, so we build stats only for the inheritance tree. - * So for partitioned tables we do consider extended stats. - */ - rte = planner_rt_fetch(rel->relid, root); - if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE) - return false; - /* look for the ndistinct statistics object matching the most vars */ nmatches_vars = 0; /* we require at least two matches */ nmatches_exprs = 0; @@ -4015,7 +4004,8 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel, Assert(nmatches_vars + nmatches_exprs > 1); - stats = statext_ndistinct_load(statOid); + rte = planner_rt_fetch(rel->relid, root); + stats = statext_ndistinct_load(statOid, rte->inh); /* * If we have a match, search it for the specific item that matches (there @@ -5245,17 +5235,6 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, if (vardata->statsTuple) break; - /* - * When dealing with regular inheritance trees, ignore extended - * stats (which were built without data from child rels, and thus - * do not represent them). For partitioned tables data there's no - * data in the non-leaf relations, so we build stats only for the - * inheritance tree. So for partitioned tables we do consider - * extended stats. - */ - if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE) - break; - /* skip stats without per-expression stats */ if (info->kind != STATS_EXT_EXPRESSIONS) continue; @@ -5274,22 +5253,16 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, /* found a match, see if we can extract pg_statistic row */ if (equal(node, expr)) { - HeapTuple t = statext_expressions_load(info->statOid, pos); - - /* Get statistics object's table for permission check */ - RangeTblEntry *rte; Oid userid; - vardata->statsTuple = t; - /* * XXX Not sure if we should cache the tuple somewhere. * Now we just create a new copy every time. */ - vardata->freefunc = ReleaseDummy; + vardata->statsTuple = + statext_expressions_load(info->statOid, rte->inh, pos); - rte = planner_rt_fetch(onerel->relid, root); - Assert(rte->rtekind == RTE_RELATION); + vardata->freefunc = ReleaseDummy; /* * Use checkAsUser if it's set, in case we're accessing diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index beeebfe59f1..f4e7819f1e2 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -763,11 +763,11 @@ static const struct cachedesc cacheinfo[] = { 32 }, {StatisticExtDataRelationId, /* STATEXTDATASTXOID */ - StatisticExtDataStxoidIndexId, - 1, + StatisticExtDataStxoidInhIndexId, + 2, { Anum_pg_statistic_ext_data_stxoid, - 0, + Anum_pg_statistic_ext_data_stxdinherit, 0, 0 }, diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index fc904bcb9be..8324d957633 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202201121 +#define CATALOG_VERSION_NO 202201161 #endif diff --git a/src/include/catalog/pg_statistic_ext_data.h b/src/include/catalog/pg_statistic_ext_data.h index 7c94d7b7cd7..b01e6205974 100644 --- a/src/include/catalog/pg_statistic_ext_data.h +++ b/src/include/catalog/pg_statistic_ext_data.h @@ -32,6 +32,7 @@ CATALOG(pg_statistic_ext_data,3429,StatisticExtDataRelationId) { Oid stxoid BKI_LOOKUP(pg_statistic_ext); /* statistics object * this data is for */ + bool stxdinherit; /* true if inheritance children are included */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ @@ -53,6 +54,7 @@ typedef FormData_pg_statistic_ext_data * Form_pg_statistic_ext_data; DECLARE_TOAST(pg_statistic_ext_data, 3430, 3431); -DECLARE_UNIQUE_INDEX_PKEY(pg_statistic_ext_data_stxoid_index, 3433, StatisticExtDataStxoidIndexId, on pg_statistic_ext_data using btree(stxoid oid_ops)); +DECLARE_UNIQUE_INDEX_PKEY(pg_statistic_ext_data_stxoid_inh_index, 3433, StatisticExtDataStxoidInhIndexId, on pg_statistic_ext_data using btree(stxoid oid_ops, stxdinherit bool_ops)); + #endif /* PG_STATISTIC_EXT_DATA_H */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 2d3cdddaf48..56d2bb66161 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -83,6 +83,7 @@ extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt); extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt); extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt); extern void RemoveStatisticsById(Oid statsOid); +extern void RemoveStatisticsDataById(Oid statsOid, bool inh); extern Oid StatisticsGetRelation(Oid statId, bool missing_ok); /* commands/aggregatecmds.c */ diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1f33fe13c11..1f3845b3fec 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -934,6 +934,7 @@ typedef struct StatisticExtInfo NodeTag type; Oid statOid; /* OID of the statistics row */ + bool inherit; /* includes child relations */ RelOptInfo *rel; /* back-link to statistic's table */ char kind; /* statistics kind of this entry */ Bitmapset *keys; /* attnums of the columns covered */ diff --git a/src/include/statistics/statistics.h b/src/include/statistics/statistics.h index 4a3676278de..bb7ef1240e0 100644 --- a/src/include/statistics/statistics.h +++ b/src/include/statistics/statistics.h @@ -94,11 +94,11 @@ typedef struct MCVList MCVItem items[FLEXIBLE_ARRAY_MEMBER]; /* array of MCV items */ } MCVList; -extern MVNDistinct *statext_ndistinct_load(Oid mvoid); -extern MVDependencies *statext_dependencies_load(Oid mvoid); -extern MCVList *statext_mcv_load(Oid mvoid); +extern MVNDistinct *statext_ndistinct_load(Oid mvoid, bool inh); +extern MVDependencies *statext_dependencies_load(Oid mvoid, bool inh); +extern MCVList *statext_mcv_load(Oid mvoid, bool inh); -extern void BuildRelationExtStatistics(Relation onerel, double totalrows, +extern void BuildRelationExtStatistics(Relation onerel, bool inh, double totalrows, int numrows, HeapTuple *rows, int natts, VacAttrStats **vacattrstats); extern int ComputeExtStatisticsRows(Relation onerel, @@ -121,9 +121,10 @@ extern Selectivity statext_clauselist_selectivity(PlannerInfo *root, bool is_or); extern bool has_stats_of_kind(List *stats, char requiredkind); extern StatisticExtInfo *choose_best_statistics(List *stats, char requiredkind, + bool inh, Bitmapset **clause_attnums, List **clause_exprs, int nclauses); -extern HeapTuple statext_expressions_load(Oid stxoid, int idx); +extern HeapTuple statext_expressions_load(Oid stxoid, bool inh, int idx); #endif /* STATISTICS_H */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index b58b062b10d..d652f7b5fb4 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2443,6 +2443,7 @@ pg_stats_ext| SELECT cn.nspname AS schemaname, JOIN pg_attribute a ON (((a.attrelid = s.stxrelid) AND (a.attnum = k.k))))) AS attnames, pg_get_statisticsobjdef_expressions(s.oid) AS exprs, s.stxkind AS kinds, + sd.stxdinherit AS inherited, sd.stxdndistinct AS n_distinct, sd.stxddependencies AS dependencies, m.most_common_vals, @@ -2469,6 +2470,7 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname, s.stxname AS statistics_name, pg_get_userbyid(s.stxowner) AS statistics_owner, stat.expr, + sd.stxdinherit AS inherited, (stat.a).stanullfrac AS null_frac, (stat.a).stawidth AS avg_width, (stat.a).stadistinct AS n_distinct, diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out index fd0cc54ea11..042316aeed8 100644 --- a/src/test/regress/expected/stats_ext.out +++ b/src/test/regress/expected/stats_ext.out @@ -140,13 +140,12 @@ Statistics objects: "public.ab1_a_b_stats" ON a, b FROM ab1; STATISTICS 0 ANALYZE ab1; -SELECT stxname, stxdndistinct, stxddependencies, stxdmcv - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxname = 'ab1_a_b_stats' - AND d.stxoid = s.oid; - stxname | stxdndistinct | stxddependencies | stxdmcv ----------------+---------------+------------------+--------- - ab1_a_b_stats | | | +SELECT stxname, stxdndistinct, stxddependencies, stxdmcv, stxdinherit + FROM pg_statistic_ext s LEFT JOIN pg_statistic_ext_data d ON (d.stxoid = s.oid) + WHERE s.stxname = 'ab1_a_b_stats'; + stxname | stxdndistinct | stxddependencies | stxdmcv | stxdinherit +---------------+---------------+------------------+---------+------------- + ab1_a_b_stats | | | | (1 row) ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1; @@ -200,12 +199,11 @@ SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b CREATE STATISTICS stxdinh ON a, b FROM stxdinh; VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2; --- Since the stats object does not include inherited stats, it should not --- affect the estimates +-- See if the extended stats affect the estimates SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); estimated | actual -----------+-------- - 400 | 150 + 150 | 150 (1 row) -- Dependencies are applied at individual relations (within append), so @@ -216,6 +214,19 @@ SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b 22 | 40 (1 row) +-- Ensure correct (non-inherited) stats are applied to inherited query +SELECT * FROM check_estimated_rows('SELECT a, b FROM ONLY stxdinh GROUP BY 1, 2'); + estimated | actual +-----------+-------- + 100 | 100 +(1 row) + +SELECT * FROM check_estimated_rows('SELECT a, b FROM ONLY stxdinh WHERE a = 0 AND b = 0'); + estimated | actual +-----------+-------- + 20 | 20 +(1 row) + DROP TABLE stxdinh, stxdinh1, stxdinh2; -- Ensure inherited stats ARE applied to inherited query in partitioned table CREATE TABLE stxdinp(i int, a int, b int) PARTITION BY RANGE (i); diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql index d2c59b0a8ad..6b954c9e500 100644 --- a/src/test/regress/sql/stats_ext.sql +++ b/src/test/regress/sql/stats_ext.sql @@ -91,10 +91,9 @@ ALTER TABLE ab1 ALTER a SET STATISTICS -1; ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0; \d ab1 ANALYZE ab1; -SELECT stxname, stxdndistinct, stxddependencies, stxdmcv - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxname = 'ab1_a_b_stats' - AND d.stxoid = s.oid; +SELECT stxname, stxdndistinct, stxddependencies, stxdmcv, stxdinherit + FROM pg_statistic_ext s LEFT JOIN pg_statistic_ext_data d ON (d.stxoid = s.oid) + WHERE s.stxname = 'ab1_a_b_stats'; ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1; \d+ ab1 -- partial analyze doesn't build stats either @@ -126,12 +125,14 @@ SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0'); CREATE STATISTICS stxdinh ON a, b FROM stxdinh; VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2; --- Since the stats object does not include inherited stats, it should not --- affect the estimates +-- See if the extended stats affect the estimates SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); -- Dependencies are applied at individual relations (within append), so -- this estimate changes a bit because we improve estimates for the parent SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0'); +-- Ensure correct (non-inherited) stats are applied to inherited query +SELECT * FROM check_estimated_rows('SELECT a, b FROM ONLY stxdinh GROUP BY 1, 2'); +SELECT * FROM check_estimated_rows('SELECT a, b FROM ONLY stxdinh WHERE a = 0 AND b = 0'); DROP TABLE stxdinh, stxdinh1, stxdinh2; -- Ensure inherited stats ARE applied to inherited query in partitioned table |