Add stxdinherit flag to pg_statistic_ext_data

Add pg_statistic_ext_data.stxdinherit flag, so that for each extended
statistics definition we can store two versions of data - one for the
relation alone, one for the whole inheritance tree. This is analogous to
pg_statistic.stainherit, but we failed to include such flag in catalogs
for extended statistics, and we had to work around it (see commits
859b3003de, 36c4bc6e72 and 20b9fa308e).

This changes the relationship between the two catalogs storing extended
statistics objects (pg_statistic_ext and pg_statistic_ext_data). Until
now, there was a simple 1:1 mapping - for each definition there was one
pg_statistic_ext_data row, and this row was inserted while creating the
statistics (and then updated during ANALYZE). With the stxdinherit flag,
we don't know how many rows there will be (child relations may be added
after the statistics object is defined), so there may be up to two rows.

We could make CREATE STATISTICS to always create both rows, but that
seems wasteful - without partitioning we only need stxdinherit=false
rows, and declaratively partitioned tables need only stxdinherit=true.
So we no longer initialize pg_statistic_ext_data in CREATE STATISTICS,
and instead make that a responsibility of ANALYZE. Which is what we do
for regular statistics too.

Patch by me, with extensive improvements and fixes by Justin Pryzby.

Author: Tomas Vondra, Justin Pryzby
Reviewed-by: Tomas Vondra, Justin Pryzby
Discussion: https://postgr.es/m/20210923212624.GI831%40telsasoft.com
pull/73/head
Tomas Vondra 4 years ago
parent e701bdd2f0
commit 269b532aef
  1. 23
      doc/src/sgml/catalogs.sgml
  2. 2
      src/backend/catalog/system_views.sql
  3. 26
      src/backend/commands/analyze.c
  4. 68
      src/backend/commands/statscmds.c
  5. 146
      src/backend/optimizer/util/plancat.c
  6. 22
      src/backend/statistics/dependencies.c
  7. 73
      src/backend/statistics/extended_stats.c
  8. 8
      src/backend/statistics/mcv.c
  9. 5
      src/backend/statistics/mvdistinct.c
  10. 37
      src/backend/utils/adt/selfuncs.c
  11. 6
      src/backend/utils/cache/syscache.c
  12. 2
      src/include/catalog/catversion.h
  13. 4
      src/include/catalog/pg_statistic_ext_data.h
  14. 1
      src/include/commands/defrem.h
  15. 1
      src/include/nodes/pathnodes.h
  16. 11
      src/include/statistics/statistics.h
  17. 2
      src/test/regress/expected/rules.out
  18. 31
      src/test/regress/expected/stats_ext.out
  19. 13
      src/test/regress/sql/stats_ext.sql

@ -7521,6 +7521,19 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
created with <link linkend="sql-createstatistics"><command>CREATE STATISTICS</command></link>. created with <link linkend="sql-createstatistics"><command>CREATE STATISTICS</command></link>.
</para> </para>
<para>
Normally there is one entry, with <structfield>stxdinherit</structfield> =
<literal>false</literal>, for each statistics object that has been analyzed.
If the table has inheritance children, a second entry with
<structfield>stxdinherit</structfield> = <literal>true</literal> is also created.
This row represents the statistics object over the inheritance tree, i.e.,
statistics for the data you'd see with
<literal>SELECT * FROM <replaceable>table</replaceable>*</literal>,
whereas the <structfield>stxdinherit</structfield> = <literal>false</literal> row
represents the results of
<literal>SELECT * FROM ONLY <replaceable>table</replaceable></literal>.
</para>
<para> <para>
Like <link linkend="catalog-pg-statistic"><structname>pg_statistic</structname></link>, Like <link linkend="catalog-pg-statistic"><structname>pg_statistic</structname></link>,
<structname>pg_statistic_ext_data</structname> should not be <structname>pg_statistic_ext_data</structname> should not be
@ -7560,6 +7573,16 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
</para></entry> </para></entry>
</row> </row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>stxdinherit</structfield> <type>bool</type>
</para>
<para>
If true, the stats include inheritance child columns, not just the
values in the specified relation
</para></entry>
</row>
<row> <row>
<entry role="catalog_table_entry"><para role="column_definition"> <entry role="catalog_table_entry"><para role="column_definition">
<structfield>stxdndistinct</structfield> <type>pg_ndistinct</type> <structfield>stxdndistinct</structfield> <type>pg_ndistinct</type>

@ -266,6 +266,7 @@ CREATE VIEW pg_stats_ext WITH (security_barrier) AS
) AS attnames, ) AS attnames,
pg_get_statisticsobjdef_expressions(s.oid) as exprs, pg_get_statisticsobjdef_expressions(s.oid) as exprs,
s.stxkind AS kinds, s.stxkind AS kinds,
sd.stxdinherit AS inherited,
sd.stxdndistinct AS n_distinct, sd.stxdndistinct AS n_distinct,
sd.stxddependencies AS dependencies, sd.stxddependencies AS dependencies,
m.most_common_vals, m.most_common_vals,
@ -298,6 +299,7 @@ CREATE VIEW pg_stats_ext_exprs WITH (security_barrier) AS
s.stxname AS statistics_name, s.stxname AS statistics_name,
pg_get_userbyid(s.stxowner) AS statistics_owner, pg_get_userbyid(s.stxowner) AS statistics_owner,
stat.expr, stat.expr,
sd.stxdinherit AS inherited,
(stat.a).stanullfrac AS null_frac, (stat.a).stanullfrac AS null_frac,
(stat.a).stawidth AS avg_width, (stat.a).stawidth AS avg_width,
(stat.a).stadistinct AS n_distinct, (stat.a).stadistinct AS n_distinct,

@ -549,7 +549,6 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
{ {
MemoryContext col_context, MemoryContext col_context,
old_context; old_context;
bool build_ext_stats;
pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE, pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
PROGRESS_ANALYZE_PHASE_COMPUTE_STATS); PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);
@ -613,29 +612,8 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
thisdata->attr_cnt, thisdata->vacattrstats); thisdata->attr_cnt, thisdata->vacattrstats);
} }
/* /* Build extended statistics (if there are any). */
* Should we build extended statistics for this relation? BuildRelationExtStatistics(onerel, inh, totalrows, numrows, rows,
*
* 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); attr_cnt, vacattrstats);
} }

@ -75,13 +75,10 @@ CreateStatistics(CreateStatsStmt *stmt)
HeapTuple htup; HeapTuple htup;
Datum values[Natts_pg_statistic_ext]; Datum values[Natts_pg_statistic_ext];
bool nulls[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; int2vector *stxkeys;
List *stxexprs = NIL; List *stxexprs = NIL;
Datum exprsDatum; Datum exprsDatum;
Relation statrel; Relation statrel;
Relation datarel;
Relation rel = NULL; Relation rel = NULL;
Oid relid; Oid relid;
ObjectAddress parentobject, ObjectAddress parentobject,
@ -514,28 +511,10 @@ CreateStatistics(CreateStatsStmt *stmt)
relation_close(statrel, RowExclusiveLock); relation_close(statrel, RowExclusiveLock);
/* /*
* Also build the pg_statistic_ext_data tuple, to hold the actual * We used to create the pg_statistic_ext_data tuple too, but it's not clear
* statistics data. * 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); 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 void
RemoveStatisticsById(Oid statsOid) RemoveStatisticsDataById(Oid statsOid, bool inh)
{ {
Relation relation; Relation relation;
HeapTuple tup; 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); relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
tup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid)); tup = SearchSysCache2(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid),
BoolGetDatum(inh));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for statistics data %u", statsOid);
/* We don't know if the data row for inh value exists. */
if (HeapTupleIsValid(tup))
{
CatalogTupleDelete(relation, &tup->t_self); CatalogTupleDelete(relation, &tup->t_self);
ReleaseSysCache(tup); ReleaseSysCache(tup);
}
table_close(relation, RowExclusiveLock); 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 * Delete the pg_statistic_ext tuple. Also send out a cache inval on the

@ -30,6 +30,7 @@
#include "catalog/pg_am.h" #include "catalog/pg_am.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h" #include "catalog/pg_statistic_ext.h"
#include "catalog/pg_statistic_ext_data.h"
#include "foreign/fdwapi.h" #include "foreign/fdwapi.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
@ -1276,6 +1277,87 @@ get_relation_constraints(PlannerInfo *root,
return result; return result;
} }
/*
* 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 * get_relation_statistics
* Retrieve extended statistics defined on the table. * Retrieve extended statistics defined on the table.
@ -1299,7 +1381,6 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
Oid statOid = lfirst_oid(l); Oid statOid = lfirst_oid(l);
Form_pg_statistic_ext staForm; Form_pg_statistic_ext staForm;
HeapTuple htup; HeapTuple htup;
HeapTuple dtup;
Bitmapset *keys = NULL; Bitmapset *keys = NULL;
List *exprs = NIL; List *exprs = NIL;
int i; int i;
@ -1309,10 +1390,6 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
elog(ERROR, "cache lookup failed for statistics object %u", statOid); elog(ERROR, "cache lookup failed for statistics object %u", statOid);
staForm = (Form_pg_statistic_ext) GETSTRUCT(htup); 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 * First, build the array of columns covered. This is ultimately
* wasted if no stats within the object have actually been built, but * 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 * Preprocess expressions (if any). We read the expressions, run them
* through eval_const_expressions, and fix the varnos. * 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; bool isnull;
@ -1364,61 +1446,13 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
} }
} }
/* add one StatisticExtInfo for each kind built */ /* extract statistics for possible values of stxdinherit flag */
if (statext_is_kind_built(dtup, STATS_EXT_NDISTINCT))
{
StatisticExtInfo *info = makeNode(StatisticExtInfo);
info->statOid = statOid; get_relation_statistics_worker(&stainfos, rel, statOid, true, keys, exprs);
info->rel = rel;
info->kind = STATS_EXT_NDISTINCT;
info->keys = bms_copy(keys);
info->exprs = exprs;
stainfos = lappend(stainfos, info); get_relation_statistics_worker(&stainfos, rel, statOid, false, keys, exprs);
}
if (statext_is_kind_built(dtup, STATS_EXT_DEPENDENCIES))
{
StatisticExtInfo *info = makeNode(StatisticExtInfo);
info->statOid = statOid;
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->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);
}
ReleaseSysCache(htup); ReleaseSysCache(htup);
ReleaseSysCache(dtup);
bms_free(keys); bms_free(keys);
} }

@ -619,14 +619,16 @@ dependency_is_fully_matched(MVDependency *dependency, Bitmapset *attnums)
* Load the functional dependencies for the indicated pg_statistic_ext tuple * Load the functional dependencies for the indicated pg_statistic_ext tuple
*/ */
MVDependencies * MVDependencies *
statext_dependencies_load(Oid mvoid) statext_dependencies_load(Oid mvoid, bool inh)
{ {
MVDependencies *result; MVDependencies *result;
bool isnull; bool isnull;
Datum deps; Datum deps;
HeapTuple htup; HeapTuple htup;
htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid)); htup = SearchSysCache2(STATEXTDATASTXOID,
ObjectIdGetDatum(mvoid),
BoolGetDatum(inh));
if (!HeapTupleIsValid(htup)) if (!HeapTupleIsValid(htup))
elog(ERROR, "cache lookup failed for statistics object %u", mvoid); elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
@ -1417,16 +1419,6 @@ dependencies_clauselist_selectivity(PlannerInfo *root,
Node **unique_exprs; Node **unique_exprs;
int unique_exprs_cnt; 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. */ /* check if there's any stats that might be useful for us. */
if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES)) if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES))
return 1.0; return 1.0;
@ -1610,6 +1602,10 @@ dependencies_clauselist_selectivity(PlannerInfo *root,
if (stat->kind != STATS_EXT_DEPENDENCIES) if (stat->kind != STATS_EXT_DEPENDENCIES)
continue; continue;
/* skip statistics with mismatching stxdinherit value */
if (stat->inherit != rte->inh)
continue;
/* /*
* Count matching attributes - we have to undo the attnum offsets. The * Count matching attributes - we have to undo the attnum offsets. The
* input attribute numbers are not offset (expressions are not * input attribute numbers are not offset (expressions are not
@ -1656,7 +1652,7 @@ dependencies_clauselist_selectivity(PlannerInfo *root,
if (nmatched + nexprs < 2) if (nmatched + nexprs < 2)
continue; 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 * The expressions may be represented by different attnums in the

@ -25,6 +25,7 @@
#include "catalog/pg_statistic_ext.h" #include "catalog/pg_statistic_ext.h"
#include "catalog/pg_statistic_ext_data.h" #include "catalog/pg_statistic_ext_data.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "commands/defrem.h"
#include "commands/progress.h" #include "commands/progress.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
@ -78,7 +79,7 @@ typedef struct StatExtEntry
static List *fetch_statentries_for_relation(Relation pg_statext, Oid relid); static List *fetch_statentries_for_relation(Relation pg_statext, Oid relid);
static VacAttrStats **lookup_var_attr_stats(Relation rel, Bitmapset *attrs, List *exprs, static VacAttrStats **lookup_var_attr_stats(Relation rel, Bitmapset *attrs, List *exprs,
int nvacatts, VacAttrStats **vacatts); int nvacatts, VacAttrStats **vacatts);
static void statext_store(Oid statOid, static void statext_store(Oid statOid, bool inh,
MVNDistinct *ndistinct, MVDependencies *dependencies, MVNDistinct *ndistinct, MVDependencies *dependencies,
MCVList *mcv, Datum exprs, VacAttrStats **stats); MCVList *mcv, Datum exprs, VacAttrStats **stats);
static int statext_compute_stattarget(int stattarget, 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. * requested stats, and serializes them back into the catalog.
*/ */
void void
BuildRelationExtStatistics(Relation onerel, double totalrows, BuildRelationExtStatistics(Relation onerel, bool inh, double totalrows,
int numrows, HeapTuple *rows, int numrows, HeapTuple *rows,
int natts, VacAttrStats **vacattrstats) int natts, VacAttrStats **vacattrstats)
{ {
@ -231,7 +232,8 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
} }
/* store the statistics in the catalog */ /* 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 */ /* for reporting progress */
pgstat_progress_update_param(PROGRESS_ANALYZE_EXT_STATS_COMPUTED, pgstat_progress_update_param(PROGRESS_ANALYZE_EXT_STATS_COMPUTED,
@ -782,23 +784,27 @@ lookup_var_attr_stats(Relation rel, Bitmapset *attrs, List *exprs,
* tuple. * tuple.
*/ */
static void static void
statext_store(Oid statOid, statext_store(Oid statOid, bool inh,
MVNDistinct *ndistinct, MVDependencies *dependencies, MVNDistinct *ndistinct, MVDependencies *dependencies,
MCVList *mcv, Datum exprs, VacAttrStats **stats) MCVList *mcv, Datum exprs, VacAttrStats **stats)
{ {
Relation pg_stextdata; Relation pg_stextdata;
HeapTuple stup, HeapTuple stup;
oldtup;
Datum values[Natts_pg_statistic_ext_data]; Datum values[Natts_pg_statistic_ext_data];
bool nulls[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); pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
memset(nulls, true, sizeof(nulls)); memset(nulls, true, sizeof(nulls));
memset(replaces, false, sizeof(replaces));
memset(values, 0, sizeof(values)); 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 * Construct a new pg_statistic_ext_data tuple, replacing the calculated
* stats. * stats.
@ -831,25 +837,15 @@ statext_store(Oid statOid,
values[Anum_pg_statistic_ext_data_stxdexpr - 1] = exprs; 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; * Delete the old tuple if it exists, and insert a new one. It's easier
replaces[Anum_pg_statistic_ext_data_stxddependencies - 1] = true; * than trying to update or insert, based on various conditions.
replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true; */
replaces[Anum_pg_statistic_ext_data_stxdexpr - 1] = true; RemoveStatisticsDataById(statOid, inh);
/* 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 */ /* form and insert a new tuple */
stup = heap_modify_tuple(oldtup, stup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls);
RelationGetDescr(pg_stextdata), CatalogTupleInsert(pg_stextdata, stup);
values,
nulls,
replaces);
ReleaseSysCache(oldtup);
CatalogTupleUpdate(pg_stextdata, &stup->t_self, stup);
heap_freetuple(stup); heap_freetuple(stup);
@ -1235,7 +1231,7 @@ stat_covers_expressions(StatisticExtInfo *stat, List *exprs,
* further tiebreakers are needed. * further tiebreakers are needed.
*/ */
StatisticExtInfo * StatisticExtInfo *
choose_best_statistics(List *stats, char requiredkind, choose_best_statistics(List *stats, char requiredkind, bool inh,
Bitmapset **clause_attnums, List **clause_exprs, Bitmapset **clause_attnums, List **clause_exprs,
int nclauses) int nclauses)
{ {
@ -1257,6 +1253,10 @@ choose_best_statistics(List *stats, char requiredkind,
if (info->kind != requiredkind) if (info->kind != requiredkind)
continue; continue;
/* skip statistics with mismatching inheritance flag */
if (info->inherit != inh)
continue;
/* /*
* Collect attributes and expressions in remaining (unestimated) * Collect attributes and expressions in remaining (unestimated)
* clauses fully covered by this statistic object. * 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; Selectivity sel = (is_or) ? 0.0 : 1.0;
RangeTblEntry *rte = planner_rt_fetch(rel->relid, root); 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. */ /* check if there's any stats that might be useful for us. */
if (!has_stats_of_kind(rel->statlist, STATS_EXT_MCV)) if (!has_stats_of_kind(rel->statlist, STATS_EXT_MCV))
return sel; return sel;
@ -1758,7 +1748,7 @@ statext_mcv_clauselist_selectivity(PlannerInfo *root, List *clauses, int varReli
Bitmapset *simple_clauses; Bitmapset *simple_clauses;
/* find the best suited statistics object for these attnums */ /* 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_attnums, list_exprs,
list_length(clauses)); list_length(clauses));
@ -1847,7 +1837,7 @@ statext_mcv_clauselist_selectivity(PlannerInfo *root, List *clauses, int varReli
MCVList *mcv_list; MCVList *mcv_list;
/* Load the MCV list stored in the statistics object */ /* 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 * 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. * identified by the supplied index.
*/ */
HeapTuple HeapTuple
statext_expressions_load(Oid stxoid, int idx) statext_expressions_load(Oid stxoid, bool inh, int idx)
{ {
bool isnull; bool isnull;
Datum value; Datum value;
@ -2418,7 +2408,8 @@ statext_expressions_load(Oid stxoid, int idx)
HeapTupleData tmptup; HeapTupleData tmptup;
HeapTuple tup; HeapTuple tup;
htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(stxoid)); htup = SearchSysCache2(STATEXTDATASTXOID,
ObjectIdGetDatum(stxoid), BoolGetDatum(inh));
if (!HeapTupleIsValid(htup)) if (!HeapTupleIsValid(htup))
elog(ERROR, "cache lookup failed for statistics object %u", stxoid); elog(ERROR, "cache lookup failed for statistics object %u", stxoid);

@ -559,12 +559,13 @@ build_column_frequencies(SortItem *groups, int ngroups,
* Load the MCV list for the indicated pg_statistic_ext tuple. * Load the MCV list for the indicated pg_statistic_ext tuple.
*/ */
MCVList * MCVList *
statext_mcv_load(Oid mvoid) statext_mcv_load(Oid mvoid, bool inh)
{ {
MCVList *result; MCVList *result;
bool isnull; bool isnull;
Datum mcvlist; Datum mcvlist;
HeapTuple htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid)); HeapTuple htup = SearchSysCache2(STATEXTDATASTXOID,
ObjectIdGetDatum(mvoid), BoolGetDatum(inh));
if (!HeapTupleIsValid(htup)) if (!HeapTupleIsValid(htup))
elog(ERROR, "cache lookup failed for statistics object %u", mvoid); elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
@ -2038,12 +2039,13 @@ mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat,
int i; int i;
MCVList *mcv; MCVList *mcv;
Selectivity s = 0.0; Selectivity s = 0.0;
RangeTblEntry *rte = root->simple_rte_array[rel->relid];
/* match/mismatch bitmap for each MCV item */ /* match/mismatch bitmap for each MCV item */
bool *matches = NULL; bool *matches = NULL;
/* load the MCV list stored in the statistics object */ /* 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 */ /* build a match bitmap for the clauses */
matches = mcv_get_match_bitmap(root, clauses, stat->keys, stat->exprs, matches = mcv_get_match_bitmap(root, clauses, stat->keys, stat->exprs,

@ -146,14 +146,15 @@ statext_ndistinct_build(double totalrows, StatsBuildData *data)
* Load the ndistinct value for the indicated pg_statistic_ext tuple * Load the ndistinct value for the indicated pg_statistic_ext tuple
*/ */
MVNDistinct * MVNDistinct *
statext_ndistinct_load(Oid mvoid) statext_ndistinct_load(Oid mvoid, bool inh)
{ {
MVNDistinct *result; MVNDistinct *result;
bool isnull; bool isnull;
Datum ndist; Datum ndist;
HeapTuple htup; HeapTuple htup;
htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid)); htup = SearchSysCache2(STATEXTDATASTXOID,
ObjectIdGetDatum(mvoid), BoolGetDatum(inh));
if (!HeapTupleIsValid(htup)) if (!HeapTupleIsValid(htup))
elog(ERROR, "cache lookup failed for statistics object %u", mvoid); elog(ERROR, "cache lookup failed for statistics object %u", mvoid);

@ -3919,17 +3919,6 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
if (!rel->statlist) if (!rel->statlist)
return false; 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 */ /* look for the ndistinct statistics object matching the most vars */
nmatches_vars = 0; /* we require at least two matches */ nmatches_vars = 0; /* we require at least two matches */
nmatches_exprs = 0; nmatches_exprs = 0;
@ -4015,7 +4004,8 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
Assert(nmatches_vars + nmatches_exprs > 1); 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 * 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) if (vardata->statsTuple)
break; 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 */ /* skip stats without per-expression stats */
if (info->kind != STATS_EXT_EXPRESSIONS) if (info->kind != STATS_EXT_EXPRESSIONS)
continue; continue;
@ -5274,22 +5253,16 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
/* found a match, see if we can extract pg_statistic row */ /* found a match, see if we can extract pg_statistic row */
if (equal(node, expr)) if (equal(node, expr))
{ {
HeapTuple t = statext_expressions_load(info->statOid, pos);
/* Get statistics object's table for permission check */
RangeTblEntry *rte;
Oid userid; Oid userid;
vardata->statsTuple = t;
/* /*
* XXX Not sure if we should cache the tuple somewhere. * XXX Not sure if we should cache the tuple somewhere.
* Now we just create a new copy every time. * 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); vardata->freefunc = ReleaseDummy;
Assert(rte->rtekind == RTE_RELATION);
/* /*
* Use checkAsUser if it's set, in case we're accessing * Use checkAsUser if it's set, in case we're accessing

@ -763,11 +763,11 @@ static const struct cachedesc cacheinfo[] = {
32 32
}, },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */ {StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidIndexId, StatisticExtDataStxoidInhIndexId,
1, 2,
{ {
Anum_pg_statistic_ext_data_stxoid, Anum_pg_statistic_ext_data_stxoid,
0, Anum_pg_statistic_ext_data_stxdinherit,
0, 0,
0 0
}, },

@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 202201121 #define CATALOG_VERSION_NO 202201161
#endif #endif

@ -32,6 +32,7 @@ CATALOG(pg_statistic_ext_data,3429,StatisticExtDataRelationId)
{ {
Oid stxoid BKI_LOOKUP(pg_statistic_ext); /* statistics object Oid stxoid BKI_LOOKUP(pg_statistic_ext); /* statistics object
* this data is for */ * this data is for */
bool stxdinherit; /* true if inheritance children are included */
#ifdef CATALOG_VARLEN /* variable-length fields start here */ #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_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 */ #endif /* PG_STATISTIC_EXT_DATA_H */

@ -83,6 +83,7 @@ extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt); extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt); extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid); extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
extern Oid StatisticsGetRelation(Oid statId, bool missing_ok); extern Oid StatisticsGetRelation(Oid statId, bool missing_ok);
/* commands/aggregatecmds.c */ /* commands/aggregatecmds.c */

@ -934,6 +934,7 @@ typedef struct StatisticExtInfo
NodeTag type; NodeTag type;
Oid statOid; /* OID of the statistics row */ Oid statOid; /* OID of the statistics row */
bool inherit; /* includes child relations */
RelOptInfo *rel; /* back-link to statistic's table */ RelOptInfo *rel; /* back-link to statistic's table */
char kind; /* statistics kind of this entry */ char kind; /* statistics kind of this entry */
Bitmapset *keys; /* attnums of the columns covered */ Bitmapset *keys; /* attnums of the columns covered */

@ -94,11 +94,11 @@ typedef struct MCVList
MCVItem items[FLEXIBLE_ARRAY_MEMBER]; /* array of MCV items */ MCVItem items[FLEXIBLE_ARRAY_MEMBER]; /* array of MCV items */
} MCVList; } MCVList;
extern MVNDistinct *statext_ndistinct_load(Oid mvoid); extern MVNDistinct *statext_ndistinct_load(Oid mvoid, bool inh);
extern MVDependencies *statext_dependencies_load(Oid mvoid); extern MVDependencies *statext_dependencies_load(Oid mvoid, bool inh);
extern MCVList *statext_mcv_load(Oid mvoid); 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 numrows, HeapTuple *rows,
int natts, VacAttrStats **vacattrstats); int natts, VacAttrStats **vacattrstats);
extern int ComputeExtStatisticsRows(Relation onerel, extern int ComputeExtStatisticsRows(Relation onerel,
@ -121,9 +121,10 @@ extern Selectivity statext_clauselist_selectivity(PlannerInfo *root,
bool is_or); bool is_or);
extern bool has_stats_of_kind(List *stats, char requiredkind); extern bool has_stats_of_kind(List *stats, char requiredkind);
extern StatisticExtInfo *choose_best_statistics(List *stats, char requiredkind, extern StatisticExtInfo *choose_best_statistics(List *stats, char requiredkind,
bool inh,
Bitmapset **clause_attnums, Bitmapset **clause_attnums,
List **clause_exprs, List **clause_exprs,
int nclauses); 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 */ #endif /* STATISTICS_H */

@ -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, JOIN pg_attribute a ON (((a.attrelid = s.stxrelid) AND (a.attnum = k.k))))) AS attnames,
pg_get_statisticsobjdef_expressions(s.oid) AS exprs, pg_get_statisticsobjdef_expressions(s.oid) AS exprs,
s.stxkind AS kinds, s.stxkind AS kinds,
sd.stxdinherit AS inherited,
sd.stxdndistinct AS n_distinct, sd.stxdndistinct AS n_distinct,
sd.stxddependencies AS dependencies, sd.stxddependencies AS dependencies,
m.most_common_vals, m.most_common_vals,
@ -2469,6 +2470,7 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
s.stxname AS statistics_name, s.stxname AS statistics_name,
pg_get_userbyid(s.stxowner) AS statistics_owner, pg_get_userbyid(s.stxowner) AS statistics_owner,
stat.expr, stat.expr,
sd.stxdinherit AS inherited,
(stat.a).stanullfrac AS null_frac, (stat.a).stanullfrac AS null_frac,
(stat.a).stawidth AS avg_width, (stat.a).stawidth AS avg_width,
(stat.a).stadistinct AS n_distinct, (stat.a).stadistinct AS n_distinct,

@ -140,13 +140,12 @@ Statistics objects:
"public.ab1_a_b_stats" ON a, b FROM ab1; STATISTICS 0 "public.ab1_a_b_stats" ON a, b FROM ab1; STATISTICS 0
ANALYZE ab1; ANALYZE ab1;
SELECT stxname, stxdndistinct, stxddependencies, stxdmcv SELECT stxname, stxdndistinct, stxddependencies, stxdmcv, stxdinherit
FROM pg_statistic_ext s, pg_statistic_ext_data d FROM pg_statistic_ext s LEFT JOIN pg_statistic_ext_data d ON (d.stxoid = s.oid)
WHERE s.stxname = 'ab1_a_b_stats' WHERE s.stxname = 'ab1_a_b_stats';
AND d.stxoid = s.oid; stxname | stxdndistinct | stxddependencies | stxdmcv | stxdinherit
stxname | stxdndistinct | stxddependencies | stxdmcv ---------------+---------------+------------------+---------+-------------
---------------+---------------+------------------+--------- ab1_a_b_stats | | | |
ab1_a_b_stats | | |
(1 row) (1 row)
ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1; 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; CREATE STATISTICS stxdinh ON a, b FROM stxdinh;
VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2; VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2;
-- Since the stats object does not include inherited stats, it should not -- See if the extended stats affect the estimates
-- affect the estimates
SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2');
estimated | actual estimated | actual
-----------+-------- -----------+--------
400 | 150 150 | 150
(1 row) (1 row)
-- Dependencies are applied at individual relations (within append), so -- 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 22 | 40
(1 row) (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; DROP TABLE stxdinh, stxdinh1, stxdinh2;
-- Ensure inherited stats ARE applied to inherited query in partitioned table -- Ensure inherited stats ARE applied to inherited query in partitioned table
CREATE TABLE stxdinp(i int, a int, b int) PARTITION BY RANGE (i); CREATE TABLE stxdinp(i int, a int, b int) PARTITION BY RANGE (i);

@ -91,10 +91,9 @@ ALTER TABLE ab1 ALTER a SET STATISTICS -1;
ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0; ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
\d ab1 \d ab1
ANALYZE ab1; ANALYZE ab1;
SELECT stxname, stxdndistinct, stxddependencies, stxdmcv SELECT stxname, stxdndistinct, stxddependencies, stxdmcv, stxdinherit
FROM pg_statistic_ext s, pg_statistic_ext_data d FROM pg_statistic_ext s LEFT JOIN pg_statistic_ext_data d ON (d.stxoid = s.oid)
WHERE s.stxname = 'ab1_a_b_stats' WHERE s.stxname = 'ab1_a_b_stats';
AND d.stxoid = s.oid;
ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1; ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1;
\d+ ab1 \d+ ab1
-- partial analyze doesn't build stats either -- 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'); SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0');
CREATE STATISTICS stxdinh ON a, b FROM stxdinh; CREATE STATISTICS stxdinh ON a, b FROM stxdinh;
VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2; VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2;
-- Since the stats object does not include inherited stats, it should not -- See if the extended stats affect the estimates
-- affect the estimates
SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2');
-- Dependencies are applied at individual relations (within append), so -- Dependencies are applied at individual relations (within append), so
-- this estimate changes a bit because we improve estimates for the parent -- 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'); 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; DROP TABLE stxdinh, stxdinh1, stxdinh2;
-- Ensure inherited stats ARE applied to inherited query in partitioned table -- Ensure inherited stats ARE applied to inherited query in partitioned table

Loading…
Cancel
Save