At Thu, 26 Oct 2017 15:06:30 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI 
<horiguchi.kyot...@lab.ntt.co.jp> wrote in 
<20171026.150630.115694437.horiguchi.kyot...@lab.ntt.co.jp>
> At Fri, 20 Oct 2017 19:15:16 +0900, Masahiko Sawada <sawada.m...@gmail.com> 
> wrote in <CAD21AoAkaw-u0feAVN_VrKZA5tvzp7jT=mqcqp-svmegkxh...@mail.gmail.com>
> > >   n_mod_since_analyze          | 20000
> > > + vacuum_requred               | true
> > > + last_vacuum_oldest_xid       | 8023
> > > + last_vacuum_left_to_truncate | 5123
> > > + last_vacuum_truncated        | 387
> > >   last_vacuum                  | 2017-10-10 17:21:54.380805+09
> > >   last_autovacuum              | 2017-10-10 17:21:54.380805+09
> > > + last_autovacuum_status       | Killed by lock conflict
> > > ...
> > >   autovacuum_count             | 128
> > > + incomplete_autovacuum_count  | 53
> > >
> > > # The last one might be needless..
> > 
> > I'm not sure that the above informations will help for users or DBA
> > but personally I sometimes want to have the number of index scans of
> > the last autovacuum in the pg_stat_user_tables view. That value
> > indicates how efficiently vacuums performed and would be a signal to
> > increase the setting of autovacuum_work_mem for user.
> 
> Btree and all existing index AMs (except brin) seem to visit the
> all pages in every index scan so it would be valuable. Instead
> the number of visited index pages during a table scan might be
> usable. It is more relevant to performance than the number of
> scans, on the other hand it is a bit difficult to get something
> worth from the number in a moment. I'll show the number of scans
> in the first cut.
> 
> > > Where the "Killed by lock conflict" is one of the followings.
> > >
> > >    - Completed
> > >    - Truncation skipped
> > >    - Partially truncated
> > >    - Skipped
> > >    - Killed by lock conflict
> > >
> > > This seems enough to find the cause of a table bloat. The same
> > > discussion could be applied to analyze but it might be the
> > > another issue.
> > >
> > > There may be a better way to indicate the vacuum soundness. Any
> > > opinions and suggestions are welcome.
> > >
> > > I'm going to make a patch to do the 'formal' one for the time
> > > being.

Done with small modifications. In the attached patch
pg_stat_all_tables has the following new columns. Documentations
is not provided at this stage.

-----
  n_mod_since_analyze     | 0
+ vacuum_required         | not requried
  last_vacuum             | 
  last_autovacuum         | 2017-10-30 18:51:32.060551+09
  last_analyze            | 
  last_autoanalyze        | 2017-10-30 18:48:33.414711+09
  vacuum_count            | 0
+ last_vacuum_truncated   | 0
+ last_vacuum_untruncated | 0
+ last_vacuum_index_scans | 0
+ last_vacuum_oldest_xmin | 2134
+ last_vacuum_status      | agressive vacuum completed
+ autovacuum_fail_count   | 0
  autovacuum_count        | 5
  analyze_count           | 0
  autoanalyze_count       | 1
-----
Where each column shows the following infomation.

+ vacuum_required         | not requried

 VACUUM requirement status. Takes the following values.

  - partial
    Partial (or normal) will be performed by the next autovacuum.
    The word "partial" is taken from the comment for
    vacuum_set_xid_limits.

  - aggressive
    Aggressive scan will be performed by the next autovacuum.

  - required
    Any type of autovacuum will be performed. The type of scan is
    unknown because the view failed to take the required lock on
    the table. (AutoVacuumrequirement())

  - not required
    Next autovacuum won't perform scan on this relation.

  - not required (lock not acquired)

    Autovacuum should be disabled and the distance to
    freeze-limit is not known because required lock is not
    available.

  - close to freeze-limit xid
    Shown while autovacuum is disabled. The table is in the
    manual vacuum window to avoid anti-wraparound autovacuum.

+ last_vacuum_truncated | 0

  The number of truncated pages in the last completed
  (auto)vacuum.

+ last_vacuum_untruncated | 0
  The number of pages the last completed (auto)vacuum tried to
  truncate but could not for some reason.

+ last_vacuum_index_scans | 0
  The number of index scans performed in the last completed
  (auto)vacuum.

+ last_vacuum_oldest_xmin | 2134
  The oldest xmin used in the last completed (auto)vacuum.

+ last_vacuum_status      | agressive vacuum completed

  The finish status of the last vacuum. Takes the following
  values. (pg_stat_get_last_vacuum_status())

   - completed
     The last partial (auto)vacuum is completed.

   - vacuum full completed
     The last VACUUM FULL is completed.

   - aggressive vacuum completed
     The last aggressive (auto)vacuum is completed.

   - error while $progress
     The last vacuum stopped by error while $progress.
     The $progress one of the vacuum progress phases.

   - canceled while $progress
     The last vacuum was canceled while $progress

     This is caused by user cancellation of manual vacuum or
     killed by another backend who wants to acquire lock on the
     relation.

   - skipped - lock unavailable
     The last autovacuum on the relation was skipped because
     required lock was not available.

   - unvacuumable
     A past autovacuum tried vacuum on the relation but it is not
     vacuumable for reasons of ownership or accessibility problem.
     (Such relations are not shown in pg_stat_all_tables..)

+ autovacuum_fail_count   | 0
  The number of successive failure of vacuum on the relation.
  Reset to zero by completed vacuum.

======

In the patch, vacrelstats if pointed from a static variable and
cancel reporting is performed in PG_CATCH() section in vacuum().
Every unthrown error like lock acquisition failure is reported by
explicit pgstat_report_vacuum() with the corresponding finish
code.

Vacuum requirement status is calculated in AutoVacuumRequirment()
and returned as a string. Access share lock on the target
relation is required but it returns only available values if the
lock is not available. I decided to return incomplete (coarse
grained) result than wait for a lock that isn't known to be
relased in a short time for a perfect result.


regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
>From 336748b61559bee66328a241394b365ebaacba6a Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyot...@lab.ntt.co.jp>
Date: Fri, 27 Oct 2017 17:36:12 +0900
Subject: [PATCH] Add several vacuum information in pg_stat_*_tables.

---
 src/backend/catalog/system_views.sql |   7 ++
 src/backend/commands/cluster.c       |   2 +-
 src/backend/commands/vacuum.c        | 105 ++++++++++++++++++++++--
 src/backend/commands/vacuumlazy.c    | 103 +++++++++++++++++++++---
 src/backend/postmaster/autovacuum.c  | 115 ++++++++++++++++++++++++++
 src/backend/postmaster/pgstat.c      |  80 +++++++++++++++---
 src/backend/utils/adt/pgstatfuncs.c  | 152 ++++++++++++++++++++++++++++++++++-
 src/include/catalog/pg_proc.h        |  14 ++++
 src/include/commands/vacuum.h        |   4 +-
 src/include/pgstat.h                 |  38 ++++++++-
 src/include/postmaster/autovacuum.h  |   1 +
 src/test/regress/expected/rules.out  |  21 +++++
 12 files changed, 606 insertions(+), 36 deletions(-)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index dc40cde..452bf5d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -523,11 +523,18 @@ CREATE VIEW pg_stat_all_tables AS
             pg_stat_get_live_tuples(C.oid) AS n_live_tup,
             pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
             pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
+			pg_stat_get_vacuum_necessity(C.oid) AS vacuum_required,
             pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
             pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
             pg_stat_get_last_analyze_time(C.oid) as last_analyze,
             pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
             pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
+			pg_stat_get_last_vacuum_truncated(C.oid) AS last_vacuum_truncated,
+			pg_stat_get_last_vacuum_untruncated(C.oid) AS last_vacuum_untruncated,
+			pg_stat_get_last_vacuum_index_scans(C.oid) AS last_vacuum_index_scans,
+			pg_stat_get_last_vacuum_oldest_xmin(C.oid) AS last_vacuum_oldest_xmin,
+			pg_stat_get_last_vacuum_status(C.oid) AS last_vacuum_status,
+			pg_stat_get_autovacuum_fail_count(C.oid) AS autovacuum_fail_count,
             pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
             pg_stat_get_analyze_count(C.oid) AS analyze_count,
             pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e..403b76d 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -850,7 +850,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
 	 */
 	vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0,
 						  &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
-						  NULL);
+						  NULL, NULL, NULL);
 
 	/*
 	 * FreezeXid will become the table's new relfrozenxid, and that mustn't go
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index cbd6e9b..a0c5a12 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -35,6 +35,7 @@
 #include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_namespace.h"
 #include "commands/cluster.h"
+#include "commands/progress.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -367,6 +368,9 @@ vacuum(int options, List *relations, VacuumParams *params,
 	}
 	PG_CATCH();
 	{
+		/* report the final status of this vacuum */
+		lazy_vacuum_cancel_handler();
+
 		in_vacuum = false;
 		VacuumCostActive = false;
 		PG_RE_THROW();
@@ -585,6 +589,10 @@ get_all_vacuum_rels(void)
  *	 Xmax.
  * - mxactFullScanLimit is a value against which a table's relminmxid value is
  *	 compared to produce a full-table vacuum, as with xidFullScanLimit.
+ * - aggressive is set if it is not NULL and set true if the table needs
+ *   aggressive scan.
+ * - close_to_wrap_around_limit is set if it is not NULL and set true if it is
+ *   in anti-anti-wraparound window.
  *
  * xidFullScanLimit and mxactFullScanLimit can be passed as NULL if caller is
  * not interested.
@@ -599,9 +607,11 @@ vacuum_set_xid_limits(Relation rel,
 					  TransactionId *freezeLimit,
 					  TransactionId *xidFullScanLimit,
 					  MultiXactId *multiXactCutoff,
-					  MultiXactId *mxactFullScanLimit)
+					  MultiXactId *mxactFullScanLimit,
+					  bool *aggressive, bool *close_to_wrap_around_limit)
 {
 	int			freezemin;
+	int			freezemax;
 	int			mxid_freezemin;
 	int			effective_multixact_freeze_max_age;
 	TransactionId limit;
@@ -701,11 +711,13 @@ vacuum_set_xid_limits(Relation rel,
 
 	*multiXactCutoff = mxactLimit;
 
-	if (xidFullScanLimit != NULL)
+	if (xidFullScanLimit != NULL || aggressive != NULL)
 	{
 		int			freezetable;
+		bool		maybe_anti_wrapround = false;
 
-		Assert(mxactFullScanLimit != NULL);
+		/* these two output should be requested together  */
+		Assert(xidFullScanLimit == NULL || mxactFullScanLimit != NULL);
 
 		/*
 		 * Determine the table freeze age to use: as specified by the caller,
@@ -717,7 +729,14 @@ vacuum_set_xid_limits(Relation rel,
 		freezetable = freeze_table_age;
 		if (freezetable < 0)
 			freezetable = vacuum_freeze_table_age;
-		freezetable = Min(freezetable, autovacuum_freeze_max_age * 0.95);
+
+		freezemax = autovacuum_freeze_max_age * 0.95;
+		if (freezemax < freezetable)
+		{
+			/* We may be in anti-anti-warparound window */
+			freezetable = freezemax;
+			maybe_anti_wrapround = true;
+		}
 		Assert(freezetable >= 0);
 
 		/*
@@ -728,7 +747,8 @@ vacuum_set_xid_limits(Relation rel,
 		if (!TransactionIdIsNormal(limit))
 			limit = FirstNormalTransactionId;
 
-		*xidFullScanLimit = limit;
+		if (xidFullScanLimit)
+			*xidFullScanLimit = limit;
 
 		/*
 		 * Similar to the above, determine the table freeze age to use for
@@ -741,10 +761,20 @@ vacuum_set_xid_limits(Relation rel,
 		freezetable = multixact_freeze_table_age;
 		if (freezetable < 0)
 			freezetable = vacuum_multixact_freeze_table_age;
-		freezetable = Min(freezetable,
-						  effective_multixact_freeze_max_age * 0.95);
+
+		freezemax = effective_multixact_freeze_max_age * 0.95;
+		if (freezemax < freezetable)
+		{
+			/* We may be in anti-anti-warparound window */
+			freezetable = freezemax;
+			maybe_anti_wrapround = true;
+		}
 		Assert(freezetable >= 0);
 
+		/* We may be in anti-anti-warparound window */
+		if (effective_multixact_freeze_max_age * 0.95 < freezetable)
+			maybe_anti_wrapround = true;
+
 		/*
 		 * Compute MultiXact limit causing a full-table vacuum, being careful
 		 * to generate a valid MultiXact value.
@@ -753,11 +783,38 @@ vacuum_set_xid_limits(Relation rel,
 		if (mxactLimit < FirstMultiXactId)
 			mxactLimit = FirstMultiXactId;
 
-		*mxactFullScanLimit = mxactLimit;
+		if (mxactFullScanLimit)
+			*mxactFullScanLimit = mxactLimit;
+
+		/*
+		 * We request an aggressive scan if the table's frozen Xid is now
+		 * older than or equal to the requested Xid full-table scan limit; or
+		 * if the table's minimum MultiXactId is older than or equal to the
+		 * requested mxid full-table scan limit.
+		 */
+		if (aggressive)
+		{
+			*aggressive =
+				TransactionIdPrecedesOrEquals(rel->rd_rel->relfrozenxid,
+											  limit);
+			*aggressive |=
+				MultiXactIdPrecedesOrEquals(rel->rd_rel->relminmxid,
+											mxactLimit);
+
+			/* set close_to_wrap_around_limit if requested */
+			if (close_to_wrap_around_limit)
+				*close_to_wrap_around_limit =
+					(*aggressive && maybe_anti_wrapround);
+		}
+		else
+		{
+			Assert (!close_to_wrap_around_limit);
+		}
 	}
 	else
 	{
 		Assert(mxactFullScanLimit == NULL);
+		Assert(aggressive == NULL);
 	}
 }
 
@@ -1410,6 +1467,9 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 
 	if (!onerel)
 	{
+		pgstat_report_vacuum(relid, false,
+							 0, 0, 0, 0, 0, PGSTAT_VACUUM_SKIP_LOCK_FAILED,
+							 InvalidTransactionId, 0, 0);
 		PopActiveSnapshot();
 		CommitTransactionCommand();
 		return false;
@@ -1441,6 +1501,12 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 					(errmsg("skipping \"%s\" --- only table or database owner can vacuum it",
 							RelationGetRelationName(onerel))));
 		relation_close(onerel, lmode);
+
+		pgstat_report_vacuum(RelationGetRelid(onerel),
+							 onerel->rd_rel->relisshared,
+							 0, 0, 0, 0, 0, PGSTAT_VACUUM_SKIP_NONTARGET,
+							 InvalidTransactionId, 0, 0);
+
 		PopActiveSnapshot();
 		CommitTransactionCommand();
 		return false;
@@ -1458,6 +1524,12 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 				(errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
 						RelationGetRelationName(onerel))));
 		relation_close(onerel, lmode);
+
+		pgstat_report_vacuum(RelationGetRelid(onerel),
+							 onerel->rd_rel->relisshared,
+							 0, 0, 0, 0, 0, PGSTAT_VACUUM_SKIP_NONTARGET,
+							 InvalidTransactionId, 0, 0);
+
 		PopActiveSnapshot();
 		CommitTransactionCommand();
 		return false;
@@ -1473,6 +1545,12 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	if (RELATION_IS_OTHER_TEMP(onerel))
 	{
 		relation_close(onerel, lmode);
+
+		pgstat_report_vacuum(RelationGetRelid(onerel),
+							 onerel->rd_rel->relisshared,
+							 0, 0, 0, 0, 0, PGSTAT_VACUUM_SKIP_NONTARGET,
+							 InvalidTransactionId, 0, 0);
+
 		PopActiveSnapshot();
 		CommitTransactionCommand();
 		return false;
@@ -1486,6 +1564,12 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
 		relation_close(onerel, lmode);
+
+		pgstat_report_vacuum(RelationGetRelid(onerel),
+							 onerel->rd_rel->relisshared,
+							 0, 0, 0, 0, 0, PGSTAT_VACUUM_SKIP_NONTARGET,
+							 InvalidTransactionId, 0, 0);
+
 		PopActiveSnapshot();
 		CommitTransactionCommand();
 		/* It's OK to proceed with ANALYZE on this table */
@@ -1531,6 +1615,8 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	 */
 	if (options & VACOPT_FULL)
 	{
+		bool isshared = onerel->rd_rel->relisshared;
+
 		/* close relation before vacuuming, but hold lock until commit */
 		relation_close(onerel, NoLock);
 		onerel = NULL;
@@ -1538,6 +1624,9 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 		/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
 		cluster_rel(relid, InvalidOid, false,
 					(options & VACOPT_VERBOSE) != 0);
+		pgstat_report_vacuum(relid, isshared, 0, 0, 0, 0, 0,
+							 PGSTAT_VACUUM_FULL_FINISHED,
+							 InvalidTransactionId, 0, 0);
 	}
 	else
 		lazy_vacuum_rel(onerel, options, params, vac_strategy);
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 172d213..372d661 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -55,6 +55,7 @@
 #include "postmaster/autovacuum.h"
 #include "storage/bufmgr.h"
 #include "storage/freespace.h"
+#include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -105,6 +106,8 @@
 
 typedef struct LVRelStats
 {
+	Oid			reloid;			/* oid of the target relation */
+	bool		shared;			/* is shared relation? */
 	/* hasindex = true means two-pass strategy; false means one-pass */
 	bool		hasindex;
 	/* Overall statistics about rel */
@@ -119,6 +122,7 @@ typedef struct LVRelStats
 	double		new_rel_tuples; /* new estimated total # of tuples */
 	double		new_dead_tuples;	/* new estimated total # of dead tuples */
 	BlockNumber pages_removed;
+	BlockNumber pages_not_removed;
 	double		tuples_deleted;
 	BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
 	/* List of TIDs of tuples we intend to delete */
@@ -138,6 +142,7 @@ static int	elevel = -1;
 static TransactionId OldestXmin;
 static TransactionId FreezeLimit;
 static MultiXactId MultiXactCutoff;
+static LVRelStats *current_lvstats;
 
 static BufferAccessStrategy vac_strategy;
 
@@ -216,6 +221,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	else
 		elevel = DEBUG2;
 
+	current_lvstats = NULL;
 	pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM,
 								  RelationGetRelid(onerel));
 
@@ -227,29 +233,30 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 						  params->multixact_freeze_min_age,
 						  params->multixact_freeze_table_age,
 						  &OldestXmin, &FreezeLimit, &xidFullScanLimit,
-						  &MultiXactCutoff, &mxactFullScanLimit);
+						  &MultiXactCutoff, &mxactFullScanLimit,
+						  &aggressive, NULL);
 
-	/*
-	 * We request an aggressive scan if the table's frozen Xid is now older
-	 * than or equal to the requested Xid full-table scan limit; or if the
-	 * table's minimum MultiXactId is older than or equal to the requested
-	 * mxid full-table scan limit; or if DISABLE_PAGE_SKIPPING was specified.
-	 */
-	aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
-											   xidFullScanLimit);
-	aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
-											  mxactFullScanLimit);
+	/* force aggressive scan if DISABLE_PAGE_SKIPPING was specified */
 	if (options & VACOPT_DISABLE_PAGE_SKIPPING)
 		aggressive = true;
 
 	vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
 
+	vacrelstats->reloid = RelationGetRelid(onerel);
+	vacrelstats->shared = onerel->rd_rel->relisshared;
 	vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
 	vacrelstats->old_rel_tuples = onerel->rd_rel->reltuples;
 	vacrelstats->num_index_scans = 0;
 	vacrelstats->pages_removed = 0;
+	vacrelstats->pages_not_removed = 0;
 	vacrelstats->lock_waiter_detected = false;
 
+	/*
+	 * Register current vacrelstats so that final status can be reported on
+	 * interrupts
+	 */
+	current_lvstats = vacrelstats;
+
 	/* Open all indexes of the relation */
 	vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
 	vacrelstats->hasindex = (nindexes > 0);
@@ -280,8 +287,15 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	 * Optionally truncate the relation.
 	 */
 	if (should_attempt_truncation(vacrelstats))
+	{
 		lazy_truncate_heap(onerel, vacrelstats);
 
+		/* just paranoia */
+		if (vacrelstats->rel_pages >= vacrelstats->nonempty_pages)
+			vacrelstats->pages_not_removed +=
+				vacrelstats->rel_pages - vacrelstats->nonempty_pages;
+	}
+
 	/* Report that we are now doing final cleanup */
 	pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,
 								 PROGRESS_VACUUM_PHASE_FINAL_CLEANUP);
@@ -339,10 +353,22 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	if (new_live_tuples < 0)
 		new_live_tuples = 0;	/* just in case */
 
-	pgstat_report_vacuum(RelationGetRelid(onerel),
+	/* vacuum successfully finished. nothing to do on exit */
+	current_lvstats = NULL;
+
+	pgstat_report_vacuum(vacrelstats->reloid,
 						 onerel->rd_rel->relisshared,
 						 new_live_tuples,
-						 vacrelstats->new_dead_tuples);
+						 vacrelstats->new_dead_tuples,
+						 vacrelstats->pages_removed,
+						 vacrelstats->pages_not_removed,
+						 vacrelstats->num_index_scans,
+						 OldestXmin,
+						 aggressive ?
+						 PGSTAT_VACUUM_AGGRESSIVE_FINISHED :
+						 PGSTAT_VACUUM_FINISHED,
+						 0, 0);
+
 	pgstat_progress_end_command();
 
 	/* and log the action if appropriate */
@@ -2205,3 +2231,54 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
 
 	return all_visible;
 }
+
+/*
+ * lazy_vacuum_cancel_handler - report interrupted vacuum status
+ */
+void
+lazy_vacuum_cancel_handler(void)
+{
+	LVRelStats *stats = current_lvstats;
+	LocalPgBackendStatus *local_beentry;
+	PgBackendStatus *beentry;
+	int				phase;
+	int				err;
+
+	current_lvstats = NULL;
+
+	/* we have nothing to report */
+	if (!stats)
+		return;
+
+	/* get vacuum progress stored in backend status */
+	local_beentry = pgstat_fetch_stat_local_beentry(MyBackendId);
+	if (!local_beentry)
+		return;
+
+	beentry = &local_beentry->backendStatus;
+
+	Assert (beentry && beentry->st_progress_command == PROGRESS_COMMAND_VACUUM);
+
+	phase = beentry->st_progress_param[PROGRESS_VACUUM_PHASE];
+
+	/* we can reach here both on interrupt and error */
+	if (geterrcode() == ERRCODE_QUERY_CANCELED)
+		err = PGSTAT_VACUUM_CANCELED;
+	else
+		err = PGSTAT_VACUUM_ERROR;
+
+	/*
+	 * vacuum has been canceled, report stats numbers without normalization
+	 * here. (But currently they are not used.)
+	 */
+	pgstat_report_vacuum(stats->reloid,
+						 stats->shared,
+						 stats->new_rel_tuples,
+						 stats->new_dead_tuples,
+						 stats->pages_removed,
+						 stats->pages_not_removed,
+						 stats->num_index_scans,
+						 OldestXmin,
+						 err,
+						 phase, geterrcode());
+}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index c04c0b5..6c32d0b 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -831,6 +831,121 @@ shutdown:
 }
 
 /*
+ * Returns status string of auto vacuum on the relation
+ */
+char *
+AutoVacuumRequirement(Oid reloid)
+{
+	Relation classRel;
+	Relation rel;
+	TupleDesc	pg_class_desc;
+	HeapTuple tuple;
+	Form_pg_class classForm;
+	AutoVacOpts *relopts;
+	PgStat_StatTabEntry *tabentry;
+	PgStat_StatDBEntry *shared;
+	PgStat_StatDBEntry *dbentry;
+	int			effective_multixact_freeze_max_age;
+	bool		dovacuum;
+	bool		doanalyze;
+	bool		wraparound;
+	bool		aggressive;
+	bool		xid_calculated = false;
+	bool		in_anti_wa_window = false;
+	char	   *ret = "not requried";
+
+	/* Compute the multixact age for which freezing is urgent. */
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	/* Fetch the pgclass entry for this relation */
+	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", reloid);
+	classForm = (Form_pg_class) GETSTRUCT(tuple);
+
+	/* extract relopts for autovacuum */
+	classRel = heap_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = RelationGetDescr(classRel);
+	relopts = extract_autovac_opts(tuple, pg_class_desc);
+	heap_close(classRel, AccessShareLock);
+
+	/* Fetch the pgstat shared entry and entry for this database */
+	shared = pgstat_fetch_stat_dbentry(InvalidOid);
+	dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
+
+	/* Fetch the pgstat entry for this table */
+	tabentry = get_pgstat_tabentry_relid(reloid, classForm->relisshared,
+										 shared, dbentry);
+
+	/*
+	 * Check if the relation needs vacuum. This function is intended to
+	 * suggest aggresive vacuum for the last 5% window in
+	 * autovacuum_freeze_max_age so the variable wraparound is ignored
+	 * here. See vacuum_set_xid_limits for details.
+	 */
+	relation_needs_vacanalyze(reloid, relopts, classForm, tabentry,
+							  effective_multixact_freeze_max_age,
+							  &dovacuum, &doanalyze, &wraparound);
+	ReleaseSysCache(tuple);
+
+	/* get further information if needed */
+	rel = NULL;
+
+	/* don't get stuck with lock  */
+	if (ConditionalLockRelationOid(reloid, AccessShareLock))
+		rel = try_relation_open(reloid, NoLock);
+
+	if (rel)
+	{
+		TransactionId OldestXmin, FreezeLimit;
+		MultiXactId MultiXactCutoff;
+
+		vacuum_set_xid_limits(rel,
+							  vacuum_freeze_min_age,
+							  vacuum_freeze_table_age,
+							  vacuum_multixact_freeze_min_age,
+							  vacuum_multixact_freeze_table_age,
+							  &OldestXmin, &FreezeLimit, NULL,
+							  &MultiXactCutoff, NULL,
+							  &aggressive, &in_anti_wa_window);
+
+		xid_calculated = true;
+		relation_close(rel, AccessShareLock);
+	}
+
+	/* choose the proper message according to the calculation above */
+	if (xid_calculated)
+	{
+		if (dovacuum)
+		{
+			/* we don't care anti-wraparound if autovacuum is on */
+			if (aggressive)
+				ret = "aggressive";
+			else
+				ret = "partial";
+		}
+		else if (in_anti_wa_window)
+			ret = "close to freeze-limit xid";
+		/* otherwise just "not requried" */
+	}
+	else
+	{
+		/*
+		 * failed to compute xid limits. show less-grained messages. We can
+		 * use just "required" in the autovacuum case is enough to distinguish
+		 * from full-grained messages, but we require additional words in the
+		 * case where autovacuum is turned off.
+		 */
+		if (dovacuum)
+			ret = "required";
+		else
+			ret = "not required (lock not acquired)";
+	}
+
+	return ret;
+}
+
+/*
  * Determine the time to sleep, based on the database list.
  *
  * The "canlaunch" parameter indicates whether we can start a worker right now,
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 3a0b49c..721b172 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1403,7 +1403,13 @@ pgstat_report_autovac(Oid dboid)
  */
 void
 pgstat_report_vacuum(Oid tableoid, bool shared,
-					 PgStat_Counter livetuples, PgStat_Counter deadtuples)
+					 PgStat_Counter livetuples, PgStat_Counter deadtuples,
+					 PgStat_Counter pages_removed,
+					 PgStat_Counter pages_not_removed,
+					 PgStat_Counter num_index_scans,
+					 TransactionId	oldestxmin,
+					 PgStat_Counter status, PgStat_Counter last_phase,
+					 PgStat_Counter errcode)
 {
 	PgStat_MsgVacuum msg;
 
@@ -1417,6 +1423,13 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
 	msg.m_vacuumtime = GetCurrentTimestamp();
 	msg.m_live_tuples = livetuples;
 	msg.m_dead_tuples = deadtuples;
+	msg.m_pages_removed = pages_removed;
+	msg.m_pages_not_removed = pages_not_removed;
+	msg.m_num_index_scans = num_index_scans;
+	msg.m_oldest_xmin = oldestxmin;
+	msg.m_vacuum_status = status;
+	msg.m_vacuum_last_phase = last_phase;
+	msg.m_vacuum_errcode = errcode;
 	pgstat_send(&msg, sizeof(msg));
 }
 
@@ -4576,17 +4589,25 @@ pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create)
 	if (!found)
 	{
 		result->numscans = 0;
+
 		result->tuples_returned = 0;
 		result->tuples_fetched = 0;
 		result->tuples_inserted = 0;
 		result->tuples_updated = 0;
 		result->tuples_deleted = 0;
 		result->tuples_hot_updated = 0;
+
 		result->n_live_tuples = 0;
 		result->n_dead_tuples = 0;
 		result->changes_since_analyze = 0;
+		result->n_pages_removed = 0;
+		result->n_pages_not_removed = 0;
+		result->n_index_scans = 0;
+		result->oldest_xmin = InvalidTransactionId;
+
 		result->blocks_fetched = 0;
 		result->blocks_hit = 0;
+
 		result->vacuum_timestamp = 0;
 		result->vacuum_count = 0;
 		result->autovac_vacuum_timestamp = 0;
@@ -4595,6 +4616,11 @@ pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create)
 		result->analyze_count = 0;
 		result->autovac_analyze_timestamp = 0;
 		result->autovac_analyze_count = 0;
+
+		result->vacuum_status = 0;
+		result->vacuum_last_phase = 0;
+		result->vacuum_errcode = 0;
+		result->vacuum_failcount = 0;
 	}
 
 	return result;
@@ -5979,18 +6005,50 @@ pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len)
 
 	tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
 
-	tabentry->n_live_tuples = msg->m_live_tuples;
-	tabentry->n_dead_tuples = msg->m_dead_tuples;
+	tabentry->vacuum_status = msg->m_vacuum_status;
+	tabentry->vacuum_last_phase = msg->m_vacuum_last_phase;
+	tabentry->vacuum_errcode = msg->m_vacuum_errcode;
 
-	if (msg->m_autovacuum)
-	{
-		tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
-		tabentry->autovac_vacuum_count++;
-	}
-	else
+	/*
+	 * We store the numbers only when the vacuum has been completed. They
+	 * might be usable to find how much the stopped vacuum processed but we
+	 * choose not to show them rather than show bogus numbers.
+	 */
+	switch ((StatVacuumStatus)msg->m_vacuum_status)
 	{
-		tabentry->vacuum_timestamp = msg->m_vacuumtime;
-		tabentry->vacuum_count++;
+	case PGSTAT_VACUUM_FINISHED:
+	case PGSTAT_VACUUM_FULL_FINISHED:
+	case PGSTAT_VACUUM_AGGRESSIVE_FINISHED:
+		tabentry->n_live_tuples = msg->m_live_tuples;
+		tabentry->n_dead_tuples = msg->m_dead_tuples;
+		tabentry->n_pages_removed = msg->m_pages_removed;
+		tabentry->n_pages_not_removed = msg->m_pages_not_removed;
+		tabentry->n_index_scans = msg->m_num_index_scans;
+		tabentry->oldest_xmin = msg->m_oldest_xmin;
+		tabentry->vacuum_failcount = 0;
+
+		if (msg->m_autovacuum)
+		{
+			tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
+			tabentry->autovac_vacuum_count++;
+		}
+		else
+		{
+			tabentry->vacuum_timestamp = msg->m_vacuumtime;
+			tabentry->vacuum_count++;
+		}
+		break;
+
+	case PGSTAT_VACUUM_ERROR:
+	case PGSTAT_VACUUM_CANCELED:
+	case PGSTAT_VACUUM_SKIP_LOCK_FAILED:
+		tabentry->vacuum_failcount++;
+		break;
+
+	case PGSTAT_VACUUM_SKIP_NONTARGET:
+	default:
+		/* don't increment failure count for non-target tables */
+		break;
 	}
 }
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 8d9e7c1..bddc243 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -23,6 +23,7 @@
 #include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
+#include "postmaster/autovacuum.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/acl.h"
@@ -194,6 +195,156 @@ pg_stat_get_mod_since_analyze(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_vacuum_necessity(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+
+	PG_RETURN_TEXT_P(cstring_to_text(AutoVacuumRequirement(relid)));
+}
+
+Datum
+pg_stat_get_last_vacuum_truncated(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatTabEntry *tabentry;
+
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (tabentry->n_pages_removed);
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+pg_stat_get_last_vacuum_untruncated(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatTabEntry *tabentry;
+
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (tabentry->n_pages_not_removed);
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+pg_stat_get_last_vacuum_index_scans(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int32		result;
+	PgStat_StatTabEntry *tabentry;
+
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+		result = 0;
+	else
+		result = (int32) (tabentry->n_index_scans);
+
+	PG_RETURN_INT32(result);
+}
+
+Datum
+pg_stat_get_last_vacuum_oldest_xmin(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	TransactionId	result;
+	PgStat_StatTabEntry *tabentry;
+
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+		result = InvalidTransactionId;
+	else
+		result = (int32) (tabentry->oldest_xmin);
+
+	return TransactionIdGetDatum(result);
+}
+
+Datum
+pg_stat_get_last_vacuum_status(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char 	   *result = "unknown";
+	PgStat_StatTabEntry *tabentry;
+
+	/*
+	 * status string. this must be synced with the strings shown by the
+	 * statistics view "pg_stat_progress_vacuum"
+	 */
+	static char *phasestr[] =
+		{"initialization",
+		 "scanning heap",
+		 "vacuuming indexes",
+		 "vacuuming heap",
+		 "cleaning up indexes",
+		 "trucating heap",
+		 "performing final cleanup"};
+
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) != NULL)
+	{
+		int					phase;
+		StatVacuumStatus	status;
+
+		status = tabentry->vacuum_status;
+		switch (status)
+		{
+		case PGSTAT_VACUUM_FINISHED:
+			result = "completed";
+			break;
+		case PGSTAT_VACUUM_ERROR:
+		case PGSTAT_VACUUM_CANCELED:
+			phase = tabentry->vacuum_last_phase;
+			/* number of elements of phasestr above */
+			if (phase >= 0 && phase <= 7)
+				result = psprintf("%s while %s",
+								  status == PGSTAT_VACUUM_CANCELED ?
+								  "canceled" : "error",
+								  phasestr[phase]);
+			else
+				result = psprintf("unknown vacuum phase: %d", phase);
+			break;
+		case PGSTAT_VACUUM_SKIP_LOCK_FAILED:
+			result = "skipped - lock unavailable";
+			break;
+
+		case PGSTAT_VACUUM_AGGRESSIVE_FINISHED:
+			result = "aggressive vacuum completed";
+			break;
+
+		case PGSTAT_VACUUM_FULL_FINISHED:
+			result = "vacuum full completed";
+			break;
+
+		case PGSTAT_VACUUM_SKIP_NONTARGET:
+			result = "unvacuumable";
+			break;
+
+		default:
+			result = "unknown status";
+			break;
+		}
+	}
+
+	PG_RETURN_TEXT_P(cstring_to_text(result));
+}
+
+Datum
+pg_stat_get_autovacuum_fail_count(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatTabEntry *tabentry;
+
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+		result = 0;
+	else
+		result = (int32) (tabentry->vacuum_failcount);
+
+	PG_RETURN_INT32(result);
+}
 
 Datum
 pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS)
@@ -210,7 +361,6 @@ pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
-
 Datum
 pg_stat_get_blocks_hit(PG_FUNCTION_ARGS)
 {
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 93c031a..5a1c77d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2887,6 +2887,20 @@ DATA(insert OID = 3317 (  pg_stat_get_wal_receiver	PGNSP PGUID 12 1 0 0 0 f f f
 DESCR("statistics: information about WAL receiver");
 DATA(insert OID = 6118 (  pg_stat_get_subscription	PGNSP PGUID 12 1 0 0 0 f f f f f f s r 1 0 2249 "26" "{26,26,26,23,3220,1184,1184,3220,1184}" "{i,o,o,o,o,o,o,o,o}" "{subid,subid,relid,pid,received_lsn,last_msg_send_time,last_msg_receipt_time,latest_end_lsn,latest_end_time}" _null_ _null_ pg_stat_get_subscription _null_ _null_ _null_ ));
 DESCR("statistics: information about subscription");
+DATA(insert OID = 3419 (  pg_stat_get_vacuum_necessity	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_vacuum_necessity _null_ _null_ _null_ ));
+DESCR("statistics: true if needs vacuum");
+DATA(insert OID = 3420 (  pg_stat_get_last_vacuum_untruncated	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_last_vacuum_untruncated _null_ _null_ _null_ ));
+DESCR("statistics: pages left untruncated in the last vacuum");
+DATA(insert OID = 3421 (  pg_stat_get_last_vacuum_truncated	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_last_vacuum_truncated _null_ _null_ _null_ ));
+DESCR("statistics: pages truncated in the last vacuum");
+DATA(insert OID = 3422 (  pg_stat_get_last_vacuum_index_scans	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 23 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_last_vacuum_index_scans _null_ _null_ _null_ ));
+DESCR("statistics: number of index scans in the last vacuum");
+DATA(insert OID = 3423 (  pg_stat_get_last_vacuum_oldest_xmin	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 28 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_last_vacuum_oldest_xmin _null_ _null_ _null_ ));
+DESCR("statistics: The oldest xmin used in the last vacuum");
+DATA(insert OID = 3424 (  pg_stat_get_last_vacuum_status	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_last_vacuum_status _null_ _null_ _null_ ));
+DESCR("statistics: ending status of the last vacuum");
+DATA(insert OID = 3425 (  pg_stat_get_autovacuum_fail_count	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 23 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_autovacuum_fail_count _null_ _null_ _null_ ));
+DESCR("statistics: number of successively failed vacuum trials");
 DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 23 "" _null_ _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
 DESCR("statistics: current backend PID");
 DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 23 "23" _null_ _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 7a7b793..6091bab 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -182,13 +182,15 @@ extern void vacuum_set_xid_limits(Relation rel,
 					  TransactionId *freezeLimit,
 					  TransactionId *xidFullScanLimit,
 					  MultiXactId *multiXactCutoff,
-					  MultiXactId *mxactFullScanLimit);
+					  MultiXactId *mxactFullScanLimit,
+					  bool *aggressive, bool *in_wa_window);
 extern void vac_update_datfrozenxid(void);
 extern void vacuum_delay_point(void);
 
 /* in commands/vacuumlazy.c */
 extern void lazy_vacuum_rel(Relation onerel, int options,
 				VacuumParams *params, BufferAccessStrategy bstrategy);
+extern void lazy_vacuum_cancel_handler(void);
 
 /* in commands/analyze.c */
 extern void analyze_rel(Oid relid, RangeVar *relation, int options,
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 089b7c3..bab8332 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -67,6 +67,20 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_DEADLOCK
 } StatMsgType;
 
+/*
+ * The exit status stored in vacuum report.
+ */
+typedef enum StatVacuumStatus
+{
+	PGSTAT_VACUUM_FINISHED,
+	PGSTAT_VACUUM_CANCELED,
+	PGSTAT_VACUUM_ERROR,
+	PGSTAT_VACUUM_SKIP_LOCK_FAILED,
+	PGSTAT_VACUUM_SKIP_NONTARGET,
+	PGSTAT_VACUUM_AGGRESSIVE_FINISHED,
+	PGSTAT_VACUUM_FULL_FINISHED
+} StatVacuumStatus;
+
 /* ----------
  * The data type used for counters.
  * ----------
@@ -369,6 +383,13 @@ typedef struct PgStat_MsgVacuum
 	TimestampTz m_vacuumtime;
 	PgStat_Counter m_live_tuples;
 	PgStat_Counter m_dead_tuples;
+	PgStat_Counter m_pages_removed;
+	PgStat_Counter m_pages_not_removed;
+	PgStat_Counter m_num_index_scans;
+	TransactionId  m_oldest_xmin;
+	PgStat_Counter m_vacuum_status;
+	PgStat_Counter m_vacuum_last_phase;
+	PgStat_Counter m_vacuum_errcode;
 } PgStat_MsgVacuum;
 
 
@@ -629,6 +650,10 @@ typedef struct PgStat_StatTabEntry
 	PgStat_Counter n_live_tuples;
 	PgStat_Counter n_dead_tuples;
 	PgStat_Counter changes_since_analyze;
+	PgStat_Counter n_pages_removed;
+	PgStat_Counter n_pages_not_removed;
+	PgStat_Counter n_index_scans;
+	TransactionId  oldest_xmin;
 
 	PgStat_Counter blocks_fetched;
 	PgStat_Counter blocks_hit;
@@ -641,6 +666,11 @@ typedef struct PgStat_StatTabEntry
 	PgStat_Counter analyze_count;
 	TimestampTz autovac_analyze_timestamp;	/* autovacuum initiated */
 	PgStat_Counter autovac_analyze_count;
+
+	PgStat_Counter	vacuum_status;
+	PgStat_Counter	vacuum_last_phase;
+	PgStat_Counter	vacuum_errcode;
+	PgStat_Counter	vacuum_failcount;
 } PgStat_StatTabEntry;
 
 
@@ -1165,7 +1195,13 @@ extern void pgstat_reset_single_counter(Oid objectid, PgStat_Single_Reset_Type t
 
 extern void pgstat_report_autovac(Oid dboid);
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
-					 PgStat_Counter livetuples, PgStat_Counter deadtuples);
+					 PgStat_Counter livetuples, PgStat_Counter deadtuples,
+					 PgStat_Counter pages_removed,
+					 PgStat_Counter pages_not_removed,
+					 PgStat_Counter num_index_scans,
+					 TransactionId oldextxmin,
+					 PgStat_Counter status, PgStat_Counter last_phase,
+					 PgStat_Counter errcode);
 extern void pgstat_report_analyze(Relation rel,
 					  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 					  bool resetcounter);
diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h
index 3469915..848a322 100644
--- a/src/include/postmaster/autovacuum.h
+++ b/src/include/postmaster/autovacuum.h
@@ -49,6 +49,7 @@ extern int	Log_autovacuum_min_duration;
 extern bool AutoVacuumingActive(void);
 extern bool IsAutoVacuumLauncherProcess(void);
 extern bool IsAutoVacuumWorkerProcess(void);
+extern char *AutoVacuumRequirement(Oid reloid);
 
 #define IsAnyAutoVacuumProcess() \
 	(IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44..fb1ea49 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1759,11 +1759,18 @@ pg_stat_all_tables| SELECT c.oid AS relid,
     pg_stat_get_live_tuples(c.oid) AS n_live_tup,
     pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
     pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
+    pg_stat_get_vacuum_necessity(c.oid) AS vacuum_required,
     pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
     pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
     pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
     pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
     pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
+    pg_stat_get_last_vacuum_truncated(c.oid) AS last_vacuum_truncated,
+    pg_stat_get_last_vacuum_untruncated(c.oid) AS last_vacuum_untruncated,
+    pg_stat_get_last_vacuum_index_scans(c.oid) AS last_vacuum_index_scans,
+    pg_stat_get_last_vacuum_oldest_xmin(c.oid) AS last_vacuum_oldest_xmin,
+    pg_stat_get_last_vacuum_status(c.oid) AS last_vacuum_status,
+    pg_stat_get_autovacuum_fail_count(c.oid) AS autovacuum_fail_count,
     pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
     pg_stat_get_analyze_count(c.oid) AS analyze_count,
     pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count
@@ -1906,11 +1913,18 @@ pg_stat_sys_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.n_live_tup,
     pg_stat_all_tables.n_dead_tup,
     pg_stat_all_tables.n_mod_since_analyze,
+    pg_stat_all_tables.vacuum_required,
     pg_stat_all_tables.last_vacuum,
     pg_stat_all_tables.last_autovacuum,
     pg_stat_all_tables.last_analyze,
     pg_stat_all_tables.last_autoanalyze,
     pg_stat_all_tables.vacuum_count,
+    pg_stat_all_tables.last_vacuum_truncated,
+    pg_stat_all_tables.last_vacuum_untruncated,
+    pg_stat_all_tables.last_vacuum_index_scans,
+    pg_stat_all_tables.last_vacuum_oldest_xmin,
+    pg_stat_all_tables.last_vacuum_status,
+    pg_stat_all_tables.autovacuum_fail_count,
     pg_stat_all_tables.autovacuum_count,
     pg_stat_all_tables.analyze_count,
     pg_stat_all_tables.autoanalyze_count
@@ -1949,11 +1963,18 @@ pg_stat_user_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.n_live_tup,
     pg_stat_all_tables.n_dead_tup,
     pg_stat_all_tables.n_mod_since_analyze,
+    pg_stat_all_tables.vacuum_required,
     pg_stat_all_tables.last_vacuum,
     pg_stat_all_tables.last_autovacuum,
     pg_stat_all_tables.last_analyze,
     pg_stat_all_tables.last_autoanalyze,
     pg_stat_all_tables.vacuum_count,
+    pg_stat_all_tables.last_vacuum_truncated,
+    pg_stat_all_tables.last_vacuum_untruncated,
+    pg_stat_all_tables.last_vacuum_index_scans,
+    pg_stat_all_tables.last_vacuum_oldest_xmin,
+    pg_stat_all_tables.last_vacuum_status,
+    pg_stat_all_tables.autovacuum_fail_count,
     pg_stat_all_tables.autovacuum_count,
     pg_stat_all_tables.analyze_count,
     pg_stat_all_tables.autoanalyze_count
-- 
2.9.2

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to