diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 0366d59624e80fc3ee643f5fc301e9a65eb736bf..c32088f81dfc7120795b69a9afb0f8706f084d43 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,6 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = common gist hash heap index nbtree transam gin spgist
+SUBDIRS	    = common gin gist hash heap index nbtree rmgrdesc spgist transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index 4536c9c63dc99e8c59ad02bc4495161a5ff7a786..0ff66c875bc61a9a3e36ba9783513092be541420 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -766,69 +766,6 @@ gin_redo(XLogRecPtr lsn, XLogRecord *record)
 	MemoryContextReset(opCtx);
 }
 
-static void
-desc_node(StringInfo buf, RelFileNode node, BlockNumber blkno)
-{
-	appendStringInfo(buf, "node: %u/%u/%u blkno: %u",
-					 node.spcNode, node.dbNode, node.relNode, blkno);
-}
-
-void
-gin_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	switch (info)
-	{
-		case XLOG_GIN_CREATE_INDEX:
-			appendStringInfo(buf, "Create index, ");
-			desc_node(buf, *(RelFileNode *) rec, GIN_ROOT_BLKNO);
-			break;
-		case XLOG_GIN_CREATE_PTREE:
-			appendStringInfo(buf, "Create posting tree, ");
-			desc_node(buf, ((ginxlogCreatePostingTree *) rec)->node, ((ginxlogCreatePostingTree *) rec)->blkno);
-			break;
-		case XLOG_GIN_INSERT:
-			appendStringInfo(buf, "Insert item, ");
-			desc_node(buf, ((ginxlogInsert *) rec)->node, ((ginxlogInsert *) rec)->blkno);
-			appendStringInfo(buf, " offset: %u nitem: %u isdata: %c isleaf %c isdelete %c updateBlkno:%u",
-							 ((ginxlogInsert *) rec)->offset,
-							 ((ginxlogInsert *) rec)->nitem,
-							 (((ginxlogInsert *) rec)->isData) ? 'T' : 'F',
-							 (((ginxlogInsert *) rec)->isLeaf) ? 'T' : 'F',
-							 (((ginxlogInsert *) rec)->isDelete) ? 'T' : 'F',
-							 ((ginxlogInsert *) rec)->updateBlkno);
-			break;
-		case XLOG_GIN_SPLIT:
-			appendStringInfo(buf, "Page split, ");
-			desc_node(buf, ((ginxlogSplit *) rec)->node, ((ginxlogSplit *) rec)->lblkno);
-			appendStringInfo(buf, " isrootsplit: %c", (((ginxlogSplit *) rec)->isRootSplit) ? 'T' : 'F');
-			break;
-		case XLOG_GIN_VACUUM_PAGE:
-			appendStringInfo(buf, "Vacuum page, ");
-			desc_node(buf, ((ginxlogVacuumPage *) rec)->node, ((ginxlogVacuumPage *) rec)->blkno);
-			break;
-		case XLOG_GIN_DELETE_PAGE:
-			appendStringInfo(buf, "Delete page, ");
-			desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno);
-			break;
-		case XLOG_GIN_UPDATE_META_PAGE:
-			appendStringInfo(buf, "Update metapage, ");
-			desc_node(buf, ((ginxlogUpdateMeta *) rec)->node, GIN_METAPAGE_BLKNO);
-			break;
-		case XLOG_GIN_INSERT_LISTPAGE:
-			appendStringInfo(buf, "Insert new list page, ");
-			desc_node(buf, ((ginxlogInsertListPage *) rec)->node, ((ginxlogInsertListPage *) rec)->blkno);
-			break;
-		case XLOG_GIN_DELETE_LISTPAGE:
-			appendStringInfo(buf, "Delete list pages (%d), ", ((ginxlogDeleteListPages *) rec)->ndeleted);
-			desc_node(buf, ((ginxlogDeleteListPages *) rec)->node, GIN_METAPAGE_BLKNO);
-			break;
-		default:
-			elog(PANIC, "gin_desc: unknown op code %u", info);
-	}
-}
-
 void
 gin_xlog_startup(void)
 {
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
index 4440499d48ab004ff9205842f66c38ac2657801a..f9c8fcbcf59560a6db21d7f252f28b4dc52f1a70 100644
--- a/src/backend/access/gist/gistxlog.c
+++ b/src/backend/access/gist/gistxlog.c
@@ -362,55 +362,6 @@ gist_redo(XLogRecPtr lsn, XLogRecord *record)
 	MemoryContextReset(opCtx);
 }
 
-static void
-out_target(StringInfo buf, RelFileNode node)
-{
-	appendStringInfo(buf, "rel %u/%u/%u",
-					 node.spcNode, node.dbNode, node.relNode);
-}
-
-static void
-out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
-{
-	out_target(buf, xlrec->node);
-	appendStringInfo(buf, "; block number %u", xlrec->blkno);
-}
-
-static void
-out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec)
-{
-	appendStringInfo(buf, "page_split: ");
-	out_target(buf, xlrec->node);
-	appendStringInfo(buf, "; block number %u splits to %d pages",
-					 xlrec->origblkno, xlrec->npage);
-}
-
-void
-gist_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	switch (info)
-	{
-		case XLOG_GIST_PAGE_UPDATE:
-			appendStringInfo(buf, "page_update: ");
-			out_gistxlogPageUpdate(buf, (gistxlogPageUpdate *) rec);
-			break;
-		case XLOG_GIST_PAGE_SPLIT:
-			out_gistxlogPageSplit(buf, (gistxlogPageSplit *) rec);
-			break;
-		case XLOG_GIST_CREATE_INDEX:
-			appendStringInfo(buf, "create_index: rel %u/%u/%u",
-							 ((RelFileNode *) rec)->spcNode,
-							 ((RelFileNode *) rec)->dbNode,
-							 ((RelFileNode *) rec)->relNode);
-			break;
-		default:
-			appendStringInfo(buf, "unknown gist op code %u", info);
-			break;
-	}
-}
-
 void
 gist_xlog_startup(void)
 {
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 88026695b6608f37955bd569377f59d59810e07c..1efc5ef17c4bbc0b4b6337465d3251c70780329b 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -712,8 +712,3 @@ hash_redo(XLogRecPtr lsn, XLogRecord *record)
 {
 	elog(PANIC, "hash_redo: unimplemented");
 }
-
-void
-hash_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 64aecf251f27804d7eef22dde7069d6431982022..4abbdb684689ca556f13a9d9491b0a191cc47b5e 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -5678,154 +5678,6 @@ heap2_redo(XLogRecPtr lsn, XLogRecord *record)
 	}
 }
 
-static void
-out_target(StringInfo buf, xl_heaptid *target)
-{
-	appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",
-			 target->node.spcNode, target->node.dbNode, target->node.relNode,
-					 ItemPointerGetBlockNumber(&(target->tid)),
-					 ItemPointerGetOffsetNumber(&(target->tid)));
-}
-
-void
-heap_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	info &= XLOG_HEAP_OPMASK;
-	if (info == XLOG_HEAP_INSERT)
-	{
-		xl_heap_insert *xlrec = (xl_heap_insert *) rec;
-
-		if (xl_info & XLOG_HEAP_INIT_PAGE)
-			appendStringInfo(buf, "insert(init): ");
-		else
-			appendStringInfo(buf, "insert: ");
-		out_target(buf, &(xlrec->target));
-	}
-	else if (info == XLOG_HEAP_DELETE)
-	{
-		xl_heap_delete *xlrec = (xl_heap_delete *) rec;
-
-		appendStringInfo(buf, "delete: ");
-		out_target(buf, &(xlrec->target));
-	}
-	else if (info == XLOG_HEAP_UPDATE)
-	{
-		xl_heap_update *xlrec = (xl_heap_update *) rec;
-
-		if (xl_info & XLOG_HEAP_INIT_PAGE)
-			appendStringInfo(buf, "update(init): ");
-		else
-			appendStringInfo(buf, "update: ");
-		out_target(buf, &(xlrec->target));
-		appendStringInfo(buf, "; new %u/%u",
-						 ItemPointerGetBlockNumber(&(xlrec->newtid)),
-						 ItemPointerGetOffsetNumber(&(xlrec->newtid)));
-	}
-	else if (info == XLOG_HEAP_HOT_UPDATE)
-	{
-		xl_heap_update *xlrec = (xl_heap_update *) rec;
-
-		if (xl_info & XLOG_HEAP_INIT_PAGE)		/* can this case happen? */
-			appendStringInfo(buf, "hot_update(init): ");
-		else
-			appendStringInfo(buf, "hot_update: ");
-		out_target(buf, &(xlrec->target));
-		appendStringInfo(buf, "; new %u/%u",
-						 ItemPointerGetBlockNumber(&(xlrec->newtid)),
-						 ItemPointerGetOffsetNumber(&(xlrec->newtid)));
-	}
-	else if (info == XLOG_HEAP_NEWPAGE)
-	{
-		xl_heap_newpage *xlrec = (xl_heap_newpage *) rec;
-
-		appendStringInfo(buf, "newpage: rel %u/%u/%u; fork %u, blk %u",
-						 xlrec->node.spcNode, xlrec->node.dbNode,
-						 xlrec->node.relNode, xlrec->forknum,
-						 xlrec->blkno);
-	}
-	else if (info == XLOG_HEAP_LOCK)
-	{
-		xl_heap_lock *xlrec = (xl_heap_lock *) rec;
-
-		if (xlrec->shared_lock)
-			appendStringInfo(buf, "shared_lock: ");
-		else
-			appendStringInfo(buf, "exclusive_lock: ");
-		if (xlrec->xid_is_mxact)
-			appendStringInfo(buf, "mxid ");
-		else
-			appendStringInfo(buf, "xid ");
-		appendStringInfo(buf, "%u ", xlrec->locking_xid);
-		out_target(buf, &(xlrec->target));
-	}
-	else if (info == XLOG_HEAP_INPLACE)
-	{
-		xl_heap_inplace *xlrec = (xl_heap_inplace *) rec;
-
-		appendStringInfo(buf, "inplace: ");
-		out_target(buf, &(xlrec->target));
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
-
-void
-heap2_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	info &= XLOG_HEAP_OPMASK;
-	if (info == XLOG_HEAP2_FREEZE)
-	{
-		xl_heap_freeze *xlrec = (xl_heap_freeze *) rec;
-
-		appendStringInfo(buf, "freeze: rel %u/%u/%u; blk %u; cutoff %u",
-						 xlrec->node.spcNode, xlrec->node.dbNode,
-						 xlrec->node.relNode, xlrec->block,
-						 xlrec->cutoff_xid);
-	}
-	else if (info == XLOG_HEAP2_CLEAN)
-	{
-		xl_heap_clean *xlrec = (xl_heap_clean *) rec;
-
-		appendStringInfo(buf, "clean: rel %u/%u/%u; blk %u remxid %u",
-						 xlrec->node.spcNode, xlrec->node.dbNode,
-						 xlrec->node.relNode, xlrec->block,
-						 xlrec->latestRemovedXid);
-	}
-	else if (info == XLOG_HEAP2_CLEANUP_INFO)
-	{
-		xl_heap_cleanup_info *xlrec = (xl_heap_cleanup_info *) rec;
-
-		appendStringInfo(buf, "cleanup info: remxid %u",
-						 xlrec->latestRemovedXid);
-	}
-	else if (info == XLOG_HEAP2_VISIBLE)
-	{
-		xl_heap_visible *xlrec = (xl_heap_visible *) rec;
-
-		appendStringInfo(buf, "visible: rel %u/%u/%u; blk %u",
-						 xlrec->node.spcNode, xlrec->node.dbNode,
-						 xlrec->node.relNode, xlrec->block);
-	}
-	else if (info == XLOG_HEAP2_MULTI_INSERT)
-	{
-		xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec;
-
-		if (xl_info & XLOG_HEAP_INIT_PAGE)
-			appendStringInfo(buf, "multi-insert (init): ");
-		else
-			appendStringInfo(buf, "multi-insert: ");
-		appendStringInfo(buf, "rel %u/%u/%u; blk %u; %d tuples",
-				xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode,
-						 xlrec->blkno, xlrec->ntuples);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
-
 /*
  *	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 8f534803a7cea75105869de6eaf2946e03d5437c..9f850ab05ffb6b129abc53777b61fcbc7ebffa05 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -1081,151 +1081,6 @@ btree_redo(XLogRecPtr lsn, XLogRecord *record)
 	}
 }
 
-static void
-out_target(StringInfo buf, xl_btreetid *target)
-{
-	appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",
-			 target->node.spcNode, target->node.dbNode, target->node.relNode,
-					 ItemPointerGetBlockNumber(&(target->tid)),
-					 ItemPointerGetOffsetNumber(&(target->tid)));
-}
-
-void
-btree_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	switch (info)
-	{
-		case XLOG_BTREE_INSERT_LEAF:
-			{
-				xl_btree_insert *xlrec = (xl_btree_insert *) rec;
-
-				appendStringInfo(buf, "insert: ");
-				out_target(buf, &(xlrec->target));
-				break;
-			}
-		case XLOG_BTREE_INSERT_UPPER:
-			{
-				xl_btree_insert *xlrec = (xl_btree_insert *) rec;
-
-				appendStringInfo(buf, "insert_upper: ");
-				out_target(buf, &(xlrec->target));
-				break;
-			}
-		case XLOG_BTREE_INSERT_META:
-			{
-				xl_btree_insert *xlrec = (xl_btree_insert *) rec;
-
-				appendStringInfo(buf, "insert_meta: ");
-				out_target(buf, &(xlrec->target));
-				break;
-			}
-		case XLOG_BTREE_SPLIT_L:
-			{
-				xl_btree_split *xlrec = (xl_btree_split *) rec;
-
-				appendStringInfo(buf, "split_l: rel %u/%u/%u ",
-								 xlrec->node.spcNode, xlrec->node.dbNode,
-								 xlrec->node.relNode);
-				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
-							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
-								 xlrec->level, xlrec->firstright);
-				break;
-			}
-		case XLOG_BTREE_SPLIT_R:
-			{
-				xl_btree_split *xlrec = (xl_btree_split *) rec;
-
-				appendStringInfo(buf, "split_r: rel %u/%u/%u ",
-								 xlrec->node.spcNode, xlrec->node.dbNode,
-								 xlrec->node.relNode);
-				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
-							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
-								 xlrec->level, xlrec->firstright);
-				break;
-			}
-		case XLOG_BTREE_SPLIT_L_ROOT:
-			{
-				xl_btree_split *xlrec = (xl_btree_split *) rec;
-
-				appendStringInfo(buf, "split_l_root: rel %u/%u/%u ",
-								 xlrec->node.spcNode, xlrec->node.dbNode,
-								 xlrec->node.relNode);
-				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
-							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
-								 xlrec->level, xlrec->firstright);
-				break;
-			}
-		case XLOG_BTREE_SPLIT_R_ROOT:
-			{
-				xl_btree_split *xlrec = (xl_btree_split *) rec;
-
-				appendStringInfo(buf, "split_r_root: rel %u/%u/%u ",
-								 xlrec->node.spcNode, xlrec->node.dbNode,
-								 xlrec->node.relNode);
-				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
-							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
-								 xlrec->level, xlrec->firstright);
-				break;
-			}
-		case XLOG_BTREE_VACUUM:
-			{
-				xl_btree_vacuum *xlrec = (xl_btree_vacuum *) rec;
-
-				appendStringInfo(buf, "vacuum: rel %u/%u/%u; blk %u, lastBlockVacuumed %u",
-								 xlrec->node.spcNode, xlrec->node.dbNode,
-								 xlrec->node.relNode, xlrec->block,
-								 xlrec->lastBlockVacuumed);
-				break;
-			}
-		case XLOG_BTREE_DELETE:
-			{
-				xl_btree_delete *xlrec = (xl_btree_delete *) rec;
-
-				appendStringInfo(buf, "delete: index %u/%u/%u; iblk %u, heap %u/%u/%u;",
-				xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode,
-								 xlrec->block,
-								 xlrec->hnode.spcNode, xlrec->hnode.dbNode, xlrec->hnode.relNode);
-				break;
-			}
-		case XLOG_BTREE_DELETE_PAGE:
-		case XLOG_BTREE_DELETE_PAGE_META:
-		case XLOG_BTREE_DELETE_PAGE_HALF:
-			{
-				xl_btree_delete_page *xlrec = (xl_btree_delete_page *) rec;
-
-				appendStringInfo(buf, "delete_page: ");
-				out_target(buf, &(xlrec->target));
-				appendStringInfo(buf, "; dead %u; left %u; right %u",
-							xlrec->deadblk, xlrec->leftblk, xlrec->rightblk);
-				break;
-			}
-		case XLOG_BTREE_NEWROOT:
-			{
-				xl_btree_newroot *xlrec = (xl_btree_newroot *) rec;
-
-				appendStringInfo(buf, "newroot: rel %u/%u/%u; root %u lev %u",
-								 xlrec->node.spcNode, xlrec->node.dbNode,
-								 xlrec->node.relNode,
-								 xlrec->rootblk, xlrec->level);
-				break;
-			}
-		case XLOG_BTREE_REUSE_PAGE:
-			{
-				xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) rec;
-
-				appendStringInfo(buf, "reuse_page: rel %u/%u/%u; latestRemovedXid %u",
-								 xlrec->node.spcNode, xlrec->node.dbNode,
-							   xlrec->node.relNode, xlrec->latestRemovedXid);
-				break;
-			}
-		default:
-			appendStringInfo(buf, "UNKNOWN");
-			break;
-	}
-}
-
 void
 btree_xlog_startup(void)
 {
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..7d092d205d6083404a50a09c4a4e3e7aae268469
--- /dev/null
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the rmgr descriptor routines
+#
+# src/backend/access/rmgrdesc/Makefile
+#
+
+subdir = src/backend/access/rmgrdesc
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = clogdesc.o dbasedesc.o gindesc.o gistdesc.o hashdesc.o heapdesc.o \
+	   mxactdesc.o nbtdesc.o relmapdesc.o seqdesc.o smgrdesc.o spgdesc.o \
+	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/clogdesc.c b/src/backend/access/rmgrdesc/clogdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..07ab40fb4118af9eba03ccd010db070dcf6c0cad
--- /dev/null
+++ b/src/backend/access/rmgrdesc/clogdesc.c
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * clogdesc.c
+ *    rmgr descriptor routines for access/transam/clog.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/clogdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/clog.h"
+
+
+void
+clog_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == CLOG_ZEROPAGE)
+	{
+		int			pageno;
+
+		memcpy(&pageno, rec, sizeof(int));
+		appendStringInfo(buf, "zeropage: %d", pageno);
+	}
+	else if (info == CLOG_TRUNCATE)
+	{
+		int			pageno;
+
+		memcpy(&pageno, rec, sizeof(int));
+		appendStringInfo(buf, "truncate before: %d", pageno);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/dbasedesc.c b/src/backend/access/rmgrdesc/dbasedesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..d7ee96cb8596c501171ad643d876605dc146230b
--- /dev/null
+++ b/src/backend/access/rmgrdesc/dbasedesc.c
@@ -0,0 +1,43 @@
+/*-------------------------------------------------------------------------
+ *
+ * dbasedesc.c
+ *    rmgr descriptor routines for commands/dbcommands.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/dbasedesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "commands/dbcommands.h"
+#include "lib/stringinfo.h"
+
+
+void
+dbase_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_DBASE_CREATE)
+	{
+		xl_dbase_create_rec *xlrec = (xl_dbase_create_rec *) rec;
+
+		appendStringInfo(buf, "create db: copy dir %u/%u to %u/%u",
+						 xlrec->src_db_id, xlrec->src_tablespace_id,
+						 xlrec->db_id, xlrec->tablespace_id);
+	}
+	else if (info == XLOG_DBASE_DROP)
+	{
+		xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) rec;
+
+		appendStringInfo(buf, "drop db: dir %u/%u",
+						 xlrec->db_id, xlrec->tablespace_id);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/gindesc.c b/src/backend/access/rmgrdesc/gindesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..7ea36f28e1020f66ba5f54ef2079cf2b3886681b
--- /dev/null
+++ b/src/backend/access/rmgrdesc/gindesc.c
@@ -0,0 +1,83 @@
+/*-------------------------------------------------------------------------
+ *
+ * gindesc.c
+ *    rmgr descriptor routines for access/transam/gin/ginxlog.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/gindesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/gin_private.h"
+#include "lib/stringinfo.h"
+#include "storage/relfilenode.h"
+
+static void
+desc_node(StringInfo buf, RelFileNode node, BlockNumber blkno)
+{
+	appendStringInfo(buf, "node: %u/%u/%u blkno: %u",
+					 node.spcNode, node.dbNode, node.relNode, blkno);
+}
+
+void
+gin_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_GIN_CREATE_INDEX:
+			appendStringInfo(buf, "Create index, ");
+			desc_node(buf, *(RelFileNode *) rec, GIN_ROOT_BLKNO);
+			break;
+		case XLOG_GIN_CREATE_PTREE:
+			appendStringInfo(buf, "Create posting tree, ");
+			desc_node(buf, ((ginxlogCreatePostingTree *) rec)->node, ((ginxlogCreatePostingTree *) rec)->blkno);
+			break;
+		case XLOG_GIN_INSERT:
+			appendStringInfo(buf, "Insert item, ");
+			desc_node(buf, ((ginxlogInsert *) rec)->node, ((ginxlogInsert *) rec)->blkno);
+			appendStringInfo(buf, " offset: %u nitem: %u isdata: %c isleaf %c isdelete %c updateBlkno:%u",
+							 ((ginxlogInsert *) rec)->offset,
+							 ((ginxlogInsert *) rec)->nitem,
+							 (((ginxlogInsert *) rec)->isData) ? 'T' : 'F',
+							 (((ginxlogInsert *) rec)->isLeaf) ? 'T' : 'F',
+							 (((ginxlogInsert *) rec)->isDelete) ? 'T' : 'F',
+							 ((ginxlogInsert *) rec)->updateBlkno);
+			break;
+		case XLOG_GIN_SPLIT:
+			appendStringInfo(buf, "Page split, ");
+			desc_node(buf, ((ginxlogSplit *) rec)->node, ((ginxlogSplit *) rec)->lblkno);
+			appendStringInfo(buf, " isrootsplit: %c", (((ginxlogSplit *) rec)->isRootSplit) ? 'T' : 'F');
+			break;
+		case XLOG_GIN_VACUUM_PAGE:
+			appendStringInfo(buf, "Vacuum page, ");
+			desc_node(buf, ((ginxlogVacuumPage *) rec)->node, ((ginxlogVacuumPage *) rec)->blkno);
+			break;
+		case XLOG_GIN_DELETE_PAGE:
+			appendStringInfo(buf, "Delete page, ");
+			desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno);
+			break;
+		case XLOG_GIN_UPDATE_META_PAGE:
+			appendStringInfo(buf, "Update metapage, ");
+			desc_node(buf, ((ginxlogUpdateMeta *) rec)->node, GIN_METAPAGE_BLKNO);
+			break;
+		case XLOG_GIN_INSERT_LISTPAGE:
+			appendStringInfo(buf, "Insert new list page, ");
+			desc_node(buf, ((ginxlogInsertListPage *) rec)->node, ((ginxlogInsertListPage *) rec)->blkno);
+			break;
+		case XLOG_GIN_DELETE_LISTPAGE:
+			appendStringInfo(buf, "Delete list pages (%d), ", ((ginxlogDeleteListPages *) rec)->ndeleted);
+			desc_node(buf, ((ginxlogDeleteListPages *) rec)->node, GIN_METAPAGE_BLKNO);
+			break;
+		default:
+			appendStringInfo(buf, "unknown gin op code %u", info);
+			break;
+	}
+}
diff --git a/src/backend/access/rmgrdesc/gistdesc.c b/src/backend/access/rmgrdesc/gistdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..1f47c6b8bbb349063f409838060b52231beb504f
--- /dev/null
+++ b/src/backend/access/rmgrdesc/gistdesc.c
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * gistdesc.c
+ *    rmgr descriptor routines for access/gist/gistxlog.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/gistdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/gist_private.h"
+#include "lib/stringinfo.h"
+#include "storage/relfilenode.h"
+
+static void
+out_target(StringInfo buf, RelFileNode node)
+{
+	appendStringInfo(buf, "rel %u/%u/%u",
+					 node.spcNode, node.dbNode, node.relNode);
+}
+
+static void
+out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
+{
+	out_target(buf, xlrec->node);
+	appendStringInfo(buf, "; block number %u", xlrec->blkno);
+}
+
+static void
+out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec)
+{
+	appendStringInfo(buf, "page_split: ");
+	out_target(buf, xlrec->node);
+	appendStringInfo(buf, "; block number %u splits to %d pages",
+					 xlrec->origblkno, xlrec->npage);
+}
+
+void
+gist_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_GIST_PAGE_UPDATE:
+			appendStringInfo(buf, "page_update: ");
+			out_gistxlogPageUpdate(buf, (gistxlogPageUpdate *) rec);
+			break;
+		case XLOG_GIST_PAGE_SPLIT:
+			out_gistxlogPageSplit(buf, (gistxlogPageSplit *) rec);
+			break;
+		case XLOG_GIST_CREATE_INDEX:
+			appendStringInfo(buf, "create_index: rel %u/%u/%u",
+							 ((RelFileNode *) rec)->spcNode,
+							 ((RelFileNode *) rec)->dbNode,
+							 ((RelFileNode *) rec)->relNode);
+			break;
+		default:
+			appendStringInfo(buf, "unknown gist op code %u", info);
+			break;
+	}
+}
diff --git a/src/backend/access/rmgrdesc/hashdesc.c b/src/backend/access/rmgrdesc/hashdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..faba90c6f3b0aea89cb45dcf674cded72b75b895
--- /dev/null
+++ b/src/backend/access/rmgrdesc/hashdesc.c
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashdesc.c
+ *    rmgr descriptor routines for access/hash/hash.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/hashdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+
+void
+hash_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+}
diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..e65745a96f1aa164d14ea3691cbcca6008c8d3c5
--- /dev/null
+++ b/src/backend/access/rmgrdesc/heapdesc.c
@@ -0,0 +1,165 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapdesc.c
+ *    rmgr descriptor routines for access/heap/heapam.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/heapdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam_xlog.h"
+
+static void
+out_target(StringInfo buf, xl_heaptid *target)
+{
+	appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",
+			 target->node.spcNode, target->node.dbNode, target->node.relNode,
+					 ItemPointerGetBlockNumber(&(target->tid)),
+					 ItemPointerGetOffsetNumber(&(target->tid)));
+}
+
+void
+heap_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	info &= XLOG_HEAP_OPMASK;
+	if (info == XLOG_HEAP_INSERT)
+	{
+		xl_heap_insert *xlrec = (xl_heap_insert *) rec;
+
+		if (xl_info & XLOG_HEAP_INIT_PAGE)
+			appendStringInfo(buf, "insert(init): ");
+		else
+			appendStringInfo(buf, "insert: ");
+		out_target(buf, &(xlrec->target));
+	}
+	else if (info == XLOG_HEAP_DELETE)
+	{
+		xl_heap_delete *xlrec = (xl_heap_delete *) rec;
+
+		appendStringInfo(buf, "delete: ");
+		out_target(buf, &(xlrec->target));
+	}
+	else if (info == XLOG_HEAP_UPDATE)
+	{
+		xl_heap_update *xlrec = (xl_heap_update *) rec;
+
+		if (xl_info & XLOG_HEAP_INIT_PAGE)
+			appendStringInfo(buf, "update(init): ");
+		else
+			appendStringInfo(buf, "update: ");
+		out_target(buf, &(xlrec->target));
+		appendStringInfo(buf, "; new %u/%u",
+						 ItemPointerGetBlockNumber(&(xlrec->newtid)),
+						 ItemPointerGetOffsetNumber(&(xlrec->newtid)));
+	}
+	else if (info == XLOG_HEAP_HOT_UPDATE)
+	{
+		xl_heap_update *xlrec = (xl_heap_update *) rec;
+
+		if (xl_info & XLOG_HEAP_INIT_PAGE)		/* can this case happen? */
+			appendStringInfo(buf, "hot_update(init): ");
+		else
+			appendStringInfo(buf, "hot_update: ");
+		out_target(buf, &(xlrec->target));
+		appendStringInfo(buf, "; new %u/%u",
+						 ItemPointerGetBlockNumber(&(xlrec->newtid)),
+						 ItemPointerGetOffsetNumber(&(xlrec->newtid)));
+	}
+	else if (info == XLOG_HEAP_NEWPAGE)
+	{
+		xl_heap_newpage *xlrec = (xl_heap_newpage *) rec;
+
+		appendStringInfo(buf, "newpage: rel %u/%u/%u; fork %u, blk %u",
+						 xlrec->node.spcNode, xlrec->node.dbNode,
+						 xlrec->node.relNode, xlrec->forknum,
+						 xlrec->blkno);
+	}
+	else if (info == XLOG_HEAP_LOCK)
+	{
+		xl_heap_lock *xlrec = (xl_heap_lock *) rec;
+
+		if (xlrec->shared_lock)
+			appendStringInfo(buf, "shared_lock: ");
+		else
+			appendStringInfo(buf, "exclusive_lock: ");
+		if (xlrec->xid_is_mxact)
+			appendStringInfo(buf, "mxid ");
+		else
+			appendStringInfo(buf, "xid ");
+		appendStringInfo(buf, "%u ", xlrec->locking_xid);
+		out_target(buf, &(xlrec->target));
+	}
+	else if (info == XLOG_HEAP_INPLACE)
+	{
+		xl_heap_inplace *xlrec = (xl_heap_inplace *) rec;
+
+		appendStringInfo(buf, "inplace: ");
+		out_target(buf, &(xlrec->target));
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
+
+void
+heap2_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	info &= XLOG_HEAP_OPMASK;
+	if (info == XLOG_HEAP2_FREEZE)
+	{
+		xl_heap_freeze *xlrec = (xl_heap_freeze *) rec;
+
+		appendStringInfo(buf, "freeze: rel %u/%u/%u; blk %u; cutoff %u",
+						 xlrec->node.spcNode, xlrec->node.dbNode,
+						 xlrec->node.relNode, xlrec->block,
+						 xlrec->cutoff_xid);
+	}
+	else if (info == XLOG_HEAP2_CLEAN)
+	{
+		xl_heap_clean *xlrec = (xl_heap_clean *) rec;
+
+		appendStringInfo(buf, "clean: rel %u/%u/%u; blk %u remxid %u",
+						 xlrec->node.spcNode, xlrec->node.dbNode,
+						 xlrec->node.relNode, xlrec->block,
+						 xlrec->latestRemovedXid);
+	}
+	else if (info == XLOG_HEAP2_CLEANUP_INFO)
+	{
+		xl_heap_cleanup_info *xlrec = (xl_heap_cleanup_info *) rec;
+
+		appendStringInfo(buf, "cleanup info: remxid %u",
+						 xlrec->latestRemovedXid);
+	}
+	else if (info == XLOG_HEAP2_VISIBLE)
+	{
+		xl_heap_visible *xlrec = (xl_heap_visible *) rec;
+
+		appendStringInfo(buf, "visible: rel %u/%u/%u; blk %u",
+						 xlrec->node.spcNode, xlrec->node.dbNode,
+						 xlrec->node.relNode, xlrec->block);
+	}
+	else if (info == XLOG_HEAP2_MULTI_INSERT)
+	{
+		xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec;
+
+		if (xl_info & XLOG_HEAP_INIT_PAGE)
+			appendStringInfo(buf, "multi-insert (init): ");
+		else
+			appendStringInfo(buf, "multi-insert: ");
+		appendStringInfo(buf, "rel %u/%u/%u; blk %u; %d tuples",
+				xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode,
+						 xlrec->blkno, xlrec->ntuples);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/mxactdesc.c b/src/backend/access/rmgrdesc/mxactdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..33b89f91033d742ddb6fd9576fe94c9981e66074
--- /dev/null
+++ b/src/backend/access/rmgrdesc/mxactdesc.c
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * mxactdesc.c
+ *    rmgr descriptor routines for access/transam/multixact.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/mxactdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/multixact.h"
+
+
+void
+multixact_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
+	{
+		int			pageno;
+
+		memcpy(&pageno, rec, sizeof(int));
+		appendStringInfo(buf, "zero offsets page: %d", pageno);
+	}
+	else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
+	{
+		int			pageno;
+
+		memcpy(&pageno, rec, sizeof(int));
+		appendStringInfo(buf, "zero members page: %d", pageno);
+	}
+	else if (info == XLOG_MULTIXACT_CREATE_ID)
+	{
+		xl_multixact_create *xlrec = (xl_multixact_create *) rec;
+		int			i;
+
+		appendStringInfo(buf, "create multixact %u offset %u:",
+						 xlrec->mid, xlrec->moff);
+		for (i = 0; i < xlrec->nxids; i++)
+			appendStringInfo(buf, " %u", xlrec->xids[i]);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/nbtdesc.c b/src/backend/access/rmgrdesc/nbtdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..04da5f8691fbfe60f01c0b52b3c193b4b339a432
--- /dev/null
+++ b/src/backend/access/rmgrdesc/nbtdesc.c
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * nbtdesc.c
+ *    rmgr descriptor routines for access/nbtree/nbtxlog.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/nbtdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/nbtree.h"
+
+static void
+out_target(StringInfo buf, xl_btreetid *target)
+{
+	appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",
+			 target->node.spcNode, target->node.dbNode, target->node.relNode,
+					 ItemPointerGetBlockNumber(&(target->tid)),
+					 ItemPointerGetOffsetNumber(&(target->tid)));
+}
+
+void
+btree_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_BTREE_INSERT_LEAF:
+			{
+				xl_btree_insert *xlrec = (xl_btree_insert *) rec;
+
+				appendStringInfo(buf, "insert: ");
+				out_target(buf, &(xlrec->target));
+				break;
+			}
+		case XLOG_BTREE_INSERT_UPPER:
+			{
+				xl_btree_insert *xlrec = (xl_btree_insert *) rec;
+
+				appendStringInfo(buf, "insert_upper: ");
+				out_target(buf, &(xlrec->target));
+				break;
+			}
+		case XLOG_BTREE_INSERT_META:
+			{
+				xl_btree_insert *xlrec = (xl_btree_insert *) rec;
+
+				appendStringInfo(buf, "insert_meta: ");
+				out_target(buf, &(xlrec->target));
+				break;
+			}
+		case XLOG_BTREE_SPLIT_L:
+			{
+				xl_btree_split *xlrec = (xl_btree_split *) rec;
+
+				appendStringInfo(buf, "split_l: rel %u/%u/%u ",
+								 xlrec->node.spcNode, xlrec->node.dbNode,
+								 xlrec->node.relNode);
+				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
+							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
+								 xlrec->level, xlrec->firstright);
+				break;
+			}
+		case XLOG_BTREE_SPLIT_R:
+			{
+				xl_btree_split *xlrec = (xl_btree_split *) rec;
+
+				appendStringInfo(buf, "split_r: rel %u/%u/%u ",
+								 xlrec->node.spcNode, xlrec->node.dbNode,
+								 xlrec->node.relNode);
+				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
+							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
+								 xlrec->level, xlrec->firstright);
+				break;
+			}
+		case XLOG_BTREE_SPLIT_L_ROOT:
+			{
+				xl_btree_split *xlrec = (xl_btree_split *) rec;
+
+				appendStringInfo(buf, "split_l_root: rel %u/%u/%u ",
+								 xlrec->node.spcNode, xlrec->node.dbNode,
+								 xlrec->node.relNode);
+				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
+							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
+								 xlrec->level, xlrec->firstright);
+				break;
+			}
+		case XLOG_BTREE_SPLIT_R_ROOT:
+			{
+				xl_btree_split *xlrec = (xl_btree_split *) rec;
+
+				appendStringInfo(buf, "split_r_root: rel %u/%u/%u ",
+								 xlrec->node.spcNode, xlrec->node.dbNode,
+								 xlrec->node.relNode);
+				appendStringInfo(buf, "left %u, right %u, next %u, level %u, firstright %d",
+							   xlrec->leftsib, xlrec->rightsib, xlrec->rnext,
+								 xlrec->level, xlrec->firstright);
+				break;
+			}
+		case XLOG_BTREE_VACUUM:
+			{
+				xl_btree_vacuum *xlrec = (xl_btree_vacuum *) rec;
+
+				appendStringInfo(buf, "vacuum: rel %u/%u/%u; blk %u, lastBlockVacuumed %u",
+								 xlrec->node.spcNode, xlrec->node.dbNode,
+								 xlrec->node.relNode, xlrec->block,
+								 xlrec->lastBlockVacuumed);
+				break;
+			}
+		case XLOG_BTREE_DELETE:
+			{
+				xl_btree_delete *xlrec = (xl_btree_delete *) rec;
+
+				appendStringInfo(buf, "delete: index %u/%u/%u; iblk %u, heap %u/%u/%u;",
+				xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode,
+								 xlrec->block,
+								 xlrec->hnode.spcNode, xlrec->hnode.dbNode, xlrec->hnode.relNode);
+				break;
+			}
+		case XLOG_BTREE_DELETE_PAGE:
+		case XLOG_BTREE_DELETE_PAGE_META:
+		case XLOG_BTREE_DELETE_PAGE_HALF:
+			{
+				xl_btree_delete_page *xlrec = (xl_btree_delete_page *) rec;
+
+				appendStringInfo(buf, "delete_page: ");
+				out_target(buf, &(xlrec->target));
+				appendStringInfo(buf, "; dead %u; left %u; right %u",
+							xlrec->deadblk, xlrec->leftblk, xlrec->rightblk);
+				break;
+			}
+		case XLOG_BTREE_NEWROOT:
+			{
+				xl_btree_newroot *xlrec = (xl_btree_newroot *) rec;
+
+				appendStringInfo(buf, "newroot: rel %u/%u/%u; root %u lev %u",
+								 xlrec->node.spcNode, xlrec->node.dbNode,
+								 xlrec->node.relNode,
+								 xlrec->rootblk, xlrec->level);
+				break;
+			}
+		case XLOG_BTREE_REUSE_PAGE:
+			{
+				xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) rec;
+
+				appendStringInfo(buf, "reuse_page: rel %u/%u/%u; latestRemovedXid %u",
+								 xlrec->node.spcNode, xlrec->node.dbNode,
+							   xlrec->node.relNode, xlrec->latestRemovedXid);
+				break;
+			}
+		default:
+			appendStringInfo(buf, "UNKNOWN");
+			break;
+	}
+}
diff --git a/src/backend/access/rmgrdesc/relmapdesc.c b/src/backend/access/rmgrdesc/relmapdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..3eaf6be9b41793ceb1c5b44c8e9c97dff5128512
--- /dev/null
+++ b/src/backend/access/rmgrdesc/relmapdesc.c
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * relmapdesc.c
+ *    rmgr descriptor routines for utils/cache/relmapper.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/relmapdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "utils/relmapper.h"
+
+void
+relmap_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_RELMAP_UPDATE)
+	{
+		xl_relmap_update *xlrec = (xl_relmap_update *) rec;
+
+		appendStringInfo(buf, "update relmap: database %u tablespace %u size %u",
+						 xlrec->dbid, xlrec->tsid, xlrec->nbytes);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/seqdesc.c b/src/backend/access/rmgrdesc/seqdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..f28238504563b8f5994c944e71739cb8e066ce1a
--- /dev/null
+++ b/src/backend/access/rmgrdesc/seqdesc.c
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqdesc.c
+ *    rmgr descriptor routines for commands/sequence.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/seqdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "commands/sequence.h"
+
+
+void
+seq_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+	xl_seq_rec *xlrec = (xl_seq_rec *) rec;
+
+	if (info == XLOG_SEQ_LOG)
+		appendStringInfo(buf, "log: ");
+	else
+	{
+		appendStringInfo(buf, "UNKNOWN");
+		return;
+	}
+
+	appendStringInfo(buf, "rel %u/%u/%u",
+			   xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
+}
diff --git a/src/backend/access/rmgrdesc/smgrdesc.c b/src/backend/access/rmgrdesc/smgrdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..40b9708bad566de4c73410ad09ae4d45699583c2
--- /dev/null
+++ b/src/backend/access/rmgrdesc/smgrdesc.c
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * smgrdesc.c
+ *    rmgr descriptor routines for catalog/storage.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/smgrdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/catalog.h"
+#include "catalog/storage_xlog.h"
+
+
+void
+smgr_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_SMGR_CREATE)
+	{
+		xl_smgr_create *xlrec = (xl_smgr_create *) rec;
+		char	   *path = relpathperm(xlrec->rnode, xlrec->forkNum);
+
+		appendStringInfo(buf, "file create: %s", path);
+		pfree(path);
+	}
+	else if (info == XLOG_SMGR_TRUNCATE)
+	{
+		xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec;
+		char	   *path = relpathperm(xlrec->rnode, MAIN_FORKNUM);
+
+		appendStringInfo(buf, "file truncate: %s to %u blocks", path,
+						 xlrec->blkno);
+		pfree(path);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/spgdesc.c b/src/backend/access/rmgrdesc/spgdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..20de85ddf8f45a9441660f6dadd0d27a543c292b
--- /dev/null
+++ b/src/backend/access/rmgrdesc/spgdesc.c
@@ -0,0 +1,89 @@
+/*-------------------------------------------------------------------------
+ *
+ * spgdesc.c
+ *    rmgr descriptor routines for access/spgist/spgxlog.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/spgdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/spgist_private.h"
+
+static void
+out_target(StringInfo buf, RelFileNode node)
+{
+	appendStringInfo(buf, "rel %u/%u/%u ",
+					 node.spcNode, node.dbNode, node.relNode);
+}
+
+void
+spg_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_SPGIST_CREATE_INDEX:
+			appendStringInfo(buf, "create_index: rel %u/%u/%u",
+							 ((RelFileNode *) rec)->spcNode,
+							 ((RelFileNode *) rec)->dbNode,
+							 ((RelFileNode *) rec)->relNode);
+			break;
+		case XLOG_SPGIST_ADD_LEAF:
+			out_target(buf, ((spgxlogAddLeaf *) rec)->node);
+			appendStringInfo(buf, "add leaf to page: %u",
+							 ((spgxlogAddLeaf *) rec)->blknoLeaf);
+			break;
+		case XLOG_SPGIST_MOVE_LEAFS:
+			out_target(buf, ((spgxlogMoveLeafs *) rec)->node);
+			appendStringInfo(buf, "move %u leafs from page %u to page %u",
+							 ((spgxlogMoveLeafs *) rec)->nMoves,
+							 ((spgxlogMoveLeafs *) rec)->blknoSrc,
+							 ((spgxlogMoveLeafs *) rec)->blknoDst);
+			break;
+		case XLOG_SPGIST_ADD_NODE:
+			out_target(buf, ((spgxlogAddNode *) rec)->node);
+			appendStringInfo(buf, "add node to %u:%u",
+							 ((spgxlogAddNode *) rec)->blkno,
+							 ((spgxlogAddNode *) rec)->offnum);
+			break;
+		case XLOG_SPGIST_SPLIT_TUPLE:
+			out_target(buf, ((spgxlogSplitTuple *) rec)->node);
+			appendStringInfo(buf, "split node %u:%u to %u:%u",
+							 ((spgxlogSplitTuple *) rec)->blknoPrefix,
+							 ((spgxlogSplitTuple *) rec)->offnumPrefix,
+							 ((spgxlogSplitTuple *) rec)->blknoPostfix,
+							 ((spgxlogSplitTuple *) rec)->offnumPostfix);
+			break;
+		case XLOG_SPGIST_PICKSPLIT:
+			out_target(buf, ((spgxlogPickSplit *) rec)->node);
+			appendStringInfo(buf, "split leaf page");
+			break;
+		case XLOG_SPGIST_VACUUM_LEAF:
+			out_target(buf, ((spgxlogVacuumLeaf *) rec)->node);
+			appendStringInfo(buf, "vacuum leaf tuples on page %u",
+							 ((spgxlogVacuumLeaf *) rec)->blkno);
+			break;
+		case XLOG_SPGIST_VACUUM_ROOT:
+			out_target(buf, ((spgxlogVacuumRoot *) rec)->node);
+			appendStringInfo(buf, "vacuum leaf tuples on root page %u",
+							 ((spgxlogVacuumRoot *) rec)->blkno);
+			break;
+		case XLOG_SPGIST_VACUUM_REDIRECT:
+			out_target(buf, ((spgxlogVacuumRedirect *) rec)->node);
+			appendStringInfo(buf, "vacuum redirect tuples on page %u, newest XID %u",
+							 ((spgxlogVacuumRedirect *) rec)->blkno,
+						 ((spgxlogVacuumRedirect *) rec)->newestRedirectXid);
+			break;
+		default:
+			appendStringInfo(buf, "unknown spgist op code %u", info);
+			break;
+	}
+}
diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5982d1daff71efc63771a08df953f1a24460aac
--- /dev/null
+++ b/src/backend/access/rmgrdesc/standbydesc.c
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * standbydesc.c
+ *    rmgr descriptor routines for storage/ipc/standby.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/standbydesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/standby.h"
+
+static void
+standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec)
+{
+	int			i;
+
+	appendStringInfo(buf, " nextXid %u latestCompletedXid %u oldestRunningXid %u",
+					 xlrec->nextXid,
+					 xlrec->latestCompletedXid,
+					 xlrec->oldestRunningXid);
+	if (xlrec->xcnt > 0)
+	{
+		appendStringInfo(buf, "; %d xacts:", xlrec->xcnt);
+		for (i = 0; i < xlrec->xcnt; i++)
+			appendStringInfo(buf, " %u", xlrec->xids[i]);
+	}
+
+	if (xlrec->subxid_overflow)
+		appendStringInfo(buf, "; subxid ovf");
+}
+
+void
+standby_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_STANDBY_LOCK)
+	{
+		xl_standby_locks *xlrec = (xl_standby_locks *) rec;
+		int			i;
+
+		appendStringInfo(buf, "AccessExclusive locks:");
+
+		for (i = 0; i < xlrec->nlocks; i++)
+			appendStringInfo(buf, " xid %u db %u rel %u",
+							 xlrec->locks[i].xid, xlrec->locks[i].dbOid,
+							 xlrec->locks[i].relOid);
+	}
+	else if (info == XLOG_RUNNING_XACTS)
+	{
+		xl_running_xacts *xlrec = (xl_running_xacts *) rec;
+
+		appendStringInfo(buf, " running xacts:");
+		standby_desc_running_xacts(buf, xlrec);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/tblspcdesc.c b/src/backend/access/rmgrdesc/tblspcdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..803e1b014861a3b6949c54e67c175fd1b04982f6
--- /dev/null
+++ b/src/backend/access/rmgrdesc/tblspcdesc.c
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * tblspcdesc.c
+ *    rmgr descriptor routines for commands/tablespace.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/tblspcdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "commands/tablespace.h"
+
+
+void
+tblspc_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_TBLSPC_CREATE)
+	{
+		xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) rec;
+
+		appendStringInfo(buf, "create tablespace: %u \"%s\"",
+						 xlrec->ts_id, xlrec->ts_path);
+	}
+	else if (info == XLOG_TBLSPC_DROP)
+	{
+		xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) rec;
+
+		appendStringInfo(buf, "drop tablespace: %u", xlrec->ts_id);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..60deddcf8eaf0dba543e11e0a7ac81d218dae636
--- /dev/null
+++ b/src/backend/access/rmgrdesc/xactdesc.c
@@ -0,0 +1,194 @@
+/*-------------------------------------------------------------------------
+ *
+ * xactdesc.c
+ *    rmgr descriptor routines for access/transam/xact.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/xactdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "storage/sinval.h"
+#include "utils/timestamp.h"
+
+
+static void
+xact_desc_commit(StringInfo buf, xl_xact_commit *xlrec)
+{
+	int			i;
+	TransactionId *subxacts;
+
+	subxacts = (TransactionId *) &xlrec->xnodes[xlrec->nrels];
+
+	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
+
+	if (xlrec->nrels > 0)
+	{
+		appendStringInfo(buf, "; rels:");
+		for (i = 0; i < xlrec->nrels; i++)
+		{
+			char	   *path = relpathperm(xlrec->xnodes[i], MAIN_FORKNUM);
+
+			appendStringInfo(buf, " %s", path);
+			pfree(path);
+		}
+	}
+	if (xlrec->nsubxacts > 0)
+	{
+		appendStringInfo(buf, "; subxacts:");
+		for (i = 0; i < xlrec->nsubxacts; i++)
+			appendStringInfo(buf, " %u", subxacts[i]);
+	}
+	if (xlrec->nmsgs > 0)
+	{
+		SharedInvalidationMessage *msgs;
+
+		msgs = (SharedInvalidationMessage *) &subxacts[xlrec->nsubxacts];
+
+		if (XactCompletionRelcacheInitFileInval(xlrec->xinfo))
+			appendStringInfo(buf, "; relcache init file inval dbid %u tsid %u",
+							 xlrec->dbId, xlrec->tsId);
+
+		appendStringInfo(buf, "; inval msgs:");
+		for (i = 0; i < xlrec->nmsgs; i++)
+		{
+			SharedInvalidationMessage *msg = &msgs[i];
+
+			if (msg->id >= 0)
+				appendStringInfo(buf, " catcache %d", msg->id);
+			else if (msg->id == SHAREDINVALCATALOG_ID)
+				appendStringInfo(buf, " catalog %u", msg->cat.catId);
+			else if (msg->id == SHAREDINVALRELCACHE_ID)
+				appendStringInfo(buf, " relcache %u", msg->rc.relId);
+			/* remaining cases not expected, but print something anyway */
+			else if (msg->id == SHAREDINVALSMGR_ID)
+				appendStringInfo(buf, " smgr");
+			else if (msg->id == SHAREDINVALRELMAP_ID)
+				appendStringInfo(buf, " relmap");
+			else
+				appendStringInfo(buf, " unknown id %d", msg->id);
+		}
+	}
+}
+
+static void
+xact_desc_commit_compact(StringInfo buf, xl_xact_commit_compact *xlrec)
+{
+	int			i;
+
+	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
+
+	if (xlrec->nsubxacts > 0)
+	{
+		appendStringInfo(buf, "; subxacts:");
+		for (i = 0; i < xlrec->nsubxacts; i++)
+			appendStringInfo(buf, " %u", xlrec->subxacts[i]);
+	}
+}
+
+static void
+xact_desc_abort(StringInfo buf, xl_xact_abort *xlrec)
+{
+	int			i;
+
+	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
+	if (xlrec->nrels > 0)
+	{
+		appendStringInfo(buf, "; rels:");
+		for (i = 0; i < xlrec->nrels; i++)
+		{
+			char	   *path = relpathperm(xlrec->xnodes[i], MAIN_FORKNUM);
+
+			appendStringInfo(buf, " %s", path);
+			pfree(path);
+		}
+	}
+	if (xlrec->nsubxacts > 0)
+	{
+		TransactionId *xacts = (TransactionId *)
+		&xlrec->xnodes[xlrec->nrels];
+
+		appendStringInfo(buf, "; subxacts:");
+		for (i = 0; i < xlrec->nsubxacts; i++)
+			appendStringInfo(buf, " %u", xacts[i]);
+	}
+}
+
+static void
+xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
+{
+	int			i;
+
+	appendStringInfo(buf, "subxacts:");
+
+	for (i = 0; i < xlrec->nsubxacts; i++)
+		appendStringInfo(buf, " %u", xlrec->xsub[i]);
+}
+
+void
+xact_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_XACT_COMMIT_COMPACT)
+	{
+		xl_xact_commit_compact *xlrec = (xl_xact_commit_compact *) rec;
+
+		appendStringInfo(buf, "commit: ");
+		xact_desc_commit_compact(buf, xlrec);
+	}
+	else if (info == XLOG_XACT_COMMIT)
+	{
+		xl_xact_commit *xlrec = (xl_xact_commit *) rec;
+
+		appendStringInfo(buf, "commit: ");
+		xact_desc_commit(buf, xlrec);
+	}
+	else if (info == XLOG_XACT_ABORT)
+	{
+		xl_xact_abort *xlrec = (xl_xact_abort *) rec;
+
+		appendStringInfo(buf, "abort: ");
+		xact_desc_abort(buf, xlrec);
+	}
+	else if (info == XLOG_XACT_PREPARE)
+	{
+		appendStringInfo(buf, "prepare");
+	}
+	else if (info == XLOG_XACT_COMMIT_PREPARED)
+	{
+		xl_xact_commit_prepared *xlrec = (xl_xact_commit_prepared *) rec;
+
+		appendStringInfo(buf, "commit prepared %u: ", xlrec->xid);
+		xact_desc_commit(buf, &xlrec->crec);
+	}
+	else if (info == XLOG_XACT_ABORT_PREPARED)
+	{
+		xl_xact_abort_prepared *xlrec = (xl_xact_abort_prepared *) rec;
+
+		appendStringInfo(buf, "abort prepared %u: ", xlrec->xid);
+		xact_desc_abort(buf, &xlrec->arec);
+	}
+	else if (info == XLOG_XACT_ASSIGNMENT)
+	{
+		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
+
+		/*
+		 * Note that we ignore the WAL record's xid, since we're more
+		 * interested in the top-level xid that issued the record and which
+		 * xids are being reported here.
+		 */
+		appendStringInfo(buf, "xid assignment xtop %u: ", xlrec->xtop);
+		xact_desc_assignment(buf, xlrec);
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
new file mode 100644
index 0000000000000000000000000000000000000000..862e3fa754eb66cb6b42d8c8ef94e07667acad9f
--- /dev/null
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * xlogdesc.c
+ *    rmgr descriptor routines for access/transam/xlog.c
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    src/backend/access/rmgrdesc/xlogdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/xlog_internal.h"
+#include "catalog/pg_control.h"
+#include "utils/guc.h"
+
+/*
+ * GUC support
+ */
+const struct config_enum_entry wal_level_options[] = {
+	{"minimal", WAL_LEVEL_MINIMAL, false},
+	{"archive", WAL_LEVEL_ARCHIVE, false},
+	{"hot_standby", WAL_LEVEL_HOT_STANDBY, false},
+	{NULL, 0, false}
+};
+
+void
+xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
+{
+	uint8		info = xl_info & ~XLR_INFO_MASK;
+
+	if (info == XLOG_CHECKPOINT_SHUTDOWN ||
+		info == XLOG_CHECKPOINT_ONLINE)
+	{
+		CheckPoint *checkpoint = (CheckPoint *) rec;
+
+		appendStringInfo(buf, "checkpoint: redo %X/%X; "
+				   "tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; "
+						 "oldest xid %u in DB %u; oldest running xid %u; %s",
+						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
+						 checkpoint->ThisTimeLineID,
+						 checkpoint->fullPageWrites ? "true" : "false",
+						 checkpoint->nextXidEpoch, checkpoint->nextXid,
+						 checkpoint->nextOid,
+						 checkpoint->nextMulti,
+						 checkpoint->nextMultiOffset,
+						 checkpoint->oldestXid,
+						 checkpoint->oldestXidDB,
+						 checkpoint->oldestActiveXid,
+				 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
+	}
+	else if (info == XLOG_NOOP)
+	{
+		appendStringInfo(buf, "xlog no-op");
+	}
+	else if (info == XLOG_NEXTOID)
+	{
+		Oid			nextOid;
+
+		memcpy(&nextOid, rec, sizeof(Oid));
+		appendStringInfo(buf, "nextOid: %u", nextOid);
+	}
+	else if (info == XLOG_SWITCH)
+	{
+		appendStringInfo(buf, "xlog switch");
+	}
+	else if (info == XLOG_RESTORE_POINT)
+	{
+		xl_restore_point *xlrec = (xl_restore_point *) rec;
+
+		appendStringInfo(buf, "restore point: %s", xlrec->rp_name);
+
+	}
+	else if (info == XLOG_BACKUP_END)
+	{
+		XLogRecPtr	startpoint;
+
+		memcpy(&startpoint, rec, sizeof(XLogRecPtr));
+		appendStringInfo(buf, "backup end: %X/%X",
+						 (uint32) (startpoint >> 32), (uint32) startpoint);
+	}
+	else if (info == XLOG_PARAMETER_CHANGE)
+	{
+		xl_parameter_change xlrec;
+		const char *wal_level_str;
+		const struct config_enum_entry *entry;
+
+		memcpy(&xlrec, rec, sizeof(xl_parameter_change));
+
+		/* Find a string representation for wal_level */
+		wal_level_str = "?";
+		for (entry = wal_level_options; entry->name; entry++)
+		{
+			if (entry->val == xlrec.wal_level)
+			{
+				wal_level_str = entry->name;
+				break;
+			}
+		}
+
+		appendStringInfo(buf, "parameter change: max_connections=%d max_prepared_xacts=%d max_locks_per_xact=%d wal_level=%s",
+						 xlrec.MaxConnections,
+						 xlrec.max_prepared_xacts,
+						 xlrec.max_locks_per_xact,
+						 wal_level_str);
+	}
+	else if (info == XLOG_FPW_CHANGE)
+	{
+		bool		fpw;
+
+		memcpy(&fpw, rec, sizeof(bool));
+		appendStringInfo(buf, "full_page_writes: %s", fpw ? "true" : "false");
+	}
+	else
+		appendStringInfo(buf, "UNKNOWN");
+}
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index 26bbd656c100ee96d530fdc3d0d93f28be094af4..7ee225ec23606cecdf5a3dd9d8718867c96624d1 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -18,7 +18,7 @@
 #include "access/genam.h"
 #include "access/spgist_private.h"
 #include "access/transam.h"
-#include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
diff --git a/src/backend/access/spgist/spgxlog.c b/src/backend/access/spgist/spgxlog.c
index 8746b353080f96789089f16ffa3631a0206d6357..2a874a2f16b4c2f70b6c158c69e6f39e1a6ed37f 100644
--- a/src/backend/access/spgist/spgxlog.c
+++ b/src/backend/access/spgist/spgxlog.c
@@ -1113,78 +1113,6 @@ spg_redo(XLogRecPtr lsn, XLogRecord *record)
 	MemoryContextReset(opCtx);
 }
 
-static void
-out_target(StringInfo buf, RelFileNode node)
-{
-	appendStringInfo(buf, "rel %u/%u/%u ",
-					 node.spcNode, node.dbNode, node.relNode);
-}
-
-void
-spg_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	switch (info)
-	{
-		case XLOG_SPGIST_CREATE_INDEX:
-			appendStringInfo(buf, "create_index: rel %u/%u/%u",
-							 ((RelFileNode *) rec)->spcNode,
-							 ((RelFileNode *) rec)->dbNode,
-							 ((RelFileNode *) rec)->relNode);
-			break;
-		case XLOG_SPGIST_ADD_LEAF:
-			out_target(buf, ((spgxlogAddLeaf *) rec)->node);
-			appendStringInfo(buf, "add leaf to page: %u",
-							 ((spgxlogAddLeaf *) rec)->blknoLeaf);
-			break;
-		case XLOG_SPGIST_MOVE_LEAFS:
-			out_target(buf, ((spgxlogMoveLeafs *) rec)->node);
-			appendStringInfo(buf, "move %u leafs from page %u to page %u",
-							 ((spgxlogMoveLeafs *) rec)->nMoves,
-							 ((spgxlogMoveLeafs *) rec)->blknoSrc,
-							 ((spgxlogMoveLeafs *) rec)->blknoDst);
-			break;
-		case XLOG_SPGIST_ADD_NODE:
-			out_target(buf, ((spgxlogAddNode *) rec)->node);
-			appendStringInfo(buf, "add node to %u:%u",
-							 ((spgxlogAddNode *) rec)->blkno,
-							 ((spgxlogAddNode *) rec)->offnum);
-			break;
-		case XLOG_SPGIST_SPLIT_TUPLE:
-			out_target(buf, ((spgxlogSplitTuple *) rec)->node);
-			appendStringInfo(buf, "split node %u:%u to %u:%u",
-							 ((spgxlogSplitTuple *) rec)->blknoPrefix,
-							 ((spgxlogSplitTuple *) rec)->offnumPrefix,
-							 ((spgxlogSplitTuple *) rec)->blknoPostfix,
-							 ((spgxlogSplitTuple *) rec)->offnumPostfix);
-			break;
-		case XLOG_SPGIST_PICKSPLIT:
-			out_target(buf, ((spgxlogPickSplit *) rec)->node);
-			appendStringInfo(buf, "split leaf page");
-			break;
-		case XLOG_SPGIST_VACUUM_LEAF:
-			out_target(buf, ((spgxlogVacuumLeaf *) rec)->node);
-			appendStringInfo(buf, "vacuum leaf tuples on page %u",
-							 ((spgxlogVacuumLeaf *) rec)->blkno);
-			break;
-		case XLOG_SPGIST_VACUUM_ROOT:
-			out_target(buf, ((spgxlogVacuumRoot *) rec)->node);
-			appendStringInfo(buf, "vacuum leaf tuples on root page %u",
-							 ((spgxlogVacuumRoot *) rec)->blkno);
-			break;
-		case XLOG_SPGIST_VACUUM_REDIRECT:
-			out_target(buf, ((spgxlogVacuumRedirect *) rec)->node);
-			appendStringInfo(buf, "vacuum redirect tuples on page %u, newest XID %u",
-							 ((spgxlogVacuumRedirect *) rec)->blkno,
-						 ((spgxlogVacuumRedirect *) rec)->newestRedirectXid);
-			break;
-		default:
-			appendStringInfo(buf, "unknown spgist op code %u", info);
-			break;
-	}
-}
-
 void
 spg_xlog_startup(void)
 {
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 7f2f6921d5db4de494633788aa407a1f6f85e72d..e3fd56dd2ba4a6fc4a3b78df0329f5cf992b8d70 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -768,26 +768,3 @@ clog_redo(XLogRecPtr lsn, XLogRecord *record)
 	else
 		elog(PANIC, "clog_redo: unknown op code %u", info);
 }
-
-void
-clog_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == CLOG_ZEROPAGE)
-	{
-		int			pageno;
-
-		memcpy(&pageno, rec, sizeof(int));
-		appendStringInfo(buf, "zeropage: %d", pageno);
-	}
-	else if (info == CLOG_TRUNCATE)
-	{
-		int			pageno;
-
-		memcpy(&pageno, rec, sizeof(int));
-		appendStringInfo(buf, "truncate before: %d", pageno);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 8bdf387917122d3760f92108a2bf3090241e7e21..d76105e4558340c250f655d80d0502980e4478e4 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -2053,36 +2053,3 @@ multixact_redo(XLogRecPtr lsn, XLogRecord *record)
 	else
 		elog(PANIC, "multixact_redo: unknown op code %u", info);
 }
-
-void
-multixact_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
-	{
-		int			pageno;
-
-		memcpy(&pageno, rec, sizeof(int));
-		appendStringInfo(buf, "zero offsets page: %d", pageno);
-	}
-	else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
-	{
-		int			pageno;
-
-		memcpy(&pageno, rec, sizeof(int));
-		appendStringInfo(buf, "zero members page: %d", pageno);
-	}
-	else if (info == XLOG_MULTIXACT_CREATE_ID)
-	{
-		xl_multixact_create *xlrec = (xl_multixact_create *) rec;
-		int			i;
-
-		appendStringInfo(buf, "create multixact %u offset %u:",
-						 xlrec->mid, xlrec->moff);
-		for (i = 0; i < xlrec->nxids; i++)
-			appendStringInfo(buf, " %u", xlrec->xids[i]);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index aafd73fbd5bcfc4a0e90412f4fdb862fdca21ca2..cc210a7e59c616e98253e057d98832053c1c5043 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -17,7 +17,7 @@
 #include "access/spgist.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
-#include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
 #include "commands/dbcommands.h"
 #include "commands/sequence.h"
 #include "commands/tablespace.h"
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 10386dadce5cbd6a7f8ce2e71e64a0406acf9f14..349bdbcd9298225a9f6d7b539681ce0160e11842 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -4825,176 +4825,3 @@ xact_redo(XLogRecPtr lsn, XLogRecord *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
-
-static void
-xact_desc_commit(StringInfo buf, xl_xact_commit *xlrec)
-{
-	int			i;
-	TransactionId *subxacts;
-
-	subxacts = (TransactionId *) &xlrec->xnodes[xlrec->nrels];
-
-	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
-
-	if (xlrec->nrels > 0)
-	{
-		appendStringInfo(buf, "; rels:");
-		for (i = 0; i < xlrec->nrels; i++)
-		{
-			char	   *path = relpathperm(xlrec->xnodes[i], MAIN_FORKNUM);
-
-			appendStringInfo(buf, " %s", path);
-			pfree(path);
-		}
-	}
-	if (xlrec->nsubxacts > 0)
-	{
-		appendStringInfo(buf, "; subxacts:");
-		for (i = 0; i < xlrec->nsubxacts; i++)
-			appendStringInfo(buf, " %u", subxacts[i]);
-	}
-	if (xlrec->nmsgs > 0)
-	{
-		SharedInvalidationMessage *msgs;
-
-		msgs = (SharedInvalidationMessage *) &subxacts[xlrec->nsubxacts];
-
-		if (XactCompletionRelcacheInitFileInval(xlrec->xinfo))
-			appendStringInfo(buf, "; relcache init file inval dbid %u tsid %u",
-							 xlrec->dbId, xlrec->tsId);
-
-		appendStringInfo(buf, "; inval msgs:");
-		for (i = 0; i < xlrec->nmsgs; i++)
-		{
-			SharedInvalidationMessage *msg = &msgs[i];
-
-			if (msg->id >= 0)
-				appendStringInfo(buf, " catcache %d", msg->id);
-			else if (msg->id == SHAREDINVALCATALOG_ID)
-				appendStringInfo(buf, " catalog %u", msg->cat.catId);
-			else if (msg->id == SHAREDINVALRELCACHE_ID)
-				appendStringInfo(buf, " relcache %u", msg->rc.relId);
-			/* remaining cases not expected, but print something anyway */
-			else if (msg->id == SHAREDINVALSMGR_ID)
-				appendStringInfo(buf, " smgr");
-			else if (msg->id == SHAREDINVALRELMAP_ID)
-				appendStringInfo(buf, " relmap");
-			else
-				appendStringInfo(buf, " unknown id %d", msg->id);
-		}
-	}
-}
-
-static void
-xact_desc_commit_compact(StringInfo buf, xl_xact_commit_compact *xlrec)
-{
-	int			i;
-
-	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
-
-	if (xlrec->nsubxacts > 0)
-	{
-		appendStringInfo(buf, "; subxacts:");
-		for (i = 0; i < xlrec->nsubxacts; i++)
-			appendStringInfo(buf, " %u", xlrec->subxacts[i]);
-	}
-}
-
-static void
-xact_desc_abort(StringInfo buf, xl_xact_abort *xlrec)
-{
-	int			i;
-
-	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
-	if (xlrec->nrels > 0)
-	{
-		appendStringInfo(buf, "; rels:");
-		for (i = 0; i < xlrec->nrels; i++)
-		{
-			char	   *path = relpathperm(xlrec->xnodes[i], MAIN_FORKNUM);
-
-			appendStringInfo(buf, " %s", path);
-			pfree(path);
-		}
-	}
-	if (xlrec->nsubxacts > 0)
-	{
-		TransactionId *xacts = (TransactionId *)
-		&xlrec->xnodes[xlrec->nrels];
-
-		appendStringInfo(buf, "; subxacts:");
-		for (i = 0; i < xlrec->nsubxacts; i++)
-			appendStringInfo(buf, " %u", xacts[i]);
-	}
-}
-
-static void
-xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
-{
-	int			i;
-
-	appendStringInfo(buf, "subxacts:");
-
-	for (i = 0; i < xlrec->nsubxacts; i++)
-		appendStringInfo(buf, " %u", xlrec->xsub[i]);
-}
-
-void
-xact_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_XACT_COMMIT_COMPACT)
-	{
-		xl_xact_commit_compact *xlrec = (xl_xact_commit_compact *) rec;
-
-		appendStringInfo(buf, "commit: ");
-		xact_desc_commit_compact(buf, xlrec);
-	}
-	else if (info == XLOG_XACT_COMMIT)
-	{
-		xl_xact_commit *xlrec = (xl_xact_commit *) rec;
-
-		appendStringInfo(buf, "commit: ");
-		xact_desc_commit(buf, xlrec);
-	}
-	else if (info == XLOG_XACT_ABORT)
-	{
-		xl_xact_abort *xlrec = (xl_xact_abort *) rec;
-
-		appendStringInfo(buf, "abort: ");
-		xact_desc_abort(buf, xlrec);
-	}
-	else if (info == XLOG_XACT_PREPARE)
-	{
-		appendStringInfo(buf, "prepare");
-	}
-	else if (info == XLOG_XACT_COMMIT_PREPARED)
-	{
-		xl_xact_commit_prepared *xlrec = (xl_xact_commit_prepared *) rec;
-
-		appendStringInfo(buf, "commit prepared %u: ", xlrec->xid);
-		xact_desc_commit(buf, &xlrec->crec);
-	}
-	else if (info == XLOG_XACT_ABORT_PREPARED)
-	{
-		xl_xact_abort_prepared *xlrec = (xl_xact_abort_prepared *) rec;
-
-		appendStringInfo(buf, "abort prepared %u: ", xlrec->xid);
-		xact_desc_abort(buf, &xlrec->arec);
-	}
-	else if (info == XLOG_XACT_ASSIGNMENT)
-	{
-		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
-
-		/*
-		 * Note that we ignore the WAL record's xid, since we're more
-		 * interested in the top-level xid that issued the record and which
-		 * xids are being reported here.
-		 */
-		appendStringInfo(buf, "xid assignment xtop %u: ", xlrec->xtop);
-		xact_desc_assignment(buf, xlrec);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index ff1973c8c8318317821a6a2f0efbef14776ba0c7..8b19976c6855fd40a5b99d0a8d427289e46f4160 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -99,16 +99,10 @@ bool		XLOG_DEBUG = false;
  */
 #define XLOGfileslop	(2*CheckPointSegments + 1)
 
+
 /*
  * GUC support
  */
-const struct config_enum_entry wal_level_options[] = {
-	{"minimal", WAL_LEVEL_MINIMAL, false},
-	{"archive", WAL_LEVEL_ARCHIVE, false},
-	{"hot_standby", WAL_LEVEL_HOT_STANDBY, false},
-	{NULL, 0, false}
-};
-
 const struct config_enum_entry sync_method_options[] = {
 	{"fsync", SYNC_METHOD_FSYNC, false},
 #ifdef HAVE_FSYNC_WRITETHROUGH
@@ -588,25 +582,6 @@ static bool InRedo = false;
 /* Have we launched bgwriter during recovery? */
 static bool bgwriterLaunched = false;
 
-/*
- * Information logged when we detect a change in one of the parameters
- * important for Hot Standby.
- */
-typedef struct xl_parameter_change
-{
-	int			MaxConnections;
-	int			max_prepared_xacts;
-	int			max_locks_per_xact;
-	int			wal_level;
-} xl_parameter_change;
-
-/* logs restore point */
-typedef struct xl_restore_point
-{
-	TimestampTz rp_time;
-	char		rp_name[MAXFNAMELEN];
-} xl_restore_point;
-
 
 static void readRecoveryCommandFile(void);
 static void exitArchiveRecovery(TimeLineID endTLI, XLogSegNo endLogSegNo);
@@ -8031,97 +8006,6 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
 	}
 }
 
-void
-xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_CHECKPOINT_SHUTDOWN ||
-		info == XLOG_CHECKPOINT_ONLINE)
-	{
-		CheckPoint *checkpoint = (CheckPoint *) rec;
-
-		appendStringInfo(buf, "checkpoint: redo %X/%X; "
-				   "tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; "
-						 "oldest xid %u in DB %u; oldest running xid %u; %s",
-						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
-						 checkpoint->ThisTimeLineID,
-						 checkpoint->fullPageWrites ? "true" : "false",
-						 checkpoint->nextXidEpoch, checkpoint->nextXid,
-						 checkpoint->nextOid,
-						 checkpoint->nextMulti,
-						 checkpoint->nextMultiOffset,
-						 checkpoint->oldestXid,
-						 checkpoint->oldestXidDB,
-						 checkpoint->oldestActiveXid,
-				 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
-	}
-	else if (info == XLOG_NOOP)
-	{
-		appendStringInfo(buf, "xlog no-op");
-	}
-	else if (info == XLOG_NEXTOID)
-	{
-		Oid			nextOid;
-
-		memcpy(&nextOid, rec, sizeof(Oid));
-		appendStringInfo(buf, "nextOid: %u", nextOid);
-	}
-	else if (info == XLOG_SWITCH)
-	{
-		appendStringInfo(buf, "xlog switch");
-	}
-	else if (info == XLOG_RESTORE_POINT)
-	{
-		xl_restore_point *xlrec = (xl_restore_point *) rec;
-
-		appendStringInfo(buf, "restore point: %s", xlrec->rp_name);
-
-	}
-	else if (info == XLOG_BACKUP_END)
-	{
-		XLogRecPtr	startpoint;
-
-		memcpy(&startpoint, rec, sizeof(XLogRecPtr));
-		appendStringInfo(buf, "backup end: %X/%X",
-						 (uint32) (startpoint >> 32), (uint32) startpoint);
-	}
-	else if (info == XLOG_PARAMETER_CHANGE)
-	{
-		xl_parameter_change xlrec;
-		const char *wal_level_str;
-		const struct config_enum_entry *entry;
-
-		memcpy(&xlrec, rec, sizeof(xl_parameter_change));
-
-		/* Find a string representation for wal_level */
-		wal_level_str = "?";
-		for (entry = wal_level_options; entry->name; entry++)
-		{
-			if (entry->val == xlrec.wal_level)
-			{
-				wal_level_str = entry->name;
-				break;
-			}
-		}
-
-		appendStringInfo(buf, "parameter change: max_connections=%d max_prepared_xacts=%d max_locks_per_xact=%d wal_level=%s",
-						 xlrec.MaxConnections,
-						 xlrec.max_prepared_xacts,
-						 xlrec.max_locks_per_xact,
-						 wal_level_str);
-	}
-	else if (info == XLOG_FPW_CHANGE)
-	{
-		bool		fpw;
-
-		memcpy(&fpw, rec, sizeof(bool));
-		appendStringInfo(buf, "full_page_writes: %s", fpw ? "true" : "false");
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
-
 #ifdef WAL_DEBUG
 
 static void
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8818b680c8b62a8d5e9bbd2841a8b6a57ad9eeb9..d93d273eb1b6cdce7acf7ca04ee988868ec1534d 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -49,6 +49,7 @@
 #include "catalog/pg_type.h"
 #include "catalog/pg_type_fn.h"
 #include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
 #include "commands/tablecmds.h"
 #include "commands/typecmds.h"
 #include "miscadmin.h"
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 993bc49c2a2d6b73dbde6fb9682adc9afbbe32f9..24462824937f759a849425ce9b58531738b9d992 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -24,6 +24,7 @@
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
 #include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
 #include "storage/freespace.h"
 #include "storage/smgr.h"
 #include "utils/memutils.h"
@@ -60,30 +61,6 @@ typedef struct PendingRelDelete
 
 static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
 
-/*
- * Declarations for smgr-related XLOG records
- *
- * Note: we log file creation and truncation here, but logging of deletion
- * actions is handled by xact.c, because it is part of transaction commit.
- */
-
-/* XLOG gives us high 4 bits */
-#define XLOG_SMGR_CREATE	0x10
-#define XLOG_SMGR_TRUNCATE	0x20
-
-typedef struct xl_smgr_create
-{
-	RelFileNode rnode;
-	ForkNumber	forkNum;
-} xl_smgr_create;
-
-typedef struct xl_smgr_truncate
-{
-	BlockNumber blkno;
-	RelFileNode rnode;
-} xl_smgr_truncate;
-
-
 /*
  * RelationCreateStorage
  *		Create physical storage for a relation.
@@ -523,29 +500,3 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
 	else
 		elog(PANIC, "smgr_redo: unknown op code %u", info);
 }
-
-void
-smgr_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_SMGR_CREATE)
-	{
-		xl_smgr_create *xlrec = (xl_smgr_create *) rec;
-		char	   *path = relpathperm(xlrec->rnode, xlrec->forkNum);
-
-		appendStringInfo(buf, "file create: %s", path);
-		pfree(path);
-	}
-	else if (info == XLOG_SMGR_TRUNCATE)
-	{
-		xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec;
-		char	   *path = relpathperm(xlrec->rnode, MAIN_FORKNUM);
-
-		appendStringInfo(buf, "file truncate: %s to %u blocks", path,
-						 xlrec->blkno);
-		pfree(path);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index cdbce97c4ff2f2d2c65ef78c239215d625ff1abc..3c13c470fdc312bb396538428befb7f3b1de3f02 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -1992,27 +1992,3 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
 	else
 		elog(PANIC, "dbase_redo: unknown op code %u", info);
 }
-
-void
-dbase_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_DBASE_CREATE)
-	{
-		xl_dbase_create_rec *xlrec = (xl_dbase_create_rec *) rec;
-
-		appendStringInfo(buf, "create db: copy dir %u/%u to %u/%u",
-						 xlrec->src_db_id, xlrec->src_tablespace_id,
-						 xlrec->db_id, xlrec->tablespace_id);
-	}
-	else if (info == XLOG_DBASE_DROP)
-	{
-		xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) rec;
-
-		appendStringInfo(buf, "drop db: dir %u/%u",
-						 xlrec->db_id, xlrec->tablespace_id);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 4f55830ae4d3319b9dd102404a1f19aeeeebd174..634ce3f7188933eb354468a2e5f337cfa57cbd57 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1595,21 +1595,3 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record)
 
 	pfree(localpage);
 }
-
-void
-seq_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-	xl_seq_rec *xlrec = (xl_seq_rec *) rec;
-
-	if (info == XLOG_SEQ_LOG)
-		appendStringInfo(buf, "log: ");
-	else
-	{
-		appendStringInfo(buf, "UNKNOWN");
-		return;
-	}
-
-	appendStringInfo(buf, "rel %u/%u/%u",
-			   xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
-}
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 08899aeececa1641b8b0eca67a9b7a2ffab053e6..5081d8411ec833202c3013ec47bd19d8de2c6c67 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -1424,25 +1424,3 @@ tblspc_redo(XLogRecPtr lsn, XLogRecord *record)
 	else
 		elog(PANIC, "tblspc_redo: unknown op code %u", info);
 }
-
-void
-tblspc_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_TBLSPC_CREATE)
-	{
-		xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) rec;
-
-		appendStringInfo(buf, "create tablespace: %u \"%s\"",
-						 xlrec->ts_id, xlrec->ts_path);
-	}
-	else if (info == XLOG_TBLSPC_DROP)
-	{
-		xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) rec;
-
-		appendStringInfo(buf, "drop tablespace: %u", xlrec->ts_id);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 905d33148a96d3f023f027c398338b0b428bd3cd..9d2003867b5d552a8e8861de4d534eab43451236 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -787,54 +787,6 @@ standby_redo(XLogRecPtr lsn, XLogRecord *record)
 		elog(PANIC, "standby_redo: unknown op code %u", info);
 }
 
-static void
-standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec)
-{
-	int			i;
-
-	appendStringInfo(buf, " nextXid %u latestCompletedXid %u oldestRunningXid %u",
-					 xlrec->nextXid,
-					 xlrec->latestCompletedXid,
-					 xlrec->oldestRunningXid);
-	if (xlrec->xcnt > 0)
-	{
-		appendStringInfo(buf, "; %d xacts:", xlrec->xcnt);
-		for (i = 0; i < xlrec->xcnt; i++)
-			appendStringInfo(buf, " %u", xlrec->xids[i]);
-	}
-
-	if (xlrec->subxid_overflow)
-		appendStringInfo(buf, "; subxid ovf");
-}
-
-void
-standby_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_STANDBY_LOCK)
-	{
-		xl_standby_locks *xlrec = (xl_standby_locks *) rec;
-		int			i;
-
-		appendStringInfo(buf, "AccessExclusive locks:");
-
-		for (i = 0; i < xlrec->nlocks; i++)
-			appendStringInfo(buf, " xid %u db %u rel %u",
-							 xlrec->locks[i].xid, xlrec->locks[i].dbOid,
-							 xlrec->locks[i].relOid);
-	}
-	else if (info == XLOG_RUNNING_XACTS)
-	{
-		xl_running_xacts *xlrec = (xl_running_xacts *) rec;
-
-		appendStringInfo(buf, " running xacts:");
-		standby_desc_running_xacts(buf, xlrec);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
-
 /*
  * 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 18bf9daf8bf7474ebce8a6d375a3530ca492650b..d0d96ad3fd4e3ddb77005e7f6ccf6853b2e2ba99 100644
--- a/src/backend/utils/cache/relmapper.c
+++ b/src/backend/utils/cache/relmapper.c
@@ -891,19 +891,3 @@ relmap_redo(XLogRecPtr lsn, XLogRecord *record)
 	else
 		elog(PANIC, "relmap_redo: unknown op code %u", info);
 }
-
-void
-relmap_desc(StringInfo buf, uint8 xl_info, char *rec)
-{
-	uint8		info = xl_info & ~XLR_INFO_MASK;
-
-	if (info == XLOG_RELMAP_UPDATE)
-	{
-		xl_relmap_update *xlrec = (xl_relmap_update *) rec;
-
-		appendStringInfo(buf, "update relmap: database %u tablespace %u size %u",
-						 xlrec->dbid, xlrec->tsid, xlrec->nbytes);
-	}
-	else
-		appendStringInfo(buf, "UNKNOWN");
-}
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index b70a62052c4d6cba5ca294d29e8bd3858d09fdb0..89252d023015e590e05cdcaa09e238072bb2bb00 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -205,6 +205,25 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader;
 			 (uint32) ((logSegNo) / XLogSegmentsPerXLogId), \
 			 (uint32) ((logSegNo) % XLogSegmentsPerXLogId), offset)
 
+/*
+ * Information logged when we detect a change in one of the parameters
+ * important for Hot Standby.
+ */
+typedef struct xl_parameter_change
+{
+	int			MaxConnections;
+	int			max_prepared_xacts;
+	int			max_locks_per_xact;
+	int			wal_level;
+} xl_parameter_change;
+
+/* logs restore point */
+typedef struct xl_restore_point
+{
+	TimestampTz rp_time;
+	char		rp_name[MAXFNAMELEN];
+} xl_restore_point;
+
 
 /*
  * Method table for resource managers.
diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h
index d5103a88f4c3f40394c18fdd575c9dbdf4a5a039..a4c3ba6b4f2feb39633445d2915bcfff49c912a3 100644
--- a/src/include/catalog/storage.h
+++ b/src/include/catalog/storage.h
@@ -14,7 +14,6 @@
 #ifndef STORAGE_H
 #define STORAGE_H
 
-#include "access/xlog.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
 #include "utils/relcache.h"
@@ -34,9 +33,4 @@ extern void AtSubCommit_smgr(void);
 extern void AtSubAbort_smgr(void);
 extern void PostPrepare_smgr(void);
 
-extern void log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum);
-
-extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record);
-extern void smgr_desc(StringInfo buf, uint8 xl_info, char *rec);
-
 #endif   /* STORAGE_H */
diff --git a/src/include/catalog/storage_xlog.h b/src/include/catalog/storage_xlog.h
new file mode 100644
index 0000000000000000000000000000000000000000..ae3a928231328d51e5745ab58bba23c1f2462d6d
--- /dev/null
+++ b/src/include/catalog/storage_xlog.h
@@ -0,0 +1,49 @@
+/*-------------------------------------------------------------------------
+ *
+ * storage_xlog.h
+ *	  prototypes for XLog support for backend/catalog/storage.c
+ *
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/storage_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGE_XLOG_H
+#define STORAGE_XLOG_H
+
+#include "access/xlog.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+
+/*
+ * Declarations for smgr-related XLOG records
+ *
+ * Note: we log file creation and truncation here, but logging of deletion
+ * actions is handled by xact.c, because it is part of transaction commit.
+ */
+
+/* XLOG gives us high 4 bits */
+#define XLOG_SMGR_CREATE	0x10
+#define XLOG_SMGR_TRUNCATE	0x20
+
+typedef struct xl_smgr_create
+{
+	RelFileNode rnode;
+	ForkNumber	forkNum;
+} xl_smgr_create;
+
+typedef struct xl_smgr_truncate
+{
+	BlockNumber blkno;
+	RelFileNode rnode;
+} xl_smgr_truncate;
+
+extern void log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum);
+
+extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record);
+extern void smgr_desc(StringInfo buf, uint8 xl_info, char *rec);
+
+#endif   /* STORAGE_XLOG_H */