diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index 94051aa..9891ebb 100644
*** a/src/backend/access/gin/ginxlog.c
--- b/src/backend/access/gin/ginxlog.c
*************** gin_desc(StringInfo buf, uint8 xl_info,
*** 779,784 ****
--- 779,823 ----
  }
  
  void
+ gin_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_GIN_CREATE_INDEX:
+ 			sprintf(buf, "Create index");
+ 			break;
+ 		case XLOG_GIN_CREATE_PTREE:
+ 			sprintf(buf, "Create posting tree");
+ 			break;
+ 		case XLOG_GIN_INSERT:
+ 			sprintf(buf, "Insert item");
+ 			break;
+ 		case XLOG_GIN_SPLIT:
+ 			sprintf(buf, "Page split");
+ 			break;
+ 		case XLOG_GIN_VACUUM_PAGE:
+ 			sprintf(buf, "Vacuum page");
+ 			break;
+ 		case XLOG_GIN_DELETE_PAGE:
+ 			sprintf(buf, "Delete page");
+ 			break;
+ 		case XLOG_GIN_UPDATE_META_PAGE:
+ 			sprintf(buf, "Update metapage");
+ 			break;
+ 		case XLOG_GIN_INSERT_LISTPAGE:
+ 			sprintf(buf, "Insert new list page");
+ 			break;
+ 		case XLOG_GIN_DELETE_LISTPAGE:
+ 			sprintf(buf, "Delete list pages");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN GIN INFO");
+ 	}
+ }
+ 
+ void
  gin_xlog_startup(void)
  {
  	incomplete_splits = NIL;
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
index 76029d9..397afaf 100644
*** a/src/backend/access/gist/gistxlog.c
--- b/src/backend/access/gist/gistxlog.c
*************** gist_desc(StringInfo buf, uint8 xl_info,
*** 394,399 ****
--- 394,424 ----
  }
  
  void
+ gist_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_GIST_PAGE_UPDATE:
+ 			sprintf(buf, "page_update");
+ 			break;
+ 		case XLOG_GIST_PAGE_DELETE:
+ 			sprintf(buf, "page_delete");
+ 			break;
+ 		case XLOG_GIST_PAGE_SPLIT:
+ 			sprintf(buf, "page_split");
+ 			break;
+ 		case XLOG_GIST_CREATE_INDEX:
+ 			sprintf(buf, "create_index");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN GIST INFO");
+ 			break;
+ 	}
+ }
+ 
+ void
  gist_xlog_startup(void)
  {
  	opCtx = createTempGistContext();
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 8802669..f82a20a 100644
*** a/src/backend/access/hash/hash.c
--- b/src/backend/access/hash/hash.c
*************** void
*** 717,719 ****
--- 717,724 ----
  hash_desc(StringInfo buf, uint8 xl_info, char *rec)
  {
  }
+ 
+ void
+ hash_short_desc(char buf[50], uint8 xl_info)
+ {
+ }
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 99a431a..60ac3b7 100644
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
*************** heap2_desc(StringInfo buf, uint8 xl_info
*** 5709,5714 ****
--- 5709,5788 ----
  		appendStringInfo(buf, "UNKNOWN");
  }
  
+ void
+ heap_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	info &= XLOG_HEAP_OPMASK;
+ 	switch (info)
+ 	{
+ 		case XLOG_HEAP_INSERT:
+ 			if (xl_info & XLOG_HEAP_INIT_PAGE)
+ 				sprintf(buf, "insert(init)");
+ 			else
+ 				sprintf(buf, "insert");
+ 			break;
+ 		case XLOG_HEAP_DELETE:
+ 			sprintf(buf, "delete");
+ 			break;
+ 		case XLOG_HEAP_UPDATE:
+ 			if (xl_info & XLOG_HEAP_INIT_PAGE)
+ 				sprintf(buf, "update(init)");
+ 			else
+ 				sprintf(buf, "update");
+ 			break;
+ 		case XLOG_HEAP_HOT_UPDATE:
+ 			if (xl_info & XLOG_HEAP_INIT_PAGE)		/* can this case happen? */
+ 				sprintf(buf, "hot_update(init)");
+ 			else
+ 				sprintf(buf, "hot_update");
+ 			break;
+ 		case XLOG_HEAP_NEWPAGE:
+ 			sprintf(buf, "newpage");
+ 			break;
+ 		case XLOG_HEAP_LOCK:
+ 			sprintf(buf, "lock");
+ 			break;
+ 		case XLOG_HEAP_INPLACE:
+ 			sprintf(buf, "inplace");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN HEAP INFO");
+ 	}
+ }
+ 
+ void
+ heap2_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	info &= XLOG_HEAP_OPMASK;
+ 	switch (info)
+ 	{
+ 		case XLOG_HEAP2_FREEZE:
+ 			sprintf(buf, "freeze");
+ 			break;
+ 		case XLOG_HEAP2_CLEAN:
+ 			sprintf(buf, "clean");
+ 			break;
+ 		case XLOG_HEAP2_CLEANUP_INFO:
+ 			sprintf(buf, "cleanup info");
+ 			break;	
+ 		case XLOG_HEAP2_VISIBLE:
+ 			sprintf(buf, "visible");
+ 			break;
+ 		case XLOG_HEAP2_MULTI_INSERT:
+ 			if (xl_info & XLOG_HEAP_INIT_PAGE)
+ 				sprintf(buf, "multi-insert (init)");
+ 			else
+ 				sprintf(buf, "multi-insert");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN HEAP2 INFO");
+ 	}
+ }
+ 
  /*
   *	heap_sync		- sync a heap, for use when no WAL has been written
   *
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index 0f5c113..4b40e86 100644
*** a/src/backend/access/nbtree/nbtxlog.c
--- b/src/backend/access/nbtree/nbtxlog.c
*************** btree_desc(StringInfo buf, uint8 xl_info
*** 1179,1184 ****
--- 1179,1267 ----
  }
  
  void
+ btree_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_BTREE_INSERT_LEAF:
+ 			{
+ 				sprintf(buf, "insert");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_INSERT_UPPER:
+ 			{
+ 				sprintf(buf, "insert_upper");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_INSERT_META:
+ 			{
+ 				sprintf(buf, "insert_meta");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_SPLIT_L:
+ 			{
+ 				sprintf(buf, "split_l");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_SPLIT_R:
+ 			{
+ 				sprintf(buf, "split_r");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_SPLIT_L_ROOT:
+ 			{
+ 				sprintf(buf, "split_l_root");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_SPLIT_R_ROOT:
+ 			{
+ 				sprintf(buf, "split_r_root");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_VACUUM:
+ 			{
+ 				sprintf(buf, "vacuum");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_DELETE:
+ 			{
+ 				sprintf(buf, "delete");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_DELETE_PAGE:
+ 			{
+ 				sprintf(buf, "delete_page");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_DELETE_PAGE_META:
+ 			{
+ 				sprintf(buf, "delete_page_meta");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_DELETE_PAGE_HALF:
+ 			{
+ 				sprintf(buf, "delete_page_half");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_NEWROOT:
+ 			{
+ 				sprintf(buf, "newroot");
+ 				break;
+ 			}
+ 		case XLOG_BTREE_REUSE_PAGE:
+ 			{
+ 				sprintf(buf, "reuse_page");
+ 				break;
+ 			}
+ 		default:
+ 			sprintf(buf, "UNKNOWN BTREE INFO");
+ 			break;
+ 	}
+ }
+ 
+ void
  btree_xlog_startup(void)
  {
  	incomplete_actions = NIL;
diff --git a/src/backend/access/spgist/spgxlog.c b/src/backend/access/spgist/spgxlog.c
index daa8ae3..fb0160d 100644
*** a/src/backend/access/spgist/spgxlog.c
--- b/src/backend/access/spgist/spgxlog.c
*************** spg_desc(StringInfo buf, uint8 xl_info,
*** 1053,1058 ****
--- 1053,1098 ----
  }
  
  void
+ spg_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_SPGIST_CREATE_INDEX:
+ 			sprintf(buf, "create_index");
+ 			break;
+ 		case XLOG_SPGIST_ADD_LEAF:
+ 			sprintf(buf, "add leaf");
+ 			break;
+ 		case XLOG_SPGIST_MOVE_LEAFS:
+ 			sprintf(buf, "move leafs");
+ 			break;
+ 		case XLOG_SPGIST_ADD_NODE:
+ 			sprintf(buf, "add node");
+ 			break;
+ 		case XLOG_SPGIST_SPLIT_TUPLE:
+ 			sprintf(buf, "split node");
+ 			break;
+ 		case XLOG_SPGIST_PICKSPLIT:
+ 			sprintf(buf, "split leaf page");
+ 			break;
+ 		case XLOG_SPGIST_VACUUM_LEAF:
+ 			sprintf(buf, "vacuum leaf tuples on page");
+ 			break;
+ 		case XLOG_SPGIST_VACUUM_ROOT:
+ 			sprintf(buf, "vacuum leaf tuples on root page");
+ 			break;
+ 		case XLOG_SPGIST_VACUUM_REDIRECT:
+ 			sprintf(buf, "vacuum redirect tuples on page");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN SPGIST INFO");
+ 			break;
+ 	}
+ }
+ 
+ void
  spg_xlog_startup(void)
  {
  	opCtx = AllocSetContextCreate(CurrentMemoryContext,
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 69b6ef3..c2366ee 100644
*** a/src/backend/access/transam/clog.c
--- b/src/backend/access/transam/clog.c
*************** clog_desc(StringInfo buf, uint8 xl_info,
*** 790,792 ****
--- 790,810 ----
  	else
  		appendStringInfo(buf, "UNKNOWN");
  }
+ 
+ void
+ clog_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case CLOG_ZEROPAGE:
+ 			sprintf(buf, "zeropage");
+ 			break;
+ 		case CLOG_TRUNCATE:
+ 			sprintf(buf, "truncate before");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN CLOG INFO");
+ 	}
+ }
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 454ca31..53b6552 100644
*** a/src/backend/access/transam/multixact.c
--- b/src/backend/access/transam/multixact.c
*************** multixact_desc(StringInfo buf, uint8 xl_
*** 2080,2082 ****
--- 2080,2103 ----
  	else
  		appendStringInfo(buf, "UNKNOWN");
  }
+ 
+ void
+ multixact_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_MULTIXACT_ZERO_OFF_PAGE:
+ 			sprintf(buf, "zero offsets page");
+ 			break;
+ 		case XLOG_MULTIXACT_ZERO_MEM_PAGE:
+ 			sprintf(buf, "zero members page");
+ 			break;
+ 		case XLOG_MULTIXACT_CREATE_ID:
+ 			sprintf(buf, "create multixact");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN MULTIXACT INFO");
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index ed8754e..1c27898 100644
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 26,46 ****
  
  
  const RmgrData RmgrTable[RM_MAX_ID + 1] = {
! 	{"XLOG", xlog_redo, xlog_desc, NULL, NULL, NULL},
! 	{"Transaction", xact_redo, xact_desc, NULL, NULL, NULL},
! 	{"Storage", smgr_redo, smgr_desc, NULL, NULL, NULL},
! 	{"CLOG", clog_redo, clog_desc, NULL, NULL, NULL},
! 	{"Database", dbase_redo, dbase_desc, NULL, NULL, NULL},
! 	{"Tablespace", tblspc_redo, tblspc_desc, NULL, NULL, NULL},
! 	{"MultiXact", multixact_redo, multixact_desc, NULL, NULL, NULL},
! 	{"RelMap", relmap_redo, relmap_desc, NULL, NULL, NULL},
! 	{"Standby", standby_redo, standby_desc, NULL, NULL, NULL},
! 	{"Heap2", heap2_redo, heap2_desc, NULL, NULL, NULL},
! 	{"Heap", heap_redo, heap_desc, NULL, NULL, NULL},
! 	{"Btree", btree_redo, btree_desc, btree_xlog_startup, btree_xlog_cleanup, btree_safe_restartpoint},
! 	{"Hash", hash_redo, hash_desc, NULL, NULL, NULL},
! 	{"Gin", gin_redo, gin_desc, gin_xlog_startup, gin_xlog_cleanup, gin_safe_restartpoint},
! 	{"Gist", gist_redo, gist_desc, gist_xlog_startup, gist_xlog_cleanup, NULL},
! 	{"Sequence", seq_redo, seq_desc, NULL, NULL, NULL},
! 	{"SPGist", spg_redo, spg_desc, spg_xlog_startup, spg_xlog_cleanup, NULL}
  };
--- 26,46 ----
  
  
  const RmgrData RmgrTable[RM_MAX_ID + 1] = {
! 	{"XLOG", xlog_redo, xlog_desc, xlog_short_desc, NULL, NULL, NULL},
! 	{"Transaction", xact_redo, xact_desc, xact_short_desc, NULL, NULL, NULL},
! 	{"Storage", smgr_redo, smgr_desc, smgr_short_desc, NULL, NULL, NULL},
! 	{"CLOG", clog_redo, clog_desc, clog_short_desc, NULL, NULL, NULL},
! 	{"Database", dbase_redo, dbase_desc, dbase_short_desc, NULL, NULL, NULL},
! 	{"Tablespace", tblspc_redo, tblspc_desc, tblspc_short_desc, NULL, NULL, NULL},
! 	{"MultiXact", multixact_redo, multixact_desc, multixact_short_desc, NULL, NULL, NULL},
! 	{"RelMap", relmap_redo, relmap_desc, relmap_short_desc, NULL, NULL, NULL},
! 	{"Standby", standby_redo, standby_desc, standby_short_desc, NULL, NULL, NULL},
! 	{"Heap2", heap2_redo, heap2_desc, heap2_short_desc, NULL, NULL, NULL},
! 	{"Heap", heap_redo, heap_desc, heap_short_desc, NULL, NULL, NULL},
! 	{"Btree", btree_redo, btree_desc, btree_short_desc, btree_xlog_startup, btree_xlog_cleanup, btree_safe_restartpoint},
! 	{"Hash", hash_redo, hash_desc, hash_short_desc, NULL, NULL, NULL},
! 	{"Gin", gin_redo, gin_desc, gin_short_desc, gin_xlog_startup, gin_xlog_cleanup, gin_safe_restartpoint},
! 	{"Gist", gist_redo, gist_desc, gist_short_desc, gist_xlog_startup, gist_xlog_cleanup, NULL},
! 	{"Sequence", seq_redo, seq_desc, seq_short_desc, NULL, NULL, NULL},
! 	{"SPGist", spg_redo, spg_desc, spg_short_desc, spg_xlog_startup, spg_xlog_cleanup, NULL}
  };
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index e22bdac..9fe744b 100644
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
*************** xact_desc(StringInfo buf, uint8 xl_info,
*** 4972,4974 ****
--- 4972,5007 ----
  	else
  		appendStringInfo(buf, "UNKNOWN");
  }
+ 
+ void
+ xact_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_XACT_COMMIT_COMPACT:
+ 			sprintf(buf, "commit compact");
+ 			break;
+ 		case XLOG_XACT_COMMIT:
+ 			sprintf(buf, "commit");
+ 			break;
+ 		case XLOG_XACT_ABORT:
+ 			sprintf(buf, "abort");
+ 			break;
+ 		case XLOG_XACT_PREPARE:
+ 			sprintf(buf, "prepare");
+ 			break;
+ 		case XLOG_XACT_COMMIT_PREPARED:
+ 			sprintf(buf, "commit prepared");
+ 			break;
+ 		case XLOG_XACT_ABORT_PREPARED:
+ 			sprintf(buf, "abort prepared");
+ 			break;
+ 		case XLOG_XACT_ASSIGNMENT:
+ 			sprintf(buf, "xid assignment xtop");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN XACT INFO");
+ 	}
+ }
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 4b273a8..b797ebd 100644
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
*************** StartupXLOG(void)
*** 6538,6543 ****
--- 6538,6545 ----
  			bool		recoveryPause = false;
  			ErrorContextCallback errcontext;
  			TimestampTz xtime;
+ 			TimestampTz before_ts = 0;   /* keep compiler quiet */
+ 			TimestampTz after_ts;
  
  			InRedo = true;
  
*************** StartupXLOG(void)
*** 6634,6639 ****
--- 6636,6644 ----
  					TransactionIdIsValid(record->xl_xid))
  					RecordKnownAssignedTransactionIds(record->xl_xid);
  
+ 				if (pgstat_track_recovery)
+ 					before_ts = GetCurrentTimestamp();
+ 
  				RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record);
  
  				/* Pop the error context stack */
*************** StartupXLOG(void)
*** 6668,6673 ****
--- 6673,6685 ----
  				xlogctl->recoveryLastRecPtr = EndRecPtr;
  				SpinLockRelease(&xlogctl->info_lck);
  
+ 				if (pgstat_track_recovery)
+ 				{
+ 					after_ts = GetCurrentTimestamp();
+ 
+ 					pgstat_report_recovery_stats(record, before_ts, after_ts);
+ 				}
+ 
  				LastRec = ReadRecPtr;
  
  				record = ReadRecord(NULL, LOG, false);
*************** xlog_desc(StringInfo buf, uint8 xl_info,
*** 8838,8843 ****
--- 8850,8891 ----
  		appendStringInfo(buf, "UNKNOWN");
  }
  
+ void
+ xlog_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_CHECKPOINT_SHUTDOWN:
+ 			sprintf(buf, "shutdown checkpoint");
+ 			break;
+ 		case XLOG_CHECKPOINT_ONLINE:
+ 			sprintf(buf, "online checkpoint");
+ 			break;
+ 		case XLOG_NOOP:
+ 			sprintf(buf, "xlog no-op");
+ 			break;
+ 		case XLOG_NEXTOID:
+ 			sprintf(buf, "nextOid");
+ 			break;
+ 		case XLOG_SWITCH:
+ 			sprintf(buf, "xlog switch");
+ 			break;
+ 		case XLOG_RESTORE_POINT:
+ 			sprintf(buf, "restore point");
+ 			break;
+ 		case XLOG_BACKUP_END:
+ 			sprintf(buf, "backup end");
+ 			break;
+ 		case XLOG_PARAMETER_CHANGE:
+ 			sprintf(buf, "parameter change");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN XLOG INFO");
+ 	}
+ }
+ 
  #ifdef WAL_DEBUG
  
  static void
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 2e10d4d..bd89e91 100644
*** a/src/backend/access/transam/xlogfuncs.c
--- b/src/backend/access/transam/xlogfuncs.c
***************
*** 23,28 ****
--- 23,29 ----
  #include "catalog/pg_type.h"
  #include "funcapi.h"
  #include "miscadmin.h"
+ #include "pgstat.h"
  #include "replication/walreceiver.h"
  #include "storage/smgr.h"
  #include "utils/builtins.h"
*************** pg_is_in_recovery(PG_FUNCTION_ARGS)
*** 465,467 ****
--- 466,556 ----
  {
  	PG_RETURN_BOOL(RecoveryInProgress());
  }
+ 
+ 
+ #define NUM_PG_STAT_RECOVERY_ATTS 11
+ Datum
+ pg_stat_get_recovery_activity(PG_FUNCTION_ARGS)
+ {
+ 	FuncCallContext *funcctx;
+ 
+ 	Datum		values[NUM_PG_STAT_RECOVERY_ATTS];
+ 	bool		nulls[NUM_PG_STAT_RECOVERY_ATTS];
+ 	HeapTuple	tuple;
+ 
+ 	PgStat_RecoveryStats *recoveryStats; 
+ 	PgStat_RecoveryStats *recoverystats_arr;
+ 
+ 	char 		info_string[50];
+ 
+     if (!RecoveryInProgress())
+         ereport(ERROR,
+                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                  errmsg("recovery is not in progress")));
+ 
+ 	/* stuff done only on the first call of the function */
+ 	if (SRF_IS_FIRSTCALL())
+ 	{
+ 		TupleDesc	tupdesc;
+ 		MemoryContext oldcontext;
+ 
+ 		/* create a function context for cross-call persistence */
+ 		funcctx = SRF_FIRSTCALL_INIT();
+ 
+ 		/*
+ 		 * switch to memory context appropriate for multiple function calls
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ 
+ 		tupdesc = CreateTemplateTupleDesc(NUM_PG_STAT_RECOVERY_ATTS, false);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  1, "rmgr", TEXTOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  2, "wal_record_type", TEXTOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  3, "n_records", INT4OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  4, "n_bkp_blocks", INT4OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  5, "tot_record_size", INT4OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  6, "max_record_size", INT4OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  7, "min_record_size", INT4OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  8, "tot_redo_time", FLOAT8OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber)  9, "max_redo_time", FLOAT8OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "min_redo_time", FLOAT8OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 11, "last_process_time", TIMESTAMPTZOID, -1, 0);
+ 		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+ 
+ 		recoveryStats = (PgStat_RecoveryStats *) palloc0(PGSTAT_NUM_RECOVERYSTAT_ENTRIES * sizeof(PgStat_RecoveryStats));
+ 
+ 		funcctx->max_calls = pgstat_fetch_recoverystats(recoveryStats);
+ 		funcctx->user_fctx = (void *) recoveryStats;
+ 
+ 		MemoryContextSwitchTo(oldcontext);
+ 	}
+ 
+ 	/* stuff done on every call of the function */
+ 	funcctx = SRF_PERCALL_SETUP();
+ 
+ 	recoverystats_arr = (PgStat_RecoveryStats *) funcctx->user_fctx;
+ 
+ 	MemSet(values, 0, sizeof(values));
+ 	MemSet(nulls, 0, sizeof(nulls));
+ 
+ 	if (funcctx->call_cntr < funcctx->max_calls && pgstat_track_recovery)
+ 	{
+ 		values[0]  = CStringGetTextDatum(RmgrTable[recoverystats_arr[funcctx->call_cntr].rmid].rm_name);
+  		RmgrTable[recoverystats_arr[funcctx->call_cntr].rmid].rm_short_desc(info_string, recoverystats_arr[funcctx->call_cntr].info);
+  		values[1]  = CStringGetTextDatum(info_string);
+ 		values[2]  = Int32GetDatum(recoverystats_arr[funcctx->call_cntr].n_records);
+ 		values[3]  = Int32GetDatum(recoverystats_arr[funcctx->call_cntr].n_bkp_blocks);
+ 		values[4]  = Int32GetDatum(recoverystats_arr[funcctx->call_cntr].tot_record_size);
+ 		values[5]  = Int32GetDatum(recoverystats_arr[funcctx->call_cntr].max_record_size);
+ 		values[6]  = Int32GetDatum(recoverystats_arr[funcctx->call_cntr].min_record_size);
+ 		values[7]  = Float8GetDatum(recoverystats_arr[funcctx->call_cntr].tot_redo_time);
+ 		values[8]  = Float8GetDatum(recoverystats_arr[funcctx->call_cntr].max_redo_time);
+ 		values[9]  = Float8GetDatum(recoverystats_arr[funcctx->call_cntr].min_redo_time);
+ 		values[10] = TimestampTzGetDatum(recoverystats_arr[funcctx->call_cntr].last_process_time);
+ 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+ 		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+ 	}
+ 	else
+ 	{
+ 		SRF_RETURN_DONE(funcctx);
+ 	}
+ }
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index a017101..6ceb93a 100644
*** a/src/backend/catalog/storage.c
--- b/src/backend/catalog/storage.c
*************** smgr_desc(StringInfo buf, uint8 xl_info,
*** 553,555 ****
--- 553,573 ----
  	else
  		appendStringInfo(buf, "UNKNOWN");
  }
+ 
+ void
+ smgr_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch(info)
+ 	{
+ 		case XLOG_SMGR_CREATE:
+ 			sprintf(buf, "file create");
+ 			break;
+ 		case XLOG_SMGR_TRUNCATE:
+ 			sprintf(buf, "file truncate");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN SMGR INFO");
+ 	}
+ }
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 30b0bd0..0a825ce 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_stat_database_conflicts A
*** 593,598 ****
--- 593,601 ----
              pg_stat_get_db_conflict_startup_deadlock(D.oid) AS confl_deadlock
      FROM pg_database D;
  
+ CREATE VIEW pg_stat_recovery AS
+     SELECT * FROM pg_stat_get_recovery_activity() AS R;
+ 
  CREATE VIEW pg_stat_user_functions AS
      SELECT
              P.oid AS funcid,
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 42a8b31..bae55aa 100644
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
*************** dbase_desc(StringInfo buf, uint8 xl_info
*** 2000,2002 ****
--- 2000,2020 ----
  	else
  		appendStringInfo(buf, "UNKNOWN");
  }
+ 
+ void
+ dbase_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_DBASE_CREATE:
+ 			sprintf(buf, "create db");
+ 			break;
+ 		case XLOG_DBASE_DROP:
+ 			sprintf(buf, "drop db");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN DBASE INFO");
+ 	}
+ }
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index d3739cb..0a13e62 100644
*** a/src/backend/commands/sequence.c
--- b/src/backend/commands/sequence.c
*************** seq_desc(StringInfo buf, uint8 xl_info,
*** 1572,1574 ****
--- 1572,1589 ----
  	appendStringInfo(buf, "rel %u/%u/%u",
  			   xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
  }
+ 
+ void
+ seq_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_SEQ_LOG:
+ 			sprintf(buf, "log");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN SEQ INFO");
+ 	}
+ }
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index ff58490..fcf8c90 100644
*** a/src/backend/commands/tablespace.c
--- b/src/backend/commands/tablespace.c
*************** tblspc_desc(StringInfo buf, uint8 xl_inf
*** 1471,1473 ****
--- 1471,1491 ----
  	else
  		appendStringInfo(buf, "UNKNOWN");
  }
+ 
+ void
+ tblspc_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_TBLSPC_CREATE:
+ 			sprintf(buf, "create tblspc");
+ 			break;
+ 		case XLOG_TBLSPC_DROP:
+ 			sprintf(buf, "drop tblspc");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN TBLSPC INFO");
+ 	}
+ }
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index a53fc52..34e9874 100644
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 41,46 ****
--- 41,47 ----
  #include "access/transam.h"
  #include "access/twophase_rmgr.h"
  #include "access/xact.h"
+ #include "access/xlog.h"
  #include "catalog/pg_database.h"
  #include "catalog/pg_proc.h"
  #include "libpq/ip.h"
***************
*** 108,113 ****
--- 109,115 ----
  #define PGSTAT_DB_HASH_SIZE		16
  #define PGSTAT_TAB_HASH_SIZE	512
  #define PGSTAT_FUNCTION_HASH_SIZE	512
+ #define PGSTAT_RECOVERY_HASH_SIZE	512
  
  
  /* ----------
***************
*** 116,121 ****
--- 118,124 ----
   */
  bool		pgstat_track_activities = false;
  bool		pgstat_track_counts = false;
+ bool		pgstat_track_recovery = false;
  int			pgstat_track_functions = TRACK_FUNC_OFF;
  int			pgstat_track_activity_query_size = 1024;
  
*************** static int	localNumBackends = 0;
*** 223,228 ****
--- 226,237 ----
   */
  static PgStat_GlobalStats globalStats;
  
+ /*
+  * Statistics about recovery, this is loaded as part of pgstat_read_statsfile()
+  */
+ static bool recoveryHashLoaded = false;
+ static HTAB *pgStatRecoveryHash = NULL;
+ 
  /* Last time the collector successfully wrote the stats file */
  static TimestampTz last_statwrite;
  
*************** static void pgstat_recv_bgwriter(PgStat_
*** 286,291 ****
--- 295,301 ----
  static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
  static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
  static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
+ static void pgstat_recv_recoverystat(PgStat_MsgRecoverystat *msg, int len);
  static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
  static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
  
*************** pgstat_report_recovery_conflict(int reas
*** 1341,1346 ****
--- 1351,1398 ----
  	pgstat_send(&msg, sizeof(msg));
  }
  
+ 
+ /* --------
+  * pgstat_report_recovery_stats() -
+  *
+  *	Tell the collector about recovery stats.
+  * --------
+  */
+ void
+ pgstat_report_recovery_stats(XLogRecord *record, TimestampTz before_ts, TimestampTz after_ts)
+ {
+ 	if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_recovery)
+ 		return;
+ 	else
+ 	{
+ 		PgStat_MsgRecoverystat msg;
+ 		int		i;
+ 
+ 		msg.rmid = record->xl_rmid;
+ 		msg.info = record->xl_info;
+ 		msg.record_size = record->xl_tot_len;
+ 
+ 		/* the time the last record started redo process at */ 
+ 		msg.process_time = before_ts;
+ 		msg.redo_time = (double) after_ts - (double) before_ts;
+ 
+ 		/* get the number of backup blocks in this record */
+ 		msg.n_bkp_blocks = 0; 
+ 		if (record->xl_info & XLR_BKP_BLOCK_MASK)
+ 		{
+ 			for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++)
+ 			{
+ 				if (!(record->xl_info & XLR_SET_BKP_BLOCK(i)))
+ 					msg.n_bkp_blocks += 1; 
+ 			}
+ 		}
+ 
+ 		pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RECOVERYSTAT);
+ 		pgstat_send(&msg, sizeof(msg));
+ 	}
+ }
+ 
+ 
  /* --------
   * pgstat_report_deadlock() -
   *
*************** pgstat_fetch_global(void)
*** 2260,2265 ****
--- 2312,2359 ----
  	return &globalStats;
  }
  
+ /*
+  * ---------
+  * pgstat_fetch_recoverystats() -
+  *
+  *	Support function for the SQL-callable pgstat* functions. Returns
+  *	a pointer to the recoverystats statistics struct.
+  * ---------
+  */
+ int 
+ pgstat_fetch_recoverystats(PgStat_RecoveryStats *recoveryStats)
+ {
+ 	int i = 0;
+ 	HASH_SEQ_STATUS rstat;
+ 	PgStat_RecoveryStats *recoveryentry;
+ 
+ 	/* load the stats file if needed */
+ 	backend_read_statsfile();
+ 
+ 	if (!pgStatRecoveryHash)
+ 		return 0;
+ 
+ 	/* Walk the whole hash table and fill the array */
+ 	hash_seq_init(&rstat, pgStatRecoveryHash);
+ 	while ((recoveryentry = (PgStat_RecoveryStats *) hash_seq_search(&rstat)) != NULL)
+ 	{
+ 		recoveryStats[i].rectypeid = recoveryentry->rectypeid;
+ 		recoveryStats[i].rmid = recoveryentry->rmid;
+ 		recoveryStats[i].info = recoveryentry->info;
+ 		recoveryStats[i].n_records = recoveryentry->n_records;
+ 		recoveryStats[i].n_bkp_blocks = recoveryentry->n_bkp_blocks;
+ 		recoveryStats[i].tot_record_size = recoveryentry->tot_record_size;
+ 		recoveryStats[i].max_record_size = recoveryentry->max_record_size;
+ 		recoveryStats[i].min_record_size = recoveryentry->min_record_size;
+ 		recoveryStats[i].tot_redo_time = recoveryentry->tot_redo_time;
+ 		recoveryStats[i].max_redo_time = recoveryentry->max_redo_time;
+ 		recoveryStats[i].min_redo_time = recoveryentry->min_redo_time;
+ 		recoveryStats[i].last_process_time = recoveryentry->last_process_time;
+ 		i++;
+ 	}
+ 
+ 	return i;
+ }
  
  /* ------------------------------------------------------------
   * Functions for management of the shared-memory PgBackendStatus array
*************** PgstatCollectorMain(int argc, char *argv
*** 3260,3265 ****
--- 3354,3363 ----
  					pgstat_recv_recoveryconflict((PgStat_MsgRecoveryConflict *) &msg, len);
  					break;
  
+ 				case PGSTAT_MTYPE_RECOVERYSTAT:
+ 					pgstat_recv_recoverystat((PgStat_MsgRecoverystat *) &msg, len);
+ 					break;
+ 
  				case PGSTAT_MTYPE_DEADLOCK:
  					pgstat_recv_deadlock((PgStat_MsgDeadlock *) &msg, len);
  					break;
*************** pgstat_write_statsfile(bool permanent)
*** 3442,3450 ****
--- 3540,3550 ----
  	HASH_SEQ_STATUS hstat;
  	HASH_SEQ_STATUS tstat;
  	HASH_SEQ_STATUS fstat;
+ 	HASH_SEQ_STATUS rstat;
  	PgStat_StatDBEntry *dbentry;
  	PgStat_StatTabEntry *tabentry;
  	PgStat_StatFuncEntry *funcentry;
+ 	PgStat_RecoveryStats *recoveryentry;
  	FILE	   *fpout;
  	int32		format_id;
  	const char *tmpfile = permanent ? PGSTAT_STAT_PERMANENT_TMPFILE : pgstat_stat_tmpname;
*************** pgstat_write_statsfile(bool permanent)
*** 3526,3531 ****
--- 3626,3646 ----
  	}
  
  	/*
+ 	 * Walk through the recovery's stats table. This is done after write databases
+ 	 * and tables stats
+ 	 */
+ 	if (pgStatRecoveryHash)
+ 	{
+ 		hash_seq_init(&rstat, pgStatRecoveryHash);
+ 		while ((recoveryentry = (PgStat_RecoveryStats *) hash_seq_search(&rstat)) != NULL)
+ 		{
+ 			fputc('R', fpout);
+ 			rc = fwrite(recoveryentry, sizeof(PgStat_RecoveryStats), 1, fpout);
+ 			(void) rc;			/* we'll check for error with ferror */
+ 		}
+ 	}
+ 
+ 	/*
  	 * No more output to be done. Close the temp file and replace the old
  	 * pgstat.stat with it.  The ferror() check replaces testing for error
  	 * after each individual fputc or fwrite above.
*************** pgstat_read_statsfile(Oid onlydb, bool p
*** 3608,3613 ****
--- 3723,3730 ----
  	PgStat_StatTabEntry tabbuf;
  	PgStat_StatFuncEntry funcbuf;
  	PgStat_StatFuncEntry *funcentry;
+ 	PgStat_RecoveryStats recoverybuf;
+ 	PgStat_RecoveryStats *recoveryentry;
  	HASHCTL		hash_ctl;
  	HTAB	   *dbhash;
  	HTAB	   *tabhash = NULL;
*************** pgstat_read_statsfile(Oid onlydb, bool p
*** 3634,3639 ****
--- 3751,3771 ----
  						 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
  
  	/*
+ 	 * And the recovery hashtable
+ 	 */
+ 	if (!pgStatRecoveryHash)
+ 	{
+ 		hash_ctl.keysize = sizeof(Oid);
+ 		hash_ctl.entrysize = sizeof(PgStat_RecoveryStats);
+ 		hash_ctl.hash = oid_hash;
+ 		hash_ctl.hcxt = pgStatLocalContext;
+ 		pgStatRecoveryHash = hash_create("Per-cluster recovery",
+ 								   PGSTAT_RECOVERY_HASH_SIZE,
+ 								   &hash_ctl,
+ 							       HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+ 	}
+ 
+ 	/*
  	 * Clear out global statistics so they start from zero in case we can't
  	 * load an existing statsfile.
  	 */
*************** pgstat_read_statsfile(Oid onlydb, bool p
*** 3842,3847 ****
--- 3974,4011 ----
  				break;
  
  				/*
+ 				 * 'R'	A PgStat_RecoveryStats follows. This describes
+ 				 *      recovery activity
+ 				 */
+ 			case 'R':
+ 				/* if the table is already loaded avoid this */
+ 				if (!recoveryHashLoaded)
+ 				{
+ 					if (fread(&recoverybuf, 1, sizeof(PgStat_RecoveryStats),
+ 							  fpin) != sizeof(PgStat_RecoveryStats))
+ 					{
+ 						ereport(pgStatRunningInCollector ? LOG : WARNING,
+ 								(errmsg("corrupted statistics file \"%s\"",
+ 										statfile)));
+ 						goto done;
+ 					}
+ 
+ 					recoveryentry = (PgStat_RecoveryStats *) hash_search(pgStatRecoveryHash,
+ 													(void *) &(recoverybuf.rectypeid),
+ 															 HASH_ENTER, &found);
+ 	
+ 					if (found)
+ 					{
+ 						ereport(pgStatRunningInCollector ? LOG : WARNING,
+ 								(errmsg("corrupted statistics file \"%s\"",
+ 										statfile)));
+ 						goto done;
+ 					}
+ 					memcpy(recoveryentry, &recoverybuf, sizeof(recoverybuf));
+ 				}
+ 				break;
+ 
+ 				/*
  				 * 'E'	The EOF marker of a complete stats file.
  				 */
  			case 'E':
*************** pgstat_read_statsfile(Oid onlydb, bool p
*** 3858,3863 ****
--- 4022,4029 ----
  done:
  	FreeFile(fpin);
  
+ 	recoveryHashLoaded = true;
+ 
  	if (permanent)
  		unlink(PGSTAT_STAT_PERMANENT_FILENAME);
  
*************** pgstat_clear_snapshot(void)
*** 4030,4035 ****
--- 4196,4203 ----
  	/* Reset variables */
  	pgStatLocalContext = NULL;
  	pgStatDBHash = NULL;
+ 	recoveryHashLoaded = false;
+ 	pgStatRecoveryHash = NULL;
  	localBackendStatusTable = NULL;
  	localNumBackends = 0;
  }
*************** pgstat_recv_recoveryconflict(PgStat_MsgR
*** 4492,4497 ****
--- 4660,4725 ----
  }
  
  /* ----------
+  * pgstat_recv_recoverystat() -
+  *
+  *	Process as RECOVERYSTAT message.
+  * ----------
+  */
+ static void
+ pgstat_recv_recoverystat(PgStat_MsgRecoverystat *msg, int len)
+ {
+ 	int rectypeid = (msg->rmid * XLR_INFO_MASK) + msg->info;
+ 	PgStat_RecoveryStats *recoveryentry;
+ 	bool		found;
+ 
+ 	recoveryentry = (PgStat_RecoveryStats *) hash_search(pgStatRecoveryHash,
+ 														 (void *) &(rectypeid),
+ 														 HASH_ENTER, &found);
+ 
+ 	if (!found)
+ 	{
+ 		/*
+ 		 * If it's a new recovery entry, initialize counters to the values
+ 		 * we just got.
+ 		 */
+ 		recoveryentry->rectypeid = rectypeid;
+ 		recoveryentry->rmid = msg->rmid;
+ 		recoveryentry->info = msg->info;
+ 		recoveryentry->n_records = 1; 
+ 		recoveryentry->tot_record_size = msg->record_size; 
+ 		recoveryentry->max_record_size = msg->record_size; 
+ 		recoveryentry->min_record_size = msg->record_size; 
+ 		recoveryentry->tot_redo_time = msg->redo_time;
+ 		recoveryentry->max_redo_time = msg->redo_time;
+ 		recoveryentry->min_redo_time = msg->redo_time;
+ 		recoveryentry->n_bkp_blocks = msg->n_bkp_blocks; 
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * Otherwise add the values to the existing entry.
+ 		 */
+ 		recoveryentry->n_records += 1; 
+ 		recoveryentry->n_bkp_blocks += msg->n_bkp_blocks; 
+ 
+ 		recoveryentry->tot_record_size += msg->record_size; 
+ 		if (msg->record_size > recoveryentry->max_record_size)
+ 			recoveryentry->max_record_size = msg->record_size; 
+ 		if (msg->record_size < recoveryentry->min_record_size)
+ 			recoveryentry->min_record_size = msg->record_size; 
+ 
+ 		recoveryentry->tot_redo_time += msg->redo_time;
+ 		if (msg->redo_time > recoveryentry->max_redo_time)
+ 			recoveryentry->max_redo_time = msg->redo_time;
+ 		if (msg->redo_time < recoveryentry->min_redo_time)
+ 			recoveryentry->min_redo_time = msg->redo_time;
+ 	}
+ 
+ 	/* common values */
+ 	recoveryentry->last_process_time = msg->process_time;
+ }
+ 
+ /* ----------
   * pgstat_recv_deadlock() -
   *
   *	Process a DEADLOCK message.
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index dc6833b..9898637 100644
*** a/src/backend/storage/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
*************** standby_desc(StringInfo buf, uint8 xl_in
*** 798,803 ****
--- 798,821 ----
  		appendStringInfo(buf, "UNKNOWN");
  }
  
+ void
+ standby_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_STANDBY_LOCK:
+ 			sprintf(buf, "Standby AccessExclusive locks");
+ 			break;
+ 		case XLOG_RUNNING_XACTS:
+ 			sprintf(buf, "Standby running xacts");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN STANDBY INFO");
+ 	}
+ }
+ 
  /*
   * Log details of the current snapshot to WAL. This allows the snapshot state
   * to be reconstructed on the standby.
diff --git a/src/backend/utils/cache/relmapper.c b/src/backend/utils/cache/relmapper.c
index 306832e..0f06421 100644
*** a/src/backend/utils/cache/relmapper.c
--- b/src/backend/utils/cache/relmapper.c
*************** relmap_desc(StringInfo buf, uint8 xl_inf
*** 909,911 ****
--- 909,926 ----
  	else
  		appendStringInfo(buf, "UNKNOWN");
  }
+ 
+ void
+ relmap_short_desc(char buf[50], uint8 xl_info)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	switch (info)
+ 	{
+ 		case XLOG_RELMAP_UPDATE:
+ 			sprintf(buf, "update relmap");
+ 			break;
+ 		default:
+ 			sprintf(buf, "UNKNOWN RELMAP INFO");
+ 	}
+ }
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 7df5292..1432987 100644
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** static struct config_bool ConfigureNames
*** 1017,1022 ****
--- 1017,1031 ----
  		true,
  		NULL, NULL, NULL
  	},
+ 	{
+ 		{"track_recovery", PGC_POSTMASTER, STATS_COLLECTOR,
+ 			gettext_noop("Collects statistics on recovery activity."),
+ 			NULL
+ 		},
+ 		&pgstat_track_recovery,
+ 		false,
+ 		NULL, NULL, NULL
+ 	},
  
  	{
  		{"update_process_title", PGC_SUSET, STATS_COLLECTOR,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 400c52b..959a845 100644
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 420,425 ****
--- 420,426 ----
  
  #track_activities = on
  #track_counts = on
+ #track_recovery = off
  #track_functions = none			# none, pl, all
  #track_activity_query_size = 1024 	# (change requires restart)
  #update_process_title = on
diff --git a/src/include/access/clog.h b/src/include/access/clog.h
index bed3b8c..50af239 100644
*** a/src/include/access/clog.h
--- b/src/include/access/clog.h
*************** extern void TruncateCLOG(TransactionId o
*** 49,53 ****
--- 49,54 ----
  
  extern void clog_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void clog_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void clog_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* CLOG_H */
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 36e490a..76be479 100644
*** a/src/include/access/gin.h
--- b/src/include/access/gin.h
*************** extern void ginUpdateStats(Relation inde
*** 56,61 ****
--- 56,62 ----
  /* ginxlog.c */
  extern void gin_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void gin_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void gin_short_desc(char buf[50], uint8 xl_info);
  extern void gin_xlog_startup(void);
  extern void gin_xlog_cleanup(void);
  extern bool gin_safe_restartpoint(void);
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 0d6b625..43fc364 100644
*** a/src/include/access/gist_private.h
--- b/src/include/access/gist_private.h
*************** extern SplitedPageLayout *gistSplit(Rela
*** 458,463 ****
--- 458,464 ----
  /* gistxlog.c */
  extern void gist_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void gist_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void gist_short_desc(char buf[50], uint8 xl_info);
  extern void gist_xlog_startup(void);
  extern void gist_xlog_cleanup(void);
  
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index a3d0f98..6fe74fd 100644
*** a/src/include/access/hash.h
--- b/src/include/access/hash.h
*************** extern OffsetNumber _hash_binsearch_last
*** 356,360 ****
--- 356,361 ----
  /* hash.c */
  extern void hash_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void hash_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void hash_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* HASH_H */
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index fa38803..9f27fde 100644
*** a/src/include/access/heapam.h
--- b/src/include/access/heapam.h
*************** extern void heap_sync(Relation relation)
*** 128,135 ****
--- 128,137 ----
  
  extern void heap_redo(XLogRecPtr lsn, XLogRecord *rptr);
  extern void heap_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void heap_short_desc(char buf[50], uint8 xl_info);
  extern void heap2_redo(XLogRecPtr lsn, XLogRecord *rptr);
  extern void heap2_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void heap2_short_desc(char buf[50], uint8 xl_info);
  
  extern XLogRecPtr log_heap_cleanup_info(RelFileNode rnode,
  					  TransactionId latestRemovedXid);
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index e20573d..329ed8b 100644
*** a/src/include/access/multixact.h
--- b/src/include/access/multixact.h
*************** extern void multixact_twophase_postabort
*** 78,82 ****
--- 78,83 ----
  
  extern void multixact_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void multixact_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void multixact_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* MULTIXACT_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 041733c..c5c80c9 100644
*** a/src/include/access/nbtree.h
--- b/src/include/access/nbtree.h
*************** extern void _bt_leafbuild(BTSpool *btspo
*** 691,696 ****
--- 691,697 ----
   */
  extern void btree_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void btree_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void btree_short_desc(char buf[50], uint8 xl_info);
  extern void btree_xlog_startup(void);
  extern void btree_xlog_cleanup(void);
  extern bool btree_safe_restartpoint(void);
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index cd6de2c..ac528f6 100644
*** a/src/include/access/spgist.h
--- b/src/include/access/spgist.h
*************** extern Datum spgvacuumcleanup(PG_FUNCTIO
*** 198,203 ****
--- 198,204 ----
  /* spgxlog.c */
  extern void spg_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void spg_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void spg_short_desc(char buf[50], uint8 xl_info);
  extern void spg_xlog_startup(void);
  extern void spg_xlog_cleanup(void);
  
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 20e344e..214bf7a 100644
*** a/src/include/access/xact.h
--- b/src/include/access/xact.h
*************** extern int	xactGetCommittedChildren(Tran
*** 249,253 ****
--- 249,254 ----
  
  extern void xact_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void xact_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void xact_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* XACT_H */
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index f8aecef..8fcf85c 100644
*** a/src/include/access/xlog.h
--- b/src/include/access/xlog.h
*************** extern void RestoreBkpBlocks(XLogRecPtr
*** 280,285 ****
--- 280,286 ----
  
  extern void xlog_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void xlog_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void xlog_short_desc(char buf[50], uint8 xl_info);
  
  extern void issue_xlog_fsync(int fd, uint32 log, uint32 seg);
  
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index b81c156..b2dce00 100644
*** a/src/include/access/xlog_internal.h
--- b/src/include/access/xlog_internal.h
*************** typedef struct RmgrData
*** 250,255 ****
--- 250,256 ----
  	const char *rm_name;
  	void		(*rm_redo) (XLogRecPtr lsn, XLogRecord *rptr);
  	void		(*rm_desc) (StringInfo buf, uint8 xl_info, char *rec);
+ 	void		(*rm_short_desc) (char buf[], uint8 xl_info);
  	void		(*rm_startup) (void);
  	void		(*rm_cleanup) (void);
  	bool		(*rm_safe_restartpoint) (void);
*************** extern Datum pg_is_in_recovery(PG_FUNCTI
*** 281,285 ****
--- 282,287 ----
  extern Datum pg_xlog_replay_pause(PG_FUNCTION_ARGS);
  extern Datum pg_xlog_replay_resume(PG_FUNCTION_ARGS);
  extern Datum pg_is_xlog_replay_paused(PG_FUNCTION_ARGS);
+ extern Datum pg_stat_get_recovery_activity(PG_FUNCTION_ARGS);
  
  #endif   /* XLOG_INTERNAL_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ba4f5b6..0168526 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 2022 (  pg_stat_get_ac
*** 2580,2585 ****
--- 2580,2587 ----
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f t s 0 0 2249 "" "{23,25,25,25,25,25,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
+ DATA(insert OID = 3153 (  pg_stat_get_recovery_activity			PGNSP PGUID 12 1 100 0 0 f f f f t s 0 0 2249 "" "{25,25,23,23,23,23,23,701,701,701,1184}" "{o,o,o,o,o,o,o,o,o,o,o}" "{rmgr,wal_record_type,n_records,n_bkp_blocks,tot_record_size,max_record_size,min_record_size,tot_redo_time,max_redo_time,min_redo_time,last_process_time}" _null_ pg_stat_get_recovery_activity _null_ _null_ _null_ ));
+ DESCR("statistics: information about recovery activity");
  DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f t f s 0 0 23 "" _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 t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h
index d5103a8..62cdc16 100644
*** a/src/include/catalog/storage.h
--- b/src/include/catalog/storage.h
*************** extern void log_smgrcreate(RelFileNode *
*** 38,42 ****
--- 38,43 ----
  
  extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void smgr_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void smgr_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* STORAGE_H */
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index 41ca8ff..13189cd 100644
*** a/src/include/commands/dbcommands.h
--- b/src/include/commands/dbcommands.h
*************** extern char *get_database_name(Oid dbid)
*** 64,69 ****
--- 64,70 ----
  
  extern void dbase_redo(XLogRecPtr lsn, XLogRecord *rptr);
  extern void dbase_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void dbase_short_desc(char buf[50], uint8 xl_info);
  
  extern void check_encoding_locale_matches(int encoding, const char *collate, const char *ctype);
  
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 2cdf86f..ffc6935 100644
*** a/src/include/commands/sequence.h
--- b/src/include/commands/sequence.h
*************** extern void ResetSequence(Oid seq_relid)
*** 77,81 ****
--- 77,82 ----
  
  extern void seq_redo(XLogRecPtr lsn, XLogRecord *rptr);
  extern void seq_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void seq_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* SEQUENCE_H */
diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h
index fe8b6a5..c3ccd0e 100644
*** a/src/include/commands/tablespace.h
--- b/src/include/commands/tablespace.h
*************** extern bool directory_is_empty(const cha
*** 58,62 ****
--- 58,63 ----
  
  extern void tblspc_redo(XLogRecPtr lsn, XLogRecord *rptr);
  extern void tblspc_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void tblspc_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* TABLESPACE_H */
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1281bd8..47d7b0e 100644
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 11,16 ****
--- 11,18 ----
  #ifndef PGSTAT_H
  #define PGSTAT_H
  
+ #include "access/rmgr.h"
+ #include "access/xlog.h"
  #include "datatype/timestamp.h"
  #include "fmgr.h"
  #include "libpq/pqcomm.h"
***************
*** 22,30 ****
  /* Values for track_functions GUC variable --- order is significant! */
  typedef enum TrackFunctionsLevel
  {
! 	TRACK_FUNC_OFF,
! 	TRACK_FUNC_PL,
! 	TRACK_FUNC_ALL
  }	TrackFunctionsLevel;
  
  /* ----------
--- 24,32 ----
  /* Values for track_functions GUC variable --- order is significant! */
  typedef enum TrackFunctionsLevel
  {
! TRACK_FUNC_OFF,
! TRACK_FUNC_PL,
! TRACK_FUNC_ALL
  }	TrackFunctionsLevel;
  
  /* ----------
*************** typedef enum StatMsgType
*** 48,53 ****
--- 50,56 ----
  	PGSTAT_MTYPE_FUNCSTAT,
  	PGSTAT_MTYPE_FUNCPURGE,
  	PGSTAT_MTYPE_RECOVERYCONFLICT,
+ 	PGSTAT_MTYPE_RECOVERYSTAT,
  	PGSTAT_MTYPE_TEMPFILE,
  	PGSTAT_MTYPE_DEADLOCK
  } StatMsgType;
*************** typedef struct PgStat_MsgTempFile
*** 391,396 ****
--- 394,442 ----
  } PgStat_MsgTempFile;
  
  /* ----------
+  * PgStat_MsgRecoverystat	Sent by the backend upon recovery conflict
+  * ----------
+  */
+ 
+ #define PGSTAT_NUM_RECOVERYSTAT_ENTRIES 600 
+ 
+ typedef struct PgStat_MsgRecoverystat
+ {
+ 	PgStat_MsgHdr m_hdr;
+ 
+ 	RmgrId			rmid;
+ 	uint8			info;
+ 	PgStat_Counter	n_bkp_blocks;
+ 	int				record_size;
+ 	TimestampTz		process_time;
+ 	double			redo_time;
+ } PgStat_MsgRecoverystat;
+ 
+ /*
+  * Recovery statistics kept in both startup process and stats collector
+  */
+ typedef struct PgStat_RecoveryStats
+ {
+ 	/* 
+ 	 * rectypeid contains the full identification of the row which is 
+ 	 * just calculated by (rmid * XLR_INFO_MASK) + info.
+ 	 * it's kept apart just because it's a convenient key for the hash table
+      */
+ 	int				rectypeid;
+ 	RmgrId			rmid;
+ 	uint8			info;
+ 	PgStat_Counter	n_records;
+ 	PgStat_Counter	n_bkp_blocks;
+ 	int				tot_record_size;
+ 	int				max_record_size;
+ 	int				min_record_size;
+ 	double			tot_redo_time;
+ 	double			max_redo_time;
+ 	double			min_redo_time;
+ 	TimestampTz		last_process_time;
+ } PgStat_RecoveryStats;
+ 
+ /* ----------
   * PgStat_FunctionCounts	The actual per-function counts kept by a backend
   *
   * This struct should contain only actual event counters, because we memcmp
*************** typedef union PgStat_Msg
*** 497,502 ****
--- 543,549 ----
  	PgStat_MsgFuncstat msg_funcstat;
  	PgStat_MsgFuncpurge msg_funcpurge;
  	PgStat_MsgRecoveryConflict msg_recoveryconflict;
+ 	PgStat_MsgRecoverystat msg_recoverystat;
  	PgStat_MsgDeadlock msg_deadlock;
  } PgStat_Msg;
  
*************** typedef struct PgStat_FunctionCallUsage
*** 711,716 ****
--- 758,764 ----
   */
  extern bool pgstat_track_activities;
  extern bool pgstat_track_counts;
+ extern bool pgstat_track_recovery;
  extern int	pgstat_track_functions;
  extern PGDLLIMPORT int pgstat_track_activity_query_size;
  extern char *pgstat_stat_tmpname;
*************** extern void pgstat_report_analyze(Relati
*** 760,765 ****
--- 808,815 ----
  					  PgStat_Counter livetuples, PgStat_Counter deadtuples);
  
  extern void pgstat_report_recovery_conflict(int reason);
+ extern void pgstat_report_recovery_stats(XLogRecord *record, TimestampTz before_ts, TimestampTz after_ts);
+ 
  extern void pgstat_report_deadlock(void);
  
  extern void pgstat_initialize(void);
*************** extern PgBackendStatus *pgstat_fetch_sta
*** 851,855 ****
--- 901,906 ----
  extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
  extern int	pgstat_fetch_stat_numbackends(void);
  extern PgStat_GlobalStats *pgstat_fetch_global(void);
+ extern int pgstat_fetch_recoverystats(PgStat_RecoveryStats *recoveryStats);
  
  #endif   /* PGSTAT_H */
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 1027bbc..b6d1b60 100644
*** a/src/include/storage/standby.h
--- b/src/include/storage/standby.h
*************** typedef struct xl_running_xacts
*** 82,87 ****
--- 82,88 ----
  /* Recovery handlers for the Standby Rmgr (RM_STANDBY_ID) */
  extern void standby_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void standby_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void standby_short_desc(char buf[50], uint8 xl_info);
  
  /*
   * Declarations for GetRunningTransactionData(). Similar to Snapshots, but
diff --git a/src/include/utils/relmapper.h b/src/include/utils/relmapper.h
index 111a05c..d198a1f 100644
*** a/src/include/utils/relmapper.h
--- b/src/include/utils/relmapper.h
*************** extern void RelationMapInitializePhase3(
*** 58,62 ****
--- 58,63 ----
  
  extern void relmap_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void relmap_desc(StringInfo buf, uint8 xl_info, char *rec);
+ extern void relmap_short_desc(char buf[50], uint8 xl_info);
  
  #endif   /* RELMAPPER_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f67b8dc..a583a37 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** SELECT viewname, definition FROM pg_view
*** 1298,1303 ****
--- 1298,1304 ----
   pg_stat_bgwriter                | SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, pg_stat_get_bgwriter_buf_written_checkpoints() AS buffers_checkpoint, pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean, pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean, pg_stat_get_buf_written_backend() AS buffers_backend, pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
   pg_stat_database                | SELECT d.oid AS datid, d.datname, pg_stat_get_db_numbackends(d.oid) AS numbackends, pg_stat_get_db_xact_commit(d.oid) AS xact_commit, pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, pg_stat_get_db_conflict_all(d.oid) AS conflicts, pg_stat_get_db_temp_files(d.oid) AS temp_files, pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, pg_stat_get_db_deadlocks(d.oid) AS deadlocks, pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset FROM pg_database d;
   pg_stat_database_conflicts      | SELECT d.oid AS datid, d.datname, pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock FROM pg_database d;
+  pg_stat_recovery                | SELECT r.rmgr, r.wal_record_type, r.n_records, r.n_bkp_blocks, r.tot_record_size, r.max_record_size, r.min_record_size, r.tot_redo_time, r.max_redo_time, r.min_redo_time, r.last_process_time FROM pg_stat_get_recovery_activity() r(rmgr, wal_record_type, n_records, n_bkp_blocks, tot_record_size, max_record_size, min_record_size, tot_redo_time, max_redo_time, min_redo_time, last_process_time);
   pg_stat_replication             | SELECT s.pid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_hostname, s.client_port, s.backend_start, w.state, w.sent_location, w.write_location, w.flush_location, w.replay_location, w.sync_priority, w.sync_state FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port), pg_authid u, pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state) WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
   pg_stat_sys_indexes             | SELECT pg_stat_all_indexes.relid, pg_stat_all_indexes.indexrelid, pg_stat_all_indexes.schemaname, pg_stat_all_indexes.relname, pg_stat_all_indexes.indexrelname, pg_stat_all_indexes.idx_scan, pg_stat_all_indexes.idx_tup_read, pg_stat_all_indexes.idx_tup_fetch FROM pg_stat_all_indexes WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text));
   pg_stat_sys_tables              | SELECT pg_stat_all_tables.relid, pg_stat_all_tables.schemaname, pg_stat_all_tables.relname, pg_stat_all_tables.seq_scan, pg_stat_all_tables.seq_tup_read, pg_stat_all_tables.idx_scan, pg_stat_all_tables.idx_tup_fetch, pg_stat_all_tables.n_tup_ins, pg_stat_all_tables.n_tup_upd, pg_stat_all_tables.n_tup_del, pg_stat_all_tables.n_tup_hot_upd, pg_stat_all_tables.n_live_tup, pg_stat_all_tables.n_dead_tup, 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.autovacuum_count, pg_stat_all_tables.analyze_count, pg_stat_all_tables.autoanalyze_count FROM pg_stat_all_tables WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text));
*************** SELECT viewname, definition FROM pg_view
*** 1338,1344 ****
   shoelace_obsolete               | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
   street                          | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
   toyemp                          | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
! (60 rows)
  
  SELECT tablename, rulename, definition FROM pg_rules
  	ORDER BY tablename, rulename;
--- 1339,1345 ----
   shoelace_obsolete               | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
   street                          | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
   toyemp                          | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
! (61 rows)
  
  SELECT tablename, rulename, definition FROM pg_rules
  	ORDER BY tablename, rulename;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 7480ec4..9631674 100644
*** a/src/tools/pgindent/typedefs.list
--- b/src/tools/pgindent/typedefs.list
*************** PgStat_MsgFuncstat
*** 1200,1211 ****
--- 1200,1213 ----
  PgStat_MsgHdr
  PgStat_MsgInquiry
  PgStat_MsgRecoveryConflict
+ PgStat_MsgRecoverystat
  PgStat_MsgResetcounter
  PgStat_MsgResetsharedcounter
  PgStat_MsgResetsinglecounter
  PgStat_MsgTabpurge
  PgStat_MsgTabstat
  PgStat_MsgVacuum
+ PgStat_RecoveryStats
  PgStat_Shared_Reset_Target
  PgStat_Single_Reset_Type
  PgStat_StatDBEntry
