v15_autovacuum_on_partitioned_table.patch
application/octet-stream
Filename: v15_autovacuum_on_partitioned_table.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
Series: patch v15
| File | + | − |
|---|---|---|
| src/backend/access/common/reloptions.c | 7 | 8 |
| src/backend/catalog/system_views.sql | 2 | 2 |
| src/backend/commands/analyze.c | 21 | 18 |
| src/backend/commands/vacuum.c | 7 | 4 |
| src/backend/nodes/makefuncs.c | 2 | 1 |
| src/backend/parser/gram.y | 1 | 1 |
| src/backend/postmaster/autovacuum.c | 86 | 8 |
| src/backend/postmaster/pgstat.c | 199 | 11 |
| src/include/commands/vacuum.h | 2 | 2 |
| src/include/nodes/makefuncs.h | 1 | 1 |
| src/include/nodes/parsenodes.h | 3 | 0 |
| src/include/pgstat.h | 39 | 1 |
| src/test/regress/expected/rules.out | 2 | 2 |
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index d897bbe..5554275 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -108,7 +108,7 @@ static relopt_bool boolRelOpts[] =
{
"autovacuum_enabled",
"Enables autovacuum in this relation",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST | RELOPT_KIND_PARTITIONED,
ShareUpdateExclusiveLock
},
true
@@ -246,7 +246,7 @@ static relopt_int intRelOpts[] =
{
"autovacuum_analyze_threshold",
"Minimum number of tuple inserts, updates or deletes prior to analyze",
- RELOPT_KIND_HEAP,
+ RELOPT_KIND_HEAP | RELOPT_KIND_PARTITIONED,
ShareUpdateExclusiveLock
},
-1, 0, INT_MAX
@@ -420,7 +420,7 @@ static relopt_real realRelOpts[] =
{
"autovacuum_analyze_scale_factor",
"Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
- RELOPT_KIND_HEAP,
+ RELOPT_KIND_HEAP | RELOPT_KIND_PARTITIONED,
ShareUpdateExclusiveLock
},
-1, 0.0, 100.0
@@ -1962,12 +1962,11 @@ bytea *
partitioned_table_reloptions(Datum reloptions, bool validate)
{
/*
- * There are no options for partitioned tables yet, but this is able to do
- * some validation.
+ * autovacuum_enabled, autovacuum_analyze_threshold and
+ * autovacuum_analyze_scale_factor are supported for partitioned tables.
*/
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_PARTITIONED,
- 0, NULL, 0);
+
+ return default_reloptions(reloptions, validate, RELOPT_KIND_PARTITIONED);
}
/*
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 5f2541d..fb41b06 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -660,7 +660,7 @@ CREATE VIEW pg_stat_all_tables AS
FROM pg_class C LEFT JOIN
pg_index I ON C.oid = I.indrelid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
- WHERE C.relkind IN ('r', 't', 'm')
+ WHERE C.relkind IN ('r', 't', 'm', 'p')
GROUP BY C.oid, N.nspname, C.relname;
CREATE VIEW pg_stat_xact_all_tables AS
@@ -680,7 +680,7 @@ CREATE VIEW pg_stat_xact_all_tables AS
FROM pg_class C LEFT JOIN
pg_index I ON C.oid = I.indrelid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
- WHERE C.relkind IN ('r', 't', 'm')
+ WHERE C.relkind IN ('r', 't', 'm', 'p')
GROUP BY C.oid, N.nspname, C.relname;
CREATE VIEW pg_stat_sys_tables AS
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index f84616d..6e06dc3 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -89,7 +89,7 @@ static BufferAccessStrategy vac_strategy;
static void do_analyze_rel(Relation onerel,
VacuumParams *params, List *va_cols,
AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
- bool inh, bool in_outer_xact, int elevel);
+ bool inh, Oid toprel_oid, bool in_outer_xact, int elevel);
static void compute_index_stats(Relation onerel, double totalrows,
AnlIndexData *indexdata, int nindexes,
HeapTuple *rows, int numrows,
@@ -118,7 +118,8 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
*/
void
analyze_rel(Oid relid, RangeVar *relation,
- VacuumParams *params, List *va_cols, bool in_outer_xact,
+ VacuumParams *params, List *va_cols,
+ Oid toprel_oid, bool in_outer_xact,
BufferAccessStrategy bstrategy)
{
Relation onerel;
@@ -259,14 +260,14 @@ analyze_rel(Oid relid, RangeVar *relation,
*/
if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
do_analyze_rel(onerel, params, va_cols, acquirefunc,
- relpages, false, in_outer_xact, elevel);
+ relpages, false, toprel_oid, in_outer_xact, elevel);
/*
* If there are child tables, do recursive ANALYZE.
*/
if (onerel->rd_rel->relhassubclass)
do_analyze_rel(onerel, params, va_cols, acquirefunc, relpages,
- true, in_outer_xact, elevel);
+ true, toprel_oid, in_outer_xact, elevel);
/*
* Close source relation now, but keep lock so that no one deletes it
@@ -289,8 +290,8 @@ analyze_rel(Oid relid, RangeVar *relation,
static void
do_analyze_rel(Relation onerel, VacuumParams *params,
List *va_cols, AcquireSampleRowsFunc acquirefunc,
- BlockNumber relpages, bool inh, bool in_outer_xact,
- int elevel)
+ BlockNumber relpages, bool inh, Oid toprel_oid,
+ bool in_outer_xact, int elevel)
{
int attr_cnt,
tcnt,
@@ -655,20 +656,22 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
InvalidMultiXactId,
in_outer_xact);
}
+ }
- /*
- * Now report ANALYZE to the stats collector.
- *
- * We deliberately don't report to the stats collector when doing
- * inherited stats, because the stats collector only tracks per-table
- * stats.
- *
- * Reset the changes_since_analyze counter only if we analyzed all
- * columns; otherwise, there is still work for auto-analyze to do.
- */
+ /*
+ * Now report ANALYZE to the stats collector.
+ *
+ * Regarding inherited stats, we report only in the case of declarative
+ * partitioning. For partitioning based on inheritance, stats collector
+ * only tracks per-table stats.
+ *
+ * Reset the changes_since_analyze counter only if we analyzed all
+ * columns; otherwise, there is still work for auto-analyze to do.
+ */
+ if (!inh || onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
pgstat_report_analyze(onerel, totalrows, totaldeadrows,
- (va_cols == NIL));
- }
+ (va_cols == NIL), toprel_oid);
+
/*
* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup.
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 662aff0..d0dcd0c 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -474,7 +474,7 @@ vacuum(List *relations, VacuumParams *params,
}
analyze_rel(vrel->oid, vrel->relation, params,
- vrel->va_cols, in_outer_xact, vac_strategy);
+ vrel->va_cols, vrel->toprel_oid, in_outer_xact, vac_strategy);
if (use_own_xacts)
{
@@ -801,7 +801,8 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
oldcontext = MemoryContextSwitchTo(vac_context);
vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
relid,
- vrel->va_cols));
+ vrel->va_cols,
+ relid));
MemoryContextSwitchTo(oldcontext);
}
@@ -838,7 +839,8 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
oldcontext = MemoryContextSwitchTo(vac_context);
vacrels = lappend(vacrels, makeVacuumRelation(NULL,
part_oid,
- vrel->va_cols));
+ vrel->va_cols,
+ relid));
MemoryContextSwitchTo(oldcontext);
}
}
@@ -904,7 +906,8 @@ get_all_vacuum_rels(int options)
oldcontext = MemoryContextSwitchTo(vac_context);
vacrels = lappend(vacrels, makeVacuumRelation(NULL,
relid,
- NIL));
+ NIL,
+ InvalidOid));
MemoryContextSwitchTo(oldcontext);
}
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 01c110c..e431385 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -806,12 +806,13 @@ makeGroupingSet(GroupingSetKind kind, List *content, int location)
* create a VacuumRelation node
*/
VacuumRelation *
-makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
+makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols, Oid toprel_oid)
{
VacuumRelation *v = makeNode(VacuumRelation);
v->relation = relation;
v->oid = oid;
v->va_cols = va_cols;
+ v->toprel_oid = toprel_oid;
return v;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8b1bad0..7b915cd 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10692,7 +10692,7 @@ opt_name_list:
vacuum_relation:
qualified_name opt_name_list
{
- $$ = (Node *) makeVacuumRelation($1, InvalidOid, $2);
+ $$ = (Node *) makeVacuumRelation($1, InvalidOid, $2, InvalidOid);
}
;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 23ef23c..096a979 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -74,7 +74,9 @@
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
+#include "catalog/partition.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_inherits.h"
#include "commands/dbcommands.h"
#include "commands/vacuum.h"
#include "lib/ilist.h"
@@ -1970,6 +1972,7 @@ do_autovacuum(void)
bool did_vacuum = false;
bool found_concurrent_worker = false;
int i;
+ bool updated = false;
/*
* StartTransactionCommand and CommitTransactionCommand will automatically
@@ -2055,11 +2058,11 @@ do_autovacuum(void)
* Scan pg_class to determine which tables to vacuum.
*
* We do this in two passes: on the first one we collect the list of plain
- * relations and materialized views, and on the second one we collect
- * TOAST tables. The reason for doing the second pass is that during it we
- * want to use the main relation's pg_class.reloptions entry if the TOAST
- * table does not have any, and we cannot obtain it unless we know
- * beforehand what's the main table OID.
+ * relations, materialized views and partitioned tables, and on the second
+ * one we collect TOAST tables. The reason for doing the second pass is
+ * that during it we want to use the main relation's pg_class.reloptions
+ * entry if the TOAST table does not have any, and we cannot obtain it
+ * unless we know beforehand what's the main table OID.
*
* We need to check TOAST tables separately because in cases with short,
* wide tables there might be proportionally much more activity in the
@@ -2068,6 +2071,42 @@ do_autovacuum(void)
relScan = table_beginscan_catalog(classRel, 0, NULL);
/*
+ * Before collecting the list of tables to vacuum, we propagate
+ * changes_since_analyze count from leaf partitions to ancestors.
+ * This counter enables auto-analyze on partitioned tables.
+ */
+ while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ {
+ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+ PgStat_StatTabEntry *tabentry;
+ Oid relid;
+
+ if (!classForm->relispartition ||
+ classForm->relkind == RELKIND_PARTITIONED_TABLE ||
+ classForm->relpersistence == RELPERSISTENCE_TEMP)
+ continue;
+
+ relid = classForm->oid;
+
+ /* Fetch the pgstat entry for this table */
+ tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
+ shared, dbentry);
+
+ /* Propagate counter to all of ancestors. */
+ if (tabentry)
+ pgstat_propagate_changes(classForm, tabentry, InvalidOid, &updated);
+ }
+
+ /* Use fresh stats */
+ if (updated)
+ {
+ autovac_refresh_stats();
+
+ dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
+ shared = pgstat_fetch_stat_dbentry(InvalidOid);
+ }
+
+ /*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
@@ -2082,7 +2121,8 @@ do_autovacuum(void)
bool wraparound;
if (classForm->relkind != RELKIND_RELATION &&
- classForm->relkind != RELKIND_MATVIEW)
+ classForm->relkind != RELKIND_MATVIEW &&
+ classForm->relkind != RELKIND_PARTITIONED_TABLE)
continue;
relid = classForm->oid;
@@ -2745,6 +2785,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
+ ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_PARTITIONED_TABLE ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
relopts = extractRelOptions(tup, pg_class_desc, NULL);
@@ -3161,7 +3202,44 @@ relation_needs_vacanalyze(Oid relid,
*/
if (PointerIsValid(tabentry) && AutoVacuumingActive())
{
- reltuples = classForm->reltuples;
+ 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);
+ }
vactuples = tabentry->n_dead_tuples;
instuples = tabentry->inserts_since_vacuum;
anltuples = tabentry->changes_since_analyze;
@@ -3225,7 +3303,7 @@ autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
/* Set up one VacuumRelation target, identified by OID, for vacuum() */
rangevar = makeRangeVar(tab->at_nspname, tab->at_relname, -1);
- rel = makeVacuumRelation(rangevar, tab->at_relid, NIL);
+ rel = makeVacuumRelation(rangevar, tab->at_relid, NIL, InvalidOid);
rel_list = list_make1(rel);
vacuum(rel_list, &tab->at_params, bstrategy, true);
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 4b9bcd2..d5b9d0a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -38,6 +38,7 @@
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
+#include "catalog/partition.h"
#include "catalog/pg_database.h"
#include "catalog/pg_proc.h"
#include "common/ip.h"
@@ -373,6 +374,8 @@ static void pgstat_recv_resetreplslotcounter(PgStat_MsgResetreplslotcounter *msg
static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len);
static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len);
static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len);
+static void pgstat_recv_partchanges(PgStat_MsgPartChanges *msg, int len);
+static void pgstat_recv_reportedchanges(PgStat_MsgReportedChanges *msg, int len);
static void pgstat_recv_archiver(PgStat_MsgArchiver *msg, int len);
static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len);
static void pgstat_recv_wal(PgStat_MsgWal *msg, int len);
@@ -1622,12 +1625,15 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
*
* Caller must provide new live- and dead-tuples estimates, as well as a
* flag indicating whether to reset the changes_since_analyze counter.
+ * Exceptional support only changes_since_analyze for partitioned tables,
+ * though they don't have any data. This counter will tell us whether
+ * partitioned tables need autoanalyze or not.
* --------
*/
void
pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
- bool resetcounter)
+ bool resetcounter, Oid toprel_oid)
{
PgStat_MsgAnalyze msg;
@@ -1643,23 +1649,48 @@ pgstat_report_analyze(Relation rel,
* be double-counted after commit. (This approach also ensures that the
* collector ends up with the right numbers if we abort instead of
* committing.)
+ *
+ * For partitioned tables, we don't report live and dead tuples, because
+ * such tables don't have any data.
*/
if (rel->pgstat_info != NULL)
{
PgStat_TableXactStatus *trans;
- for (trans = rel->pgstat_info->trans; trans; trans = trans->upper)
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ /* If this rel is partitioned, skip modifying */
+ livetuples = deadtuples = 0;
+ else
{
- livetuples -= trans->tuples_inserted - trans->tuples_deleted;
- deadtuples -= trans->tuples_updated + trans->tuples_deleted;
+ for (trans = rel->pgstat_info->trans; trans; trans = trans->upper)
+ {
+ livetuples -= trans->tuples_inserted - trans->tuples_deleted;
+ deadtuples -= trans->tuples_updated + trans->tuples_deleted;
+ }
+ /* count stuff inserted by already-aborted subxacts, too */
+ deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
+ /* Since ANALYZE's counts are estimates, we could have underflowed */
+ livetuples = Max(livetuples, 0);
+ deadtuples = Max(deadtuples, 0);
}
- /* count stuff inserted by already-aborted subxacts, too */
- deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
- /* Since ANALYZE's counts are estimates, we could have underflowed */
- livetuples = Max(livetuples, 0);
- deadtuples = Max(deadtuples, 0);
- }
+ /*
+ * If the relation is a leaf partition and this is not autovacuum process,
+ * propagate changes_since_analyze countes to the ancestors.
+ */
+ if (!IsAutoVacuumWorkerProcess() &&
+ rel->rd_rel->relispartition &&
+ rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
+ rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
+ {
+ PgStat_StatDBEntry *dbentry;
+ PgStat_StatTabEntry *tabentry;
+ /* Fetch the pgstat entry for this table */
+ dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
+ tabentry = pgstat_get_tab_entry(dbentry, RelationGetRelid(rel), true);
+ pgstat_propagate_changes(rel->rd_rel, tabentry, toprel_oid, NULL);
+ }
+ }
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE);
msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
msg.m_tableoid = RelationGetRelid(rel);
@@ -1672,6 +1703,118 @@ pgstat_report_analyze(Relation rel,
}
/* --------
+ * pgstat_report_partchanges() -
+ *
+ * Propagate changes_since_analyze counter from a leaf partition to its parent.
+ * --------
+ */
+void
+pgstat_report_partchanges(Relation rel, PgStat_Counter changed_tuples)
+{
+ PgStat_MsgPartChanges msg;
+
+ if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
+ return;
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_PARTCHANGES);
+ msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
+ msg.m_tableoid = RelationGetRelid(rel);
+ msg.m_changed_tuples = changed_tuples;
+ pgstat_send(&msg, sizeof(msg));
+}
+
+/* --------
+ * pgstat_report_reportedchanges() -
+ *
+ * Tell the collector changes_since_analyze counter we have already
+ * propagated to its ancestors.
+ * --------
+ */
+void
+pgstat_report_reportedchanges(Relation rel, PgStat_Counter changed_tuples_reported)
+{
+ PgStat_MsgReportedChanges msg;
+
+ if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
+ return;
+
+ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_REPORTEDCHANGES);
+ msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
+ msg.m_tableoid = RelationGetRelid(rel);
+ msg.m_changed_tuples_reported = changed_tuples_reported;
+ pgstat_send(&msg, sizeof(msg));
+}
+
+/*
+ * pgstat_propagate_changes
+ *
+ * Propagate changes_since_analyze counter to all of ancestors
+ * to analyze partitioned tables automatically
+ *
+ * We can decide whether a partitioned table needs auto analyze according to
+ * changes_since_analyze which is propagated from all of the leaf partitions.
+ * To know the correct difference of partitioned table from the last analyze,
+ * we should track changes_since_analyze_reported counter for leaf partitions
+ * as well as changes_since_analyze counter. While changes_since_analyze
+ * counter tracks the number of changed tuples from the last analyze per
+ * partitions, changes_since_analyze_reported counter tracks changes_since_analyze
+ * we already propagated to ancestors. Then, we propagate only the difference
+ * between these counters to the partitioned table.
+ */
+void
+pgstat_propagate_changes(Form_pg_class classForm, PgStat_StatTabEntry *tabentry,
+ Oid toprel_oid, bool *updated)
+{
+ float4 anltuples,
+ anltuples_reported,
+ change_count;
+ List *ancestors;
+ ListCell *lc;
+ Relation parentrel,
+ childrel;
+
+ /*
+ * Get its all ancestors to propagate changes_since_analyze count.
+ * When doing manual ANALYZE on inheritance tree, toprel_oid that indicates
+ * top level table's OID is a valid. In this case, we should propagate
+ * the counter to only ancestors which are not analyzed in this round.
+ * So we get ancestors of toprel_oid.
+ */
+ if (!OidIsValid(toprel_oid))
+ ancestors = get_partition_ancestors(classForm->oid);
+ else
+ ancestors = get_partition_ancestors(toprel_oid);
+
+ anltuples = tabentry->changes_since_analyze;
+ anltuples_reported = tabentry->changes_since_analyze_reported;
+ change_count = anltuples - anltuples_reported;
+
+ /* update changes_since_analyze of ancestors */
+ if (anltuples > 0 && change_count > 0)
+ {
+ foreach(lc, ancestors)
+ {
+ Oid relid = lfirst_oid(lc);
+
+ parentrel = table_open(relid, AccessShareLock);
+ pgstat_report_partchanges(parentrel, change_count);
+ table_close(parentrel, AccessShareLock);
+ }
+
+ /* update own changes_since_analyze_reported */
+ childrel = table_open(classForm->oid, AccessShareLock);
+ pgstat_report_reportedchanges(childrel, change_count);
+ table_close(childrel, AccessShareLock);
+ }
+
+ /* If we updated the stats, *updated is set true to refresh that */
+ if (updated)
+ *updated = (anltuples > 0 && change_count > 0);
+
+ list_free(ancestors);
+}
+
+/* --------
* pgstat_report_recovery_conflict() -
*
* Tell the collector about a Hot Standby recovery conflict.
@@ -1986,7 +2129,8 @@ pgstat_initstats(Relation rel)
char relkind = rel->rd_rel->relkind;
/* We only count stats for things that have storage */
- if (!RELKIND_HAS_STORAGE(relkind))
+ if (!RELKIND_HAS_STORAGE(relkind) &&
+ relkind != RELKIND_PARTITIONED_TABLE)
{
rel->pgstat_info = NULL;
return;
@@ -5001,6 +5145,14 @@ PgstatCollectorMain(int argc, char *argv[])
pgstat_recv_analyze(&msg.msg_analyze, len);
break;
+ case PGSTAT_MTYPE_PARTCHANGES:
+ pgstat_recv_partchanges(&msg.msg_partchanges, len);
+ break;
+
+ case PGSTAT_MTYPE_REPORTEDCHANGES:
+ pgstat_recv_reportedchanges(&msg.msg_reportedchanges, len);
+ break;
+
case PGSTAT_MTYPE_ARCHIVER:
pgstat_recv_archiver(&msg.msg_archiver, len);
break;
@@ -5215,6 +5367,7 @@ pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create)
result->n_live_tuples = 0;
result->n_dead_tuples = 0;
result->changes_since_analyze = 0;
+ result->changes_since_analyze_reported = 0;
result->inserts_since_vacuum = 0;
result->blocks_fetched = 0;
result->blocks_hit = 0;
@@ -6477,6 +6630,8 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
tabentry->n_live_tuples = tabmsg->t_counts.t_delta_live_tuples;
tabentry->n_dead_tuples = tabmsg->t_counts.t_delta_dead_tuples;
tabentry->changes_since_analyze = tabmsg->t_counts.t_changed_tuples;
+ tabentry->changes_since_analyze_reported
+ = tabmsg->t_counts.t_changed_tuples_reported;
tabentry->inserts_since_vacuum = tabmsg->t_counts.t_tuples_inserted;
tabentry->blocks_fetched = tabmsg->t_counts.t_blocks_fetched;
tabentry->blocks_hit = tabmsg->t_counts.t_blocks_hit;
@@ -6512,6 +6667,8 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
tabentry->n_live_tuples += tabmsg->t_counts.t_delta_live_tuples;
tabentry->n_dead_tuples += tabmsg->t_counts.t_delta_dead_tuples;
tabentry->changes_since_analyze += tabmsg->t_counts.t_changed_tuples;
+ tabentry->changes_since_analyze_reported
+ += tabmsg->t_counts.t_changed_tuples_reported;
tabentry->inserts_since_vacuum += tabmsg->t_counts.t_tuples_inserted;
tabentry->blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
tabentry->blocks_hit += tabmsg->t_counts.t_blocks_hit;
@@ -6868,7 +7025,10 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
* have no good way to estimate how many of those there were.
*/
if (msg->m_resetcounter)
+ {
tabentry->changes_since_analyze = 0;
+ tabentry->changes_since_analyze_reported = 0;
+ }
if (msg->m_autovacuum)
{
@@ -6882,6 +7042,34 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
}
}
+static void
+pgstat_recv_partchanges(PgStat_MsgPartChanges *msg, int len)
+{
+ PgStat_StatDBEntry *dbentry;
+ PgStat_StatTabEntry *tabentry;
+
+ dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
+
+ tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
+
+ tabentry->changes_since_analyze += msg->m_changed_tuples;
+}
+
+
+static void
+pgstat_recv_reportedchanges(PgStat_MsgReportedChanges *msg, int len)
+{
+ PgStat_StatDBEntry *dbentry;
+ PgStat_StatTabEntry *tabentry;
+
+ dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
+
+ tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
+
+ tabentry->changes_since_analyze_reported += msg->m_changed_tuples_reported;
+}
+
+
/* ----------
* pgstat_recv_archiver() -
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index d029da5..a4d848b 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -280,8 +280,8 @@ extern Relation vacuum_open_relation(Oid relid, RangeVar *relation,
/* in commands/analyze.c */
extern void analyze_rel(Oid relid, RangeVar *relation,
- VacuumParams *params, List *va_cols, bool in_outer_xact,
- BufferAccessStrategy bstrategy);
+ VacuumParams *params, List *va_cols, Oid toprel_oid,
+ bool in_outer_xact, BufferAccessStrategy bstrategy);
extern bool std_typanalyze(VacAttrStats *stats);
/* in utils/misc/sampling.c --- duplicate of declarations in utils/sampling.h */
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 48a7ebf..8709a92 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -104,6 +104,6 @@ extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int location);
-extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols);
+extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols, Oid toprel_oid);
#endif /* MAKEFUNC_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7960cfe..7106457 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3317,6 +3317,9 @@ typedef struct VacuumRelation
RangeVar *relation; /* table name to process, or NULL */
Oid oid; /* table's OID; InvalidOid if not looked up */
List *va_cols; /* list of column names, or NIL for all */
+
+ /* top level table's OID for manual ANALYZE inheritance tree */
+ Oid toprel_oid;
} VacuumRelation;
/* ----------------------
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index d699502..d67e82d 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -11,6 +11,7 @@
#ifndef PGSTAT_H
#define PGSTAT_H
+#include "catalog/pg_class.h"
#include "datatype/timestamp.h"
#include "libpq/pqcomm.h"
#include "miscadmin.h"
@@ -70,6 +71,8 @@ typedef enum StatMsgType
PGSTAT_MTYPE_AUTOVAC_START,
PGSTAT_MTYPE_VACUUM,
PGSTAT_MTYPE_ANALYZE,
+ PGSTAT_MTYPE_PARTCHANGES,
+ PGSTAT_MTYPE_REPORTEDCHANGES,
PGSTAT_MTYPE_ARCHIVER,
PGSTAT_MTYPE_BGWRITER,
PGSTAT_MTYPE_WAL,
@@ -127,6 +130,7 @@ typedef struct PgStat_TableCounts
PgStat_Counter t_delta_live_tuples;
PgStat_Counter t_delta_dead_tuples;
PgStat_Counter t_changed_tuples;
+ PgStat_Counter t_changed_tuples_reported;
PgStat_Counter t_blocks_fetched;
PgStat_Counter t_blocks_hit;
@@ -430,6 +434,32 @@ typedef struct PgStat_MsgAnalyze
PgStat_Counter m_dead_tuples;
} PgStat_MsgAnalyze;
+/* ----------
+ * PgStat_MsgPartChanges Sent by the autovacuum deamon to propagate
+ * the changed_tuples counter.
+ * ----------
+ */
+typedef struct PgStat_MsgPartChanges
+{
+ PgStat_MsgHdr m_hdr;
+ Oid m_databaseid;
+ Oid m_tableoid;
+ PgStat_Counter m_changed_tuples;
+} PgStat_MsgPartChanges;
+
+/* ----------
+ * PgStat_MsgReportedChanges Sent by the autovacuum deamon to update
+ * changed_tuples_reported.
+ * ----------
+ */
+typedef struct PgStat_MsgReportedChanges
+{
+ PgStat_MsgHdr m_hdr;
+ Oid m_databaseid;
+ Oid m_tableoid;
+ PgStat_Counter m_changed_tuples_reported;
+} PgStat_MsgReportedChanges;
+
/* ----------
* PgStat_MsgArchiver Sent by the archiver to update statistics.
@@ -675,6 +705,8 @@ typedef union PgStat_Msg
PgStat_MsgAutovacStart msg_autovacuum_start;
PgStat_MsgVacuum msg_vacuum;
PgStat_MsgAnalyze msg_analyze;
+ PgStat_MsgPartChanges msg_partchanges;
+ PgStat_MsgReportedChanges msg_reportedchanges;
PgStat_MsgArchiver msg_archiver;
PgStat_MsgBgWriter msg_bgwriter;
PgStat_MsgWal msg_wal;
@@ -770,6 +802,7 @@ typedef struct PgStat_StatTabEntry
PgStat_Counter n_live_tuples;
PgStat_Counter n_dead_tuples;
PgStat_Counter changes_since_analyze;
+ PgStat_Counter changes_since_analyze_reported;
PgStat_Counter inserts_since_vacuum;
PgStat_Counter blocks_fetched;
@@ -1444,7 +1477,12 @@ extern void pgstat_report_vacuum(Oid tableoid, bool shared,
PgStat_Counter livetuples, PgStat_Counter deadtuples);
extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
- bool resetcounter);
+ bool resetcounter, Oid toprel_oid);
+
+extern void pgstat_report_partchanges(Relation rel, PgStat_Counter changes_tuples);
+extern void pgstat_report_reportedchanges(Relation rel, PgStat_Counter changes_tuples_reported);
+extern void pgstat_propagate_changes(Form_pg_class classForm, PgStat_StatTabEntry *tabentry,
+ Oid toprel_oid, bool *updated);
extern void pgstat_report_recovery_conflict(int reason);
extern void pgstat_report_deadlock(void);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 9b59a7b..954afb9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1806,7 +1806,7 @@ pg_stat_all_tables| SELECT c.oid AS relid,
FROM ((pg_class c
LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
- WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"]))
+ WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"]))
GROUP BY c.oid, n.nspname, c.relname;
pg_stat_archiver| SELECT s.archived_count,
s.last_archived_wal,
@@ -2209,7 +2209,7 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid,
FROM ((pg_class c
LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
- WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"]))
+ WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"]))
GROUP BY c.oid, n.nspname, c.relname;
pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid,
pg_stat_xact_all_tables.schemaname,