diff options
author | Tomas Vondra | 2019-06-13 15:19:21 +0000 |
---|---|---|
committer | Tomas Vondra | 2019-06-15 23:20:31 +0000 |
commit | 6cbfb784c3c91146148a76d50cda6f69ae6a79fb (patch) | |
tree | 468bd8af4fe30903c7975df67f9f195e8ff5ba1d /src/backend/commands/statscmds.c | |
parent | e3846a00c2f87402dcedf7f07950ab2d89cf5827 (diff) |
Rework the pg_statistic_ext catalog
Since extended statistic got introduced in PostgreSQL 10, there was a
single catalog pg_statistic_ext storing both the definitions and built
statistic. That's however problematic when a user is supposed to have
access only to the definitions, but not to user data.
Consider for example pg_dump on a database with RLS enabled - if the
pg_statistic_ext catalog respects RLS (which it should, if it contains
user data), pg_dump would not see any records and the result would not
define any extended statistics. That would be a surprising behavior.
Until now this was not a pressing issue, because the existing types of
extended statistic (functional dependencies and ndistinct coefficients)
do not include any user data directly. This changed with introduction
of MCV lists, which do include most common combinations of values.
The easiest way to fix this is to split the pg_statistic_ext catalog
into two - one for definitions, one for the built statistic values.
The new catalog is called pg_statistic_ext_data, and we're maintaining
a 1:1 relationship with the old catalog - either there are matching
records in both catalogs, or neither of them.
Bumped CATVERSION due to changing system catalog definitions.
Author: Dean Rasheed, with improvements by me
Reviewed-by: Dean Rasheed, John Naylor
Discussion: https://postgr.es/m/CAEZATCUhT9rt7Ui%3DVdx4N%3D%3DVV5XOK5dsXfnGgVOz_JhAicB%3DZA%40mail.gmail.com
Diffstat (limited to 'src/backend/commands/statscmds.c')
-rw-r--r-- | src/backend/commands/statscmds.c | 73 |
1 files changed, 56 insertions, 17 deletions
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index 217d3a4533a..cf406f6f96b 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -23,6 +23,7 @@ #include "catalog/namespace.h" #include "catalog/pg_namespace.h" #include "catalog/pg_statistic_ext.h" +#include "catalog/pg_statistic_ext_data.h" #include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" @@ -67,8 +68,11 @@ 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; Relation statrel; + Relation datarel; Relation rel = NULL; Oid relid; ObjectAddress parentobject, @@ -336,11 +340,6 @@ CreateStatistics(CreateStatsStmt *stmt) values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys); values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind); - /* no statistics built yet */ - nulls[Anum_pg_statistic_ext_stxndistinct - 1] = true; - nulls[Anum_pg_statistic_ext_stxdependencies - 1] = true; - nulls[Anum_pg_statistic_ext_stxmcv - 1] = true; - /* insert it into pg_statistic_ext */ htup = heap_form_tuple(statrel->rd_att, values, nulls); CatalogTupleInsert(statrel, htup); @@ -349,6 +348,29 @@ CreateStatistics(CreateStatsStmt *stmt) relation_close(statrel, RowExclusiveLock); /* + * Also build the pg_statistic_ext_data tuple, to hold the actual + * statistics data. + */ + 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; + + /* 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); + + /* * Invalidate relcache so that others see the new statistics object. */ CacheInvalidateRelcache(rel); @@ -404,6 +426,23 @@ RemoveStatisticsById(Oid statsOid) 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); + + CatalogTupleDelete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + table_close(relation, RowExclusiveLock); + + /* * Delete the pg_statistic_ext tuple. Also send out a cache inval on the * associated table, so that dependent plans will be rebuilt. */ @@ -431,8 +470,8 @@ RemoveStatisticsById(Oid statsOid) * * This could throw an error if the type change can't be supported. * If it can be supported, but the stats must be recomputed, a likely choice - * would be to set the relevant column(s) of the pg_statistic_ext tuple to - * null until the next ANALYZE. (Note that the type change hasn't actually + * would be to set the relevant column(s) of the pg_statistic_ext_data tuple + * to null until the next ANALYZE. (Note that the type change hasn't actually * happened yet, so one option that's *not* on the table is to recompute * immediately.) * @@ -456,11 +495,11 @@ UpdateStatisticsForTypeChange(Oid statsOid, Oid relationOid, int attnum, Relation rel; - Datum values[Natts_pg_statistic_ext]; - bool nulls[Natts_pg_statistic_ext]; - bool replaces[Natts_pg_statistic_ext]; + Datum values[Natts_pg_statistic_ext_data]; + bool nulls[Natts_pg_statistic_ext_data]; + bool replaces[Natts_pg_statistic_ext_data]; - oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid)); + oldtup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid)); if (!HeapTupleIsValid(oldtup)) elog(ERROR, "cache lookup failed for statistics object %u", statsOid); @@ -479,14 +518,14 @@ UpdateStatisticsForTypeChange(Oid statsOid, Oid relationOid, int attnum, * OK, we need to reset some statistics. So let's build the new tuple, * replacing the affected statistics types with NULL. */ - memset(nulls, 0, Natts_pg_statistic_ext * sizeof(bool)); - memset(replaces, 0, Natts_pg_statistic_ext * sizeof(bool)); - memset(values, 0, Natts_pg_statistic_ext * sizeof(Datum)); + memset(nulls, 0, Natts_pg_statistic_ext_data * sizeof(bool)); + memset(replaces, 0, Natts_pg_statistic_ext_data * sizeof(bool)); + memset(values, 0, Natts_pg_statistic_ext_data * sizeof(Datum)); - replaces[Anum_pg_statistic_ext_stxmcv - 1] = true; - nulls[Anum_pg_statistic_ext_stxmcv - 1] = true; + replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true; + nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true; - rel = heap_open(StatisticExtRelationId, RowExclusiveLock); + rel = heap_open(StatisticExtDataRelationId, RowExclusiveLock); /* replace the old tuple */ stup = heap_modify_tuple(oldtup, |