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.
    </para>
-
-   <para>
-    Note that operations that rewrite the table, such as <command>VACUUM
-    FULL</command>, <command>CLUSTER</command>, and some versions of
-    <command>ALTER TABLE</command> also reset the visibility map.
-    It is recommended to always issue a standard <command>VACUUM</command>
-    after such commands to rebuild it.
-   </para>
   </sect2>
 
   <sect2 id="vacuum-for-wraparound">
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);
