On 2021-Apr-08, Tom Lane wrote: > > So I tend to think that my initial instinct was the better direction: we > > should not be doing any find_all_inheritors() here at all, but instead > > rely on pg_class.reltuples to be set for the partitioned table. > > +1
This patch does that. -- Álvaro Herrera 39°49'30"S 73°17'W "I dream about dreams about dreams", sang the nightingale under the pale moon (Sandman)
>From a54701552f2ba9295aae4fe0fc22c7bac912bf45 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Thu, 8 Apr 2021 11:10:44 -0400 Subject: [PATCH] Set pg_class.reltuples for partitioned tables --- src/backend/commands/analyze.c | 12 +++++++ src/backend/commands/tablecmds.c | 51 ++++++++++++++++++++++++++++- src/backend/postmaster/autovacuum.c | 39 +--------------------- 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5bdaceefd5..2de699d838 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -656,6 +656,18 @@ do_analyze_rel(Relation onerel, VacuumParams *params, in_outer_xact); } } + else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + /* + * Partitioned tables don't have storage, so we don't set any fields in + * their pg_class entries except for relpages, which is necessary for + * auto-analyze to work properly. + */ + vac_update_relstats(onerel, -1, totalrows, + 0, false, InvalidTransactionId, + InvalidMultiXactId, + in_outer_xact); + } /* * Now report ANALYZE to the stats collector. For regular tables, we do diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1f19629a94..deca860c80 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -337,6 +337,7 @@ typedef struct ForeignTruncateInfo static void truncate_check_rel(Oid relid, Form_pg_class reltuple); static void truncate_check_perms(Oid relid, Form_pg_class reltuple); static void truncate_check_activity(Relation rel); +static void truncate_update_partedrel_stats(List *parted_rels); static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg); static List *MergeAttributes(List *schema, List *supers, char relpersistence, @@ -1755,6 +1756,7 @@ ExecuteTruncateGuts(List *explicit_rels, { List *rels; List *seq_relids = NIL; + List *parted_rels = NIL; HTAB *ft_htab = NULL; EState *estate; ResultRelInfo *resultRelInfos; @@ -1908,9 +1910,15 @@ ExecuteTruncateGuts(List *explicit_rels, Relation rel = (Relation) lfirst(lc1); int extra = lfirst_int(lc2); - /* Skip partitioned tables as there is nothing to do */ + /* + * Save OID of partitioned tables for later; nothing else to do for + * them here. + */ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + parted_rels = lappend_oid(parted_rels, RelationGetRelid(rel)); continue; + } /* * Build the lists of foreign tables belonging to each foreign server @@ -2061,6 +2069,9 @@ ExecuteTruncateGuts(List *explicit_rels, ResetSequence(seq_relid); } + /* Reset partitioned tables' pg_class.reltuples */ + truncate_update_partedrel_stats(parted_rels); + /* * Write a WAL record to allow this set of actions to be logically * decoded. @@ -2207,6 +2218,44 @@ truncate_check_activity(Relation rel) CheckTableNotInUse(rel, "TRUNCATE"); } +/* + * Update pg_class.reltuples for all the given partitioned tables to 0. + */ +static void +truncate_update_partedrel_stats(List *parted_rels) +{ + Relation pg_class; + HeapTuple tuple; + Form_pg_class rd_rel; + ListCell *lc; + + pg_class = table_open(RelationRelationId, RowExclusiveLock); + + foreach(lc, parted_rels) + { + Oid relid = lfirst_oid(lc); + bool dirty = false; + + tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for relation %u", relid); + rd_rel = (Form_pg_class) GETSTRUCT(tuple); + + if (rd_rel->reltuples != 0) + { + rd_rel->reltuples = (float4) 0; + dirty = true; + } + + if (dirty) + heap_inplace_update(pg_class, tuple); + + heap_freetuple(tuple); + } + + table_close(pg_class, RowExclusiveLock); +} + /* * storage_name * returns the name corresponding to a typstorage/attstorage enum value diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index aef9ac4dd2..a799544738 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -3209,44 +3209,7 @@ relation_needs_vacanalyze(Oid relid, */ if (PointerIsValid(tabentry) && AutoVacuumingActive()) { - if (classForm->relkind != RELKIND_PARTITIONED_TABLE) - { - reltuples = classForm->reltuples; - } - else - { - /* - * If the relation is a partitioned table, we must add up - * children's reltuples. - */ - List *children; - ListCell *lc; - - reltuples = 0; - - /* Find all members of inheritance set taking AccessShareLock */ - children = find_all_inheritors(relid, AccessShareLock, NULL); - - foreach(lc, children) - { - Oid childOID = lfirst_oid(lc); - HeapTuple childtuple; - Form_pg_class childclass; - - childtuple = SearchSysCache1(RELOID, ObjectIdGetDatum(childOID)); - childclass = (Form_pg_class) GETSTRUCT(childtuple); - - /* Skip a partitioned table and foreign partitions */ - if (RELKIND_HAS_STORAGE(childclass->relkind)) - { - /* Sum up the child's reltuples for its parent table */ - reltuples += childclass->reltuples; - } - ReleaseSysCache(childtuple); - } - - list_free(children); - } + reltuples = classForm->reltuples; vactuples = tabentry->n_dead_tuples; instuples = tabentry->inserts_since_vacuum; anltuples = tabentry->changes_since_analyze; -- 2.20.1