diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml index 4881f18..7fd7f46 100644 --- a/doc/src/sgml/maintenance.sgml +++ b/doc/src/sgml/maintenance.sgml @@ -373,14 +373,6 @@ The visibility map is vastly smaller than the heap, so it can easily be cached even when the heap is very large. - - - Note that operations that rewrite the table, such as VACUUM - FULL, CLUSTER, and some versions of - ALTER TABLE also reset the visibility map. - It is recommended to always issue a standard VACUUM - after such commands to rebuild it. - diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 43bbd90..7663108 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -38,6 +38,7 @@ #include "commands/vacuum.h" #include "miscadmin.h" #include "optimizer/planner.h" +#include "pgstat.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/predicate.h" @@ -1096,6 +1097,12 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, tups_recently_dead, pg_rusage_show(&ru0)))); + /* Send statistics */ + pgstat_report_table_rewrite(OIDOldHeap, + OldHeap->rd_rel->relisshared, + num_tuples, + false); + /* Clean up */ pfree(values); pfree(isnull); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..cad7504 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -317,7 +317,7 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode); static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode); -static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode); +static double ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode); static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); static void ATSimplePermissions(Relation rel, int allowed_targets); static void ATWrongRelkindError(Relation rel, int allowed_targets); @@ -3770,6 +3770,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode) Oid OIDNewHeap; Oid NewTableSpace; char persistence; + double num_tuples; OldHeap = heap_open(tab->relid, NoLock); @@ -3855,7 +3856,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode) * modifications, and test the current data within the table * against new constraints generated by ALTER TABLE commands. */ - ATRewriteTable(tab, OIDNewHeap, lockmode); + num_tuples = ATRewriteTable(tab, OIDNewHeap, lockmode); /* * Swap the physical files of the old and new heaps, then rebuild @@ -3871,6 +3872,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode) RecentXmin, ReadNextMultiXactId(), persistence); + + /* Send statistics */ + pgstat_report_table_rewrite(tab->relid, false, num_tuples, true); } else { @@ -3942,8 +3946,10 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode) * ATRewriteTable: scan or rewrite one table * * OIDNewHeap is InvalidOid if we don't need to rewrite + * + * The return value is the number of rows rewritten */ -static void +static double ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { Relation oldrel; @@ -3958,6 +3964,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) CommandId mycid; BulkInsertState bistate; int hi_options; + double num_tuples = 0; /* * Open the relation(s). We have surely already locked the existing @@ -4213,7 +4220,10 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) /* Write the tuple out to the new relation */ if (newrel) + { heap_insert(newrel, tuple, mycid, hi_options, bistate); + num_tuples += 1; + } ResetExprContext(econtext); @@ -4241,6 +4251,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) heap_close(newrel, NoLock); } + + return num_tuples; } /* diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 169be3f..72f17c3 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -286,6 +286,7 @@ static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, in 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_table_rewrite(PgStat_MsgTableRewrite *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_funcstat(PgStat_MsgFuncstat *msg, int len); @@ -1393,6 +1394,33 @@ pgstat_report_analyze(Relation rel, } /* -------- + * pgstat_report_table_rewrite() - + * + * Tell the collector about a table rewrite, due to CLUSTER, VACUUM FULL, or + * ALTER TABLE. + * + * Caller must provide the number of rows that have been rewritten in hopes of + * triggering a VACUUM, and also whether to try triggering an ANALYZE. + * -------- + */ +void +pgstat_report_table_rewrite(Oid tableoid, bool shared, double rows_rewritten, + bool trigger_analyze) +{ + PgStat_MsgTableRewrite msg; + + if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts) + return; + + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABLEREWRITE); + msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; + msg.m_tableoid = tableoid; + msg.m_rows_rewritten = rows_rewritten; + msg.m_trigger_analyze = trigger_analyze; + pgstat_send(&msg, sizeof(msg)); +} + +/* -------- * pgstat_report_recovery_conflict() - * * Tell the collector about a Hot Standby recovery conflict. @@ -3644,6 +3672,10 @@ PgstatCollectorMain(int argc, char *argv[]) pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, len); break; + case PGSTAT_MTYPE_TABLEREWRITE: + pgstat_recv_table_rewrite((PgStat_MsgTableRewrite *) &msg, len); + break; + case PGSTAT_MTYPE_ARCHIVER: pgstat_recv_archiver((PgStat_MsgArchiver *) &msg, len); break; @@ -5297,6 +5329,32 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) } } +/* ---------- + * pgstat_recv_table_rewrite() - + * + * Process a table rewrite message. + * ---------- + */ +static void +pgstat_recv_table_rewrite(PgStat_MsgTableRewrite *msg, int len) +{ + PgStat_StatDBEntry *dbentry; + PgStat_StatTabEntry *tabentry; + + /* + * Store the data in the table's hashtable entry. + */ + dbentry = pgstat_get_db_entry(msg->m_databaseid, true); + + tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true); + + tabentry->n_live_tuples = msg->m_rows_rewritten; + tabentry->n_dead_tuples = 0; + tabentry->inserts_since_vacuum = msg->m_rows_rewritten; + if (msg->m_trigger_analyze) + tabentry->changes_since_analyze = msg->m_rows_rewritten; +} + /* ---------- * pgstat_recv_archiver() - diff --git a/src/include/pgstat.h b/src/include/pgstat.h index d491c71..1731fb4 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -58,6 +58,7 @@ typedef enum StatMsgType PGSTAT_MTYPE_AUTOVAC_START, PGSTAT_MTYPE_VACUUM, PGSTAT_MTYPE_ANALYZE, + PGSTAT_MTYPE_TABLEREWRITE, PGSTAT_MTYPE_ARCHIVER, PGSTAT_MTYPE_BGWRITER, PGSTAT_MTYPE_FUNCSTAT, @@ -391,6 +392,22 @@ typedef struct PgStat_MsgAnalyze /* ---------- + * PgStat_MsgTableRewrite Sent by the backend of a table rewrite, + * such as CLUSTER, VACUUM FULL, or + * ALTER TABLE + * ---------- + */ +typedef struct PgStat_MsgTableRewrite +{ + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + Oid m_tableoid; + double m_rows_rewritten; + bool m_trigger_analyze; +} PgStat_MsgTableRewrite; + + +/* ---------- * PgStat_MsgArchiver Sent by the archiver to update statistics. * ---------- */ @@ -974,6 +991,8 @@ extern void pgstat_report_vacuum(Oid tableoid, bool shared, extern void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter); +extern void pgstat_report_table_rewrite(Oid tableoid, bool shared, + double rows_rewritten, bool trigger_analyze); extern void pgstat_report_recovery_conflict(int reason); extern void pgstat_report_deadlock(void);