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);