Hello,

Based on the previous discussions, I've modified the existing patch.

>+   void        (*rm_checkConsistency) (XLogReaderState *record);
>All your _checkConsistency functions share the same pattern, in short
>they all use a for loop for each block, call each time
>XLogReadBufferExtended, etc. And this leads to a *lot* of duplication.
>You would get a reduction by a couple of hundreds of lines by having a
>smarter refactoring. And to be honest, if I look at your patch what I
>think is the correct way of doing things is to add to the rmgr not
>this check consistency function, but just a pointer to the masking
>function.
+1. In rmgrlist, I've added a pointer to the masking function for each rmid.
A common function named checkConsistency calls these masking functions
based on their rmid and does comparison for each block.

>> - If WAL consistency check is enabled for a rmgrID, we always include
>> the backup image in the WAL record.
>
>What happens if wal_consistency has different settings on a standby
>and its master? If for example it is set to 'all' on the standby, and
>'none' on the master, or vice-versa, how do things react? An update of
>this parameter should be WAL-logged, no?
If wal_consistency is enabled for a rmid, standby will always check whether
backup image exists or not i.e. BKPBLOCK_HAS_IMAGE is set or not.
(I guess Amit and Robert also suggested the same in the thread)
Basically, BKPBLOCK_HAS_IMAGE is set if a block contains image and
BKPIMAGE_IS_REQUIRED_FOR_REDO (I've added this one) is set if that backup
image is required during redo. When we decode a wal record, has_image
flag of DecodedBkpBlock is set to BKPIMAGE_IS_REQUIRED_FOR_REDO.

>+       if (pg_strcasecmp(tok, "Heap2") == 0)
>+       {
>+           newwalconsistency[RM_HEAP2_ID] = true;
>+       }
>Thinking more about it, I guess that we had better change the
>definition list of rmgrs in rmgr.h and get something closer to
>RmgrDescData that pg_xlogdump has to avoid all this stanza by
>completing it with the name of the rmgr. The only special cases that
>this code path would need to take care of would be then 'none' and
>'all'. You could do this refactoring on top of the main patch to
>simplify it as it is rather big (1.7k lines).
I've modified it exactly like pg_xlogdump does. Additionally, it checks
whether masking function is defined for the rmid or not. Hence, in future,
if we want to include any other rmid for wal consistency check, we just need
to define its masking function.

>> - In recovery tests (src/test/recovery/t), I've added wal_consistency
>> parameter in the existing scripts. This feature doesn't change the
>> expected output. If there is any inconsistency, it can be verified in
>> corresponding log file.
>
>I am afraid that just generating a WARNING message is going to be
>useless for the buildfarm. If we want to detect errors, we could for
>example have an additional GUC to trigger an ERROR or a FATAL, taking
>down the cluster, and allowing things to show in red on a platform.
For now, I've kept this as a WARNING message to detect all inconsistencies
at once. Once, the patch is finalized, I'll modify it as an ERROR message.

Thoughts?


-- 
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 9bb1362..3ca64d1 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -26,12 +26,13 @@
 #include "commands/tablespace.h"
 #include "replication/message.h"
 #include "replication/origin.h"
+#include "storage/bufmask.h"
 #include "storage/standby.h"
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup) \
-	{ name, redo, desc, identify, startup, cleanup },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,maskPage) \
+	{ name, redo, desc, identify, startup, cleanup, maskPage },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 2189c22..77e79f5 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -25,6 +25,7 @@
 #include "access/commit_ts.h"
 #include "access/multixact.h"
 #include "access/rewriteheap.h"
+#include "access/rmgr.h"
 #include "access/subtrans.h"
 #include "access/timeline.h"
 #include "access/transam.h"
@@ -95,6 +96,8 @@ bool		EnableHotStandby = false;
 bool		fullPageWrites = true;
 bool		wal_log_hints = false;
 bool		wal_compression = false;
+char		*wal_consistency_string = NULL;
+bool		*wal_consistency = NULL;
 bool		log_checkpoints = false;
 int			sync_method = DEFAULT_SYNC_METHOD;
 int			wal_level = WAL_LEVEL_MINIMAL;
@@ -870,6 +873,7 @@ static void WALInsertLockAcquireExclusive(void);
 static void WALInsertLockRelease(void);
 static void WALInsertLockUpdateInsertingAt(XLogRecPtr insertingAt);
 
+static void checkConsistency(RmgrId rmid, XLogReaderState *record);
 /*
  * Insert an XLOG record represented by an already-constructed chain of data
  * chunks.  This is a low-level routine; to construct the WAL record header
@@ -6944,6 +6948,14 @@ StartupXLOG(void)
 				/* Now apply the WAL record itself */
 				RmgrTable[record->xl_rmid].rm_redo(xlogreader);
 
+				/*
+				 * After redo, check whether the backup pages associated with the WAL record
+				 * are consistent with the existing pages. This check is done only
+				 * if consistency check is enabled for the corresponding rmid.
+				 */
+				if (wal_consistency[record->xl_rmid])
+					checkConsistency(record->xl_rmid, xlogreader);
+
 				/* Pop the error context stack */
 				error_context_stack = errcallback.previous;
 
@@ -11708,3 +11720,80 @@ XLogRequestWalReceiverReply(void)
 {
 	doRequestWalReceiverReply = true;
 }
+
+static void
+checkConsistency(RmgrId rmid, XLogReaderState *record)
+{
+	uint8           info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+	RelFileNode	rnode;
+	ForkNumber	forknum;
+	BlockNumber	blkno;
+	Page		new_page, old_page;
+	int		block_id;
+	int		inconsistent_loc;
+	bool		has_image;
+
+	if (XLogRecHasAnyBlockRefs(record))
+	{
+		old_page = (Page) palloc(BLCKSZ);
+
+		for (block_id = 0; block_id <= record->max_block_id; block_id++)
+		{
+			Buffer buf;
+			char *norm_new_page, *norm_old_page;
+
+			if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+			{
+				/* Caller specified a bogus block_id. Don't do anything. */
+				continue;
+			}
+			/*
+			 * Read the contents from the current buffer
+			 * and store it in a temporary page.
+			 */
+			buf = XLogReadBufferExtended(rnode, forknum, blkno,
+										  RBM_NORMAL);
+			if (!BufferIsValid(buf))
+				continue;
+			new_page = BufferGetPage(buf);
+
+			/*
+			 * Read the contents from the backup copy, stored in WAL record
+			 * and store it in a temporary page. Before restoring, set
+			 * has_image value as true, since RestoreBlockImage checks
+			 * this flag. After restoring the image, restore the value of
+			 * has_image flag.
+			 */
+			has_image = record->blocks[block_id].has_image;
+			record->blocks[block_id].has_image = true;
+			if (!RestoreBlockImage(record, block_id, old_page))
+				elog(ERROR, "failed to restore block image");
+			record->blocks[block_id].has_image = has_image;
+
+			/* Mask pages */
+			norm_new_page = RmgrTable[rmid].rm_maskPage(info, blkno, new_page);
+			norm_old_page = RmgrTable[rmid].rm_maskPage(info, blkno, old_page);
+
+			/* Time to compare the old and new contents */
+			inconsistent_loc = comparePages(norm_new_page, norm_old_page);
+
+			if (inconsistent_loc < BLCKSZ)
+				elog(WARNING,
+					"Inconsistent page (at byte %u) found, rel %u/%u/%u, "
+					"forknum %u, blkno %u", inconsistent_loc,
+					rnode.spcNode, rnode.dbNode, rnode.relNode,
+					forknum, blkno);
+			else
+				elog(DEBUG3,
+					"Consistent page found, rel %u/%u/%u, "
+					"forknum %u, blkno %u",
+					rnode.spcNode, rnode.dbNode, rnode.relNode,
+					forknum, blkno);
+
+			pfree(norm_new_page);
+			pfree(norm_old_page);
+			ReleaseBuffer(buf);
+		}
+		pfree(old_page);
+	}
+}
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 3cd273b..54308dd 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -513,6 +513,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 		XLogRecordBlockCompressHeader cbimg = {0};
 		bool		samerel;
 		bool		is_compressed = false;
+		bool		include_image; /* Whether backup image should be included in WAL record*/
 
 		if (!regbuf->in_use)
 			continue;
@@ -556,7 +557,13 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 		if ((regbuf->flags & REGBUF_WILL_INIT) == REGBUF_WILL_INIT)
 			bkpb.fork_flags |= BKPBLOCK_WILL_INIT;
 
-		if (needs_backup)
+		/*
+		 * If needs_backup is true or wal consistency check is enabled for current rmid,
+		 * we do a fpw for the current block.
+		 */
+		include_image = needs_backup || wal_consistency[rmid];
+
+		if (include_image)
 		{
 			Page		page = regbuf->page;
 			uint16		compressed_len;
@@ -618,6 +625,13 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 
 			bimg.bimg_info = (cbimg.hole_length == 0) ? 0 : BKPIMAGE_HAS_HOLE;
 
+			/*
+			 * Remember that, if WAL consistency check is enabled for the current rmid,
+			 * we always include backup image with the WAL record. But, during redo we
+			 * restore the backup block only if needs_backup is set.
+			 */
+			if (needs_backup)
+				bimg.bimg_info |= BKPIMAGE_IS_REQUIRED_FOR_REDO;
 			if (is_compressed)
 			{
 				bimg.length = compressed_len;
@@ -680,7 +694,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 		/* Ok, copy the header to the scratch buffer */
 		memcpy(scratch, &bkpb, SizeOfXLogRecordBlockHeader);
 		scratch += SizeOfXLogRecordBlockHeader;
-		if (needs_backup)
+		if (include_image)
 		{
 			memcpy(scratch, &bimg, SizeOfXLogRecordBlockImageHeader);
 			scratch += SizeOfXLogRecordBlockImageHeader;
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index f2da505..f41f92a 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1026,6 +1026,12 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 	uint32		datatotal;
 	RelFileNode *rnode = NULL;
 	uint8		block_id;
+	bool checkConsistency = false;
+
+	#ifndef FRONTEND
+	/* Check whether wal consistency check is enabled for the current rmid.*/
+	checkConsistency = wal_consistency[record->xl_rmid];
+	#endif
 
 	ResetDecoder(state);
 
@@ -1114,11 +1120,29 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 			}
 			datatotal += blk->data_len;
 
+			/*
+			 * cross check that has_image is set if wal consistency check
+			 * is enabled for current rmid.
+			 */
+			if (checkConsistency && !blk->has_image)
+			{
+				report_invalid_record(state,
+				 "WAL consistency check is enabled, but BKPBLOCK_HAS_IMAGE not set at %X/%X",
+									  (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr);
+				goto err;
+			}
+
 			if (blk->has_image)
 			{
 				COPY_HEADER_FIELD(&blk->bimg_len, sizeof(uint16));
 				COPY_HEADER_FIELD(&blk->hole_offset, sizeof(uint16));
 				COPY_HEADER_FIELD(&blk->bimg_info, sizeof(uint8));
+				/*
+				 * During redo, backup image is restored if has_image is set. Hence,
+				 * set has_image accordingly.
+				 */
+				blk->has_image = blk->bimg_info & BKPIMAGE_IS_REQUIRED_FOR_REDO;
+
 				if (blk->bimg_info & BKPIMAGE_IS_COMPRESSED)
 				{
 					if (blk->bimg_info & BKPIMAGE_HAS_HOLE)
@@ -1242,7 +1266,11 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 
 		if (!blk->in_use)
 			continue;
-		if (blk->has_image)
+		/*
+		 * If wal consistency check is enabled for current rmid, then it will always
+		 * have a backup image.
+		 */
+		if (blk->has_image || checkConsistency)
 		{
 			blk->bkp_image = ptr;
 			ptr += blk->bimg_len;
diff --git a/src/backend/storage/buffer/Makefile b/src/backend/storage/buffer/Makefile
index 2c10fba..8630dca 100644
--- a/src/backend/storage/buffer/Makefile
+++ b/src/backend/storage/buffer/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/storage/buffer
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = buf_table.o buf_init.o bufmgr.o freelist.o localbuf.o
+OBJS = buf_table.o buf_init.o bufmask.o bufmgr.o freelist.o localbuf.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/buffer/bufmask.c b/src/backend/storage/buffer/bufmask.c
new file mode 100644
index 0000000..d380032
--- /dev/null
+++ b/src/backend/storage/buffer/bufmask.c
@@ -0,0 +1,498 @@
+/*-------------------------------------------------------------------------
+ *
+ * bufmask.c
+ *	  Routines for buffer masking, used to ensure that buffers used for
+ *	  comparison across nodes are in a consistent state.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * Most pages cannot be compared directly, because some parts of the
+ * page are not expected to be byte-by-byte identical. For example,
+ * hint bits or unused space in the page. The strategy is to normalize
+ * all pages by creating a mask of those bits that are not expected to
+ * match.
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/buffer/bufmask.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/brin_page.h"
+#include "access/nbtree.h"
+#include "access/gist.h"
+#include "access/gin_private.h"
+#include "access/hash.h"
+#include "access/htup_details.h"
+#include "access/spgist_private.h"
+#include "commands/sequence.h"
+#include "storage/bufmask.h"
+#include "storage/bufmgr.h"
+
+/* Marker used to mask pages consistently */
+#define MASK_MARKER		0xFF
+
+static void mask_page_lsn(Page page);
+static void mask_page_hint_bits(Page page);
+static void mask_unused_space(Page page);
+static char *mask_common_page(uint8 info, BlockNumber blkno,
+					const char *page, bool maskHints,
+					bool maskUnusedSpace);
+
+/*
+ * Mask Page LSN
+ */
+static void
+mask_page_lsn(Page page)
+{
+	PageHeader phdr = (PageHeader) page;
+	PageXLogRecPtrSet(phdr->pd_lsn, 0xFFFFFFFFFFFFFFFF);
+}
+
+/*
+ * Mask Page hint bits
+ */
+static void
+mask_page_hint_bits(Page page)
+{
+	PageHeader phdr = (PageHeader) page;
+
+	/* Ignore prune_xid (it's like a hint-bit) */
+	phdr->pd_prune_xid = 0xFFFFFFFF;
+
+	/* Ignore PD_PAGE_FULL and PD_HAS_FREE_LINES flags, they are just hints */
+	phdr->pd_flags |= PD_PAGE_FULL | PD_HAS_FREE_LINES;
+
+	/*
+	 * Also mask the all-visible flag.
+	 *
+	 * XXX: It is unfortunate that we have to do this. If the flag is set
+	 * incorrectly, that's serious, and we would like to catch it. If the flag
+	 * is cleared incorrectly, that's serious too. But redo of HEAP_CLEAN
+	 * records don't currently set the flag, even though it is set in the
+	 * master, so we must silence failures that that causes.
+	 */
+	phdr->pd_flags |= PD_ALL_VISIBLE;
+}
+/*
+ * Mask the unused space of a page between pd_lower and pd_upper.
+ */
+static void
+mask_unused_space(Page page)
+{
+	int pd_lower = ((PageHeader) page)->pd_lower;
+	int pd_upper = ((PageHeader) page)->pd_upper;
+	int pd_special = ((PageHeader) page)->pd_special;
+
+	/* Sanity check */
+	if (pd_lower > pd_upper || pd_special < pd_upper ||
+		pd_lower < SizeOfPageHeaderData || pd_special > BLCKSZ)
+	{
+		elog(ERROR, "invalid page pd_lower %u pd_upper %u pd_special %u\n",
+			 pd_lower, pd_upper, pd_special);
+	}
+
+	memset(page + pd_lower, MASK_MARKER, pd_upper - pd_lower);
+}
+
+/*
+ * Mask a xlog page
+ */
+char *
+mask_xlog_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	return mask_common_page(info, blkno, page, false, false);
+}
+
+/*
+ * Mask a heap page
+ */
+char *
+mask_heap_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	Page	page_norm;
+	OffsetNumber off;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+
+	mask_page_hint_bits(page_norm);
+	mask_unused_space(page_norm);
+
+	for (off = 1; off <= PageGetMaxOffsetNumber(page_norm); off++)
+	{
+		ItemId	iid = PageGetItemId(page, off);
+		char   *page_item;
+
+		page_item = (char *) (page_norm + ItemIdGetOffset(iid));
+
+		/*
+		 * Ignore hint bits and command ID.
+		 */
+		if (ItemIdIsNormal(iid))
+		{
+			HeapTupleHeader page_htup = (HeapTupleHeader) page_item;
+
+			page_htup->t_infomask =
+				HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID |
+				HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID;
+			page_htup->t_infomask |= HEAP_XACT_MASK;
+			page_htup->t_choice.t_heap.t_field3.t_cid = 0xFFFFFFFF;
+
+			/*
+			 * For a speculative tuple, the content of t_ctid is conflicting
+			 * between the backup page and current page. Hence, we set it
+			 * to current block number and offset.
+			 */
+			if (HeapTupleHeaderIsSpeculative(page_htup))
+			{
+				ItemPointerSet(&page_htup->t_ctid, blkno, off);
+			}
+		}
+
+		/*
+		 * Ignore any padding bytes after the tuple, when the length of
+		 * the item is not MAXALIGNed.
+		 */
+		if (ItemIdHasStorage(iid))
+		{
+			int len = ItemIdGetLength(iid);
+			int padlen = MAXALIGN(len) - len;
+
+			if (padlen > 0)
+				memset(page_item + len, MASK_MARKER, padlen);
+		}
+	}
+	return (char *)page_norm;
+}
+
+/*
+ * Mask a btree page
+ */
+char *
+mask_btree_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	Page	page_norm;
+	OffsetNumber off;
+	OffsetNumber maxoff;
+	BTPageOpaque maskopaq;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+
+	mask_page_hint_bits(page_norm);
+	mask_unused_space(page_norm);
+
+	maskopaq = (BTPageOpaque)
+			(((char *) page_norm) + ((PageHeader) page_norm)->pd_special);
+	/*
+	 * Mask everything on a DELETED page.
+	 */
+	if (((BTPageOpaque) PageGetSpecialPointer(page_norm))->btpo_flags & BTP_DELETED)
+	{
+		/* Page content, between standard page header and opaque struct */
+		memset(page_norm + SizeOfPageHeaderData, MASK_MARKER,
+			   BLCKSZ - SizeOfPageHeaderData);
+
+		/* pd_lower and upper */
+		memset(&((PageHeader) page_norm)->pd_lower, MASK_MARKER, sizeof(uint16));
+		memset(&((PageHeader) page_norm)->pd_upper, MASK_MARKER, sizeof(uint16));
+	}
+	else
+	{
+		/*
+		 * Mask some line pointer bits, particularly those marked as
+		 * used on a master and unused on a standby.
+		 * XXX: This could be refined.
+		 */
+		maxoff = PageGetMaxOffsetNumber(page_norm);
+		for (off = 1; off <= maxoff; off++)
+		{
+			ItemId iid = PageGetItemId(page_norm, off);
+
+			if (ItemIdIsUsed(iid))
+				iid->lp_flags = LP_UNUSED;
+		}
+	}
+
+	maskopaq->btpo_flags |= BTP_SPLIT_END | BTP_HAS_GARBAGE;
+	maskopaq->btpo_cycleid = 0;
+
+	return (char *)page_norm;
+}
+
+/*
+ * Mask a hash page
+ */
+char *
+mask_hash_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	Page	page_norm;
+	OffsetNumber off;
+	OffsetNumber maxoff;
+	HashPageOpaque opaque;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+
+	mask_page_hint_bits(page_norm);
+	mask_unused_space(page_norm);
+
+	opaque = (HashPageOpaque) PageGetSpecialPointer(page_norm);
+	/*
+	 * Mask everything on a UNUSED page.
+	 */
+	if (opaque->hasho_flag & LH_UNUSED_PAGE)
+	{
+		/* Page content, between standard page header and opaque struct */
+		memset(page_norm + SizeOfPageHeaderData, MASK_MARKER,
+			   BLCKSZ - MAXALIGN(sizeof(HashPageOpaqueData)) - SizeOfPageHeaderData);
+
+		/* pd_lower and upper */
+		memset(&((PageHeader) page_norm)->pd_lower, MASK_MARKER, sizeof(uint16));
+		memset(&((PageHeader) page_norm)->pd_upper, MASK_MARKER, sizeof(uint16));
+	}
+	else if ((opaque->hasho_flag & LH_META_PAGE)== 0)
+	{
+		/*
+		 * For pages other than metapage,
+		 * Mask some line pointer bits, particularly those marked as
+		 * used on a master and unused on a standby.
+		 * XXX: This could be refined.
+		 */
+		maxoff = PageGetMaxOffsetNumber(page_norm);
+		for (off = 1; off <= maxoff; off++)
+		{
+			ItemId iid = PageGetItemId(page_norm, off);
+
+			if (ItemIdIsUsed(iid))
+				iid->lp_flags = LP_UNUSED;
+		}
+	}
+	return (char *)page_norm;
+}
+
+/*
+ * Mask a Gin page
+ */
+char *
+mask_gin_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	Page	page_norm;
+	GinPageOpaque opaque;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+	opaque = GinPageGetOpaque(page_norm);
+
+	/* GIN metapage doesn't use pd_lower/pd_upper. Other page types do. */
+	if (blkno != 0)
+	{
+		mask_page_hint_bits(page_norm);
+
+		/*
+		 * For GIN_DELETED page, the page is initialized to empty.
+		 * Hence mask everything.
+		 */
+		if (opaque->flags & GIN_DELETED)
+			memset(page_norm, MASK_MARKER, BLCKSZ);
+		else
+			mask_unused_space(page_norm);
+	}
+
+	return (char *)page_norm;
+}
+
+/*
+ * Mask a GIST page
+ */
+char *
+mask_gist_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	Page	page_norm;
+	OffsetNumber offnum,
+				maxoff;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+
+	mask_page_hint_bits(page_norm);
+	mask_unused_space(page_norm);
+
+	/*Mask NSN*/
+	GistPageSetNSN(page_norm, 0xFFFFFFFFFFFFFFFF);
+
+	/*
+	 * We update F_FOLLOW_RIGHT flag on the left child after writing WAL record.
+	 * Hence, mask this flag.
+	 */
+	GistMarkFollowRight(page_norm);
+
+	if (GistPageIsLeaf(page_norm))
+	{
+		/*
+		 * For gist leaf pages,
+		 * Mask some line pointer bits, particularly those marked as
+		 * used on a master and unused on a standby.
+		 * XXX: This could be refined.
+		 */
+		maxoff = PageGetMaxOffsetNumber(page_norm);
+		for (offnum = FirstOffsetNumber;
+			 offnum <= maxoff;
+			 offnum = OffsetNumberNext(offnum))
+		{
+			ItemId		itemId = PageGetItemId(page_norm, offnum);
+
+			if (ItemIdIsUsed(itemId))
+				itemId->lp_flags = LP_UNUSED;
+		}
+	}
+
+	/* In Gist redo, we never mark a page as garbage. Hence, Mask It.*/
+	GistClearPageHasGarbage(page_norm);
+	return (char *)page_norm;
+}
+
+/*
+ * Mask a Sequence page
+ */
+char *
+mask_seq_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	return mask_common_page(info, blkno, page, false, true);
+}
+
+/*
+ * Mask a SpGist page
+ */
+char *
+mask_spg_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	Page	page_norm;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+
+	mask_page_hint_bits(page_norm);
+
+	if (!SpGistPageIsMeta(page_norm))
+		mask_unused_space(page_norm);
+
+	return (char *)page_norm;
+}
+
+/*
+ * Mask a BRIN page
+ */
+char *
+mask_brin_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	Page	page_norm;
+	OffsetNumber offnum,
+				maxoff;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+
+	mask_page_hint_bits(page_norm);
+
+	if (BRIN_IS_REGULAR_PAGE(page_norm))
+	{
+		mask_unused_space(page_norm);
+
+		maxoff = PageGetMaxOffsetNumber(page_norm);
+		for (offnum = FirstOffsetNumber;
+			 offnum <= maxoff;
+			 offnum = OffsetNumberNext(offnum))
+		{
+			ItemId		itemId = PageGetItemId(page_norm, offnum);
+
+			if (ItemIdIsUsed(itemId))
+				itemId->lp_flags = LP_UNUSED;
+		}
+	}
+
+	/* We need to handle brin pages of type Meta and Revmap if needed */
+
+	return (char *)page_norm;
+}
+
+/*
+ * Mask a generic page
+ */
+char *
+mask_generic_page(uint8 info, BlockNumber blkno, const char *page)
+{
+	return mask_common_page(info, blkno, page, true, true);
+}
+
+/*
+ * Mask a common page
+ */
+static char *
+mask_common_page(uint8 info, BlockNumber blkno, const char *page, bool maskHints, bool maskUnusedSpace)
+{
+	Page	page_norm;
+
+	page_norm = (Page) palloc(BLCKSZ);
+	memcpy(page_norm, page, BLCKSZ);
+
+	/*
+	 * Mask the Page LSN. Because, we store the page before updating the LSN.
+	 * Hence, LSNs of both pages will always be different.
+	 */
+	mask_page_lsn(page_norm);
+
+	if (maskHints)
+		mask_page_hint_bits(page_norm);
+
+	if (maskUnusedSpace)
+		mask_unused_space(page_norm);
+
+	return (char *)page_norm;
+}
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 1b70bfb..bf89dc6 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -1141,3 +1141,47 @@ PageSetChecksumInplace(Page page, BlockNumber blkno)
 
 	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno);
 }
+
+/*
+ * Compare the contents of two pages.
+ * If the two pages are exactly same, it returns BLCKSZ. Otherwise,
+ * it returns the location where the first mismatch has occurred.
+ */
+int
+comparePages(char *page1, char *page2)
+{
+	char	buf1[BLCKSZ * 2];
+	char	buf2[BLCKSZ * 2];
+	int		j = 0;
+	int		i;
+
+	/*
+	 * Convert the pages to be compared into hex format to facilitate
+	 * their comparison and make potential diffs more readable while
+	 * debugging.
+	 */
+	for (i = 0; i < BLCKSZ ; i++)
+	{
+		const char *digits = "0123456789ABCDEF";
+		uint8 byte1 = (uint8) page1[i];
+		uint8 byte2 = (uint8) page2[i];
+
+		buf1[j] = digits[byte1 >> 4];
+		buf2[j] = digits[byte2 >> 4];
+
+		if (buf1[j] != buf2[j])
+		{
+			break;
+		}
+		j++;
+
+		buf1[j] = digits[byte1 & 0x0F];
+		buf2[j] = digits[byte2 & 0x0F];
+		if (buf1[j] != buf2[j])
+		{
+			break;
+		}
+		j++;
+	}
+	return i;
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c5178f7..04d8b3d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,9 +28,11 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/rmgr.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
+#include "access/xlog_internal.h"
 #include "catalog/namespace.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
@@ -144,6 +146,9 @@ static bool call_enum_check_hook(struct config_enum * conf, int *newval,
 static bool check_log_destination(char **newval, void **extra, GucSource source);
 static void assign_log_destination(const char *newval, void *extra);
 
+static bool check_wal_consistency(char **newval, void **extra, GucSource source);
+static void assign_wal_consistency(const char *newval, void *extra);
+
 #ifdef HAVE_SYSLOG
 static int	syslog_facility = LOG_LOCAL0;
 #else
@@ -3248,6 +3253,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"wal_consistency", PGC_POSTMASTER, WAL_SETTINGS,
+			gettext_noop("Sets the rmgrIDs for which WAL consistency should be checked."),
+			gettext_noop("Valid values are combinations of rmgrIDs"),
+			GUC_LIST_INPUT
+		},
+		&wal_consistency_string,
+		"none",
+		check_wal_consistency, assign_wal_consistency, NULL
+	},
+
+	{
 		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
 			gettext_noop("Sets the destination for server log output."),
 			gettext_noop("Valid values are combinations of \"stderr\", "
@@ -9903,6 +9919,121 @@ assign_log_destination(const char *newval, void *extra)
 	Log_destination = *((int *) extra);
 }
 
+static bool
+check_wal_consistency(char **newval, void **extra, GucSource source)
+{
+	char	   	*rawstring;
+	List	   	*elemlist;
+	ListCell   	*l;
+	bool		*newwalconsistency;
+	bool		isRmgrId = false;	/* Does this guc include any individual rmid? */
+	bool		isAll = false;	/* Does this guc include 'all' keyword? */
+	bool		isNone = false;	/* Does this guc include 'none' keyword? */
+	int		i;
+
+	newwalconsistency = (bool *) guc_malloc(ERROR, (RM_MAX_ID + 1) * sizeof(bool));
+
+	/* Initialize the array*/
+	MemSet(newwalconsistency, 0, (RM_MAX_ID + 1) * sizeof(bool));
+
+	/* Need a modifiable copy of string */
+	rawstring = pstrdup(*newval);
+
+	/* Parse string into list of identifiers */
+	if (!SplitIdentifierString(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	foreach(l, elemlist)
+	{
+		char		*tok = (char *) lfirst(l);
+		bool		found = false;
+
+		/* Check if the token matches with any individual rmid */
+		for (i = 0; i <= RM_MAX_ID; i++)
+		{
+			if (pg_strcasecmp(tok, RmgrTable[i].rm_name) == 0)
+			{
+				/*
+				 * Found a match. Now, check if maskPage function
+				 * is defined for this rmid.
+				 */
+				if (RmgrTable[i].rm_maskPage != NULL)
+				{
+					newwalconsistency[i] = true;
+					found = true;
+					isRmgrId = true;
+					break;
+				}
+				else
+				{
+					GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
+					pfree(rawstring);
+					list_free(elemlist);
+					return false;
+				}
+			}
+		}
+
+		if (found)
+			continue;
+
+		/* Definitely not an individual rmid. Check for 'none' and 'all'. */
+		if (pg_strcasecmp(tok, "none") == 0)
+		{
+			MemSet(newwalconsistency, 0, (RM_MAX_ID + 1) * sizeof(bool));
+			isNone = true;
+		}
+		else if (pg_strcasecmp(tok, "all") == 0)
+		{
+			/*
+			 * Followings are the rmids which can have backup blocks.
+			 * We'll enable this feature only for these rmids.
+			 */
+			for (i = 0; i <= RM_MAX_ID; i++)
+			{
+				if (RmgrTable[i].rm_maskPage != NULL)
+				{
+					newwalconsistency[i] = true;
+				}
+			}
+			isAll = true;
+		}
+		else
+		{
+			GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
+			pfree(rawstring);
+			list_free(elemlist);
+			return false;
+		}
+	}
+
+	pfree(rawstring);
+	list_free(elemlist);
+
+	/* guc should contain either 'all' or 'none' or combination of rmids. */
+	if ((isAll && isNone) || (isAll && isRmgrId) || (isNone && isRmgrId))
+	{
+		GUC_check_errdetail("Invalid value combination");
+		return false;
+	}
+
+	*extra = (void *) newwalconsistency;
+
+	return true;
+}
+
+static void
+assign_wal_consistency(const char *newval, void *extra)
+{
+	wal_consistency = (bool *) extra;
+}
+
 static void
 assign_syslog_facility(int newval, void *extra)
 {
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 6d0666c..9ccc9c2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -191,6 +191,11 @@
 					#   open_sync
 #full_page_writes = on			# recover from partial page writes
 #wal_compression = off			# enable compression of full-page writes
+#wal_consistency = 'none'		# Valid values are combinations of
+					# heap2, heap, btree, hash, gin, gist, sequence,
+					# spgist, brin, generic and xlog. It can also
+					# be set to all to enable all the values.
+					# (change requires restart)
 #wal_log_hints = off			# also do full page writes of non-critical updates
 					# (change requires restart)
 #wal_buffers = -1			# min 32kB, -1 sets based on shared_buffers
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index b53591d..baeeecc 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -29,7 +29,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,maskPage) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
index 8fe20ce..f962e79 100644
--- a/src/bin/pg_xlogdump/rmgrdesc.c
+++ b/src/bin/pg_xlogdump/rmgrdesc.c
@@ -32,7 +32,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,maskPage) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index ff7fe62..0d2bc1a 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,maskPage) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index a7a0ae2..9e693b4 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -25,25 +25,25 @@
  */
 
 /* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL)
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, mask_xlog_page)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, mask_heap_page)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, mask_heap_page)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, mask_btree_page)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, mask_hash_page)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, mask_gin_page)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, mask_gist_page)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, mask_seq_page)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, mask_spg_page)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, mask_brin_page)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, mask_generic_page)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index c9f332c..295bf09 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -105,6 +105,8 @@ extern bool EnableHotStandby;
 extern bool fullPageWrites;
 extern bool wal_log_hints;
 extern bool wal_compression;
+extern bool *wal_consistency;
+extern char *wal_consistency_string;
 extern bool log_checkpoints;
 
 extern int	CheckPointSegments;
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 0a595cc..47fb0d0 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -276,6 +276,8 @@ typedef struct RmgrData
 	const char *(*rm_identify) (uint8 info);
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
+	char		*(*rm_maskPage) (uint8 info, BlockNumber blkno,
+							const char *page);
 } RmgrData;
 
 extern const RmgrData RmgrTable[];
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index 3dfcb49..d747ab1 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -137,6 +137,7 @@ typedef struct XLogRecordBlockImageHeader
 /* Information stored in bimg_info */
 #define BKPIMAGE_HAS_HOLE		0x01	/* page image has "hole" */
 #define BKPIMAGE_IS_COMPRESSED		0x02		/* page image is compressed */
+#define BKPIMAGE_IS_REQUIRED_FOR_REDO		0x04	/* page is required during redo */
 
 /*
  * Extra header information used when page image has "hole" and
diff --git a/src/include/storage/bufmask.h b/src/include/storage/bufmask.h
new file mode 100644
index 0000000..b7be1e8
--- /dev/null
+++ b/src/include/storage/bufmask.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * bufmask.h
+ *       Buffer masking definitions.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/bufmask.h
+ */
+
+#ifndef BUFMASK_H
+#define BUFMASK_H
+
+#include "postgres.h"
+#include "storage/block.h"
+
+extern char *mask_xlog_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_heap_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_btree_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_hash_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_gin_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_gist_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_seq_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_spg_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_brin_page(uint8 info, BlockNumber blkno, const char *page);
+extern char *mask_generic_page(uint8 info, BlockNumber blkno, const char *page);
+#endif
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 15cebfc..8ca98e4 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -432,4 +432,6 @@ extern void PageIndexDeleteNoCompact(Page page, OffsetNumber *itemnos,
 extern char *PageSetChecksumCopy(Page page, BlockNumber blkno);
 extern void PageSetChecksumInplace(Page page, BlockNumber blkno);
 
+extern int comparePages(Page norm_new_page, Page norm_old_page);
+
 #endif   /* BUFPAGE_H */
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index fede1e6..5ef703e 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -404,6 +404,7 @@ sub init
 	print $conf "fsync = off\n";
 	print $conf "log_statement = all\n";
 	print $conf "port = $port\n";
+	print $conf "wal_consistency = all\n";
 
 	if ($params{allows_streaming})
 	{
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to