*** a/src/backend/access/hash/hash.c
--- b/src/backend/access/hash/hash.c
***************
*** 282,288 **** hashgettuple(PG_FUNCTION_ARGS)
--- 282,290 ----
  			/*
  			 * Yes, so mark it by setting the LP_DEAD state in the item flags.
  			 */
+ 			LockBufferForHints(buf);
  			ItemIdMarkDead(PageGetItemId(page, offnum));
+ 			UnlockBufferForHints(buf);
  
  			/*
  			 * Since this can be redone later if needed, it's treated the same
*** a/src/backend/access/heap/pruneheap.c
--- b/src/backend/access/heap/pruneheap.c
***************
*** 259,266 **** heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin,
--- 259,268 ----
  		if (((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid ||
  			PageIsFull(page))
  		{
+ 			LockBufferForHints(buffer);
  			((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
  			PageClearFull(page);
+ 			UnlockBufferForHints(buffer);
  			SetBufferCommitInfoNeedsSave(buffer);
  		}
  	}
*** a/src/backend/access/nbtree/nbtinsert.c
--- b/src/backend/access/nbtree/nbtinsert.c
***************
*** 403,415 **** _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
--- 403,427 ----
  					 * everyone, so we may as well mark the index entry
  					 * killed.
  					 */
+ 					if (nbuf != InvalidBuffer)
+ 						LockBufferForHints(nbuf);
+ 					else
+ 						LockBufferForHints(buf);
+ 
  					ItemIdMarkDead(curitemid);
  					opaque->btpo_flags |= BTP_HAS_GARBAGE;
+ 
  					/* be sure to mark the proper buffer dirty... */
  					if (nbuf != InvalidBuffer)
+ 					{
+ 						UnlockBufferForHints(nbuf);
  						SetBufferCommitInfoNeedsSave(nbuf);
+ 					}
  					else
+ 					{
+ 						UnlockBufferForHints(buf);
  						SetBufferCommitInfoNeedsSave(buf);
+ 					}
  				}
  			}
  		}
*** a/src/backend/access/nbtree/nbtree.c
--- b/src/backend/access/nbtree/nbtree.c
***************
*** 1040,1046 **** restart:
--- 1040,1048 ----
  			if (vstate->cycleid != 0 &&
  				opaque->btpo_cycleid == vstate->cycleid)
  			{
+ 				LockBufferForHints(buf);
  				opaque->btpo_cycleid = 0;
+ 				UnlockBufferForHints(buf);
  				SetBufferCommitInfoNeedsSave(buf);
  			}
  		}
*** a/src/backend/commands/sequence.c
--- b/src/backend/commands/sequence.c
***************
*** 1092,1101 **** read_info(SeqTable elm, Relation rel, Buffer *buf)
--- 1092,1103 ----
  	 */
  	if (HeapTupleHeaderGetXmax(tuple.t_data) != InvalidTransactionId)
  	{
+ 		LockBufferForHints(*buf);
  		HeapTupleHeaderSetXmax(tuple.t_data, InvalidTransactionId);
  		tuple.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
  		tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
  		SetBufferCommitInfoNeedsSave(*buf);
+ 		UnlockBufferForHints(*buf);
  	}
  
  	seq = (Form_pg_sequence) GETSTRUCT(&tuple);
*** a/src/backend/storage/buffer/bufmgr.c
--- b/src/backend/storage/buffer/bufmgr.c
***************
*** 440,446 **** ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
  			smgrread(smgr, forkNum, blockNum, (char *) bufBlock);
  
  			/* check for garbage data */
! 			if (!PageHeaderIsValid((PageHeader) bufBlock))
  			{
  				if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages)
  				{
--- 440,446 ----
  			smgrread(smgr, forkNum, blockNum, (char *) bufBlock);
  
  			/* check for garbage data */
! 			if (!PageIsVerified((Page) bufBlock))
  			{
  				if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages)
  				{
***************
*** 1860,1865 **** FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln)
--- 1860,1866 ----
  {
  	XLogRecPtr	recptr;
  	ErrorContextCallback errcontext;
+ 	Block		bufBlock;
  
  	/*
  	 * Acquire the buffer's io_in_progress lock.  If StartBufferIO returns
***************
*** 1907,1912 **** FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln)
--- 1908,1928 ----
  	buf->flags &= ~BM_JUST_DIRTIED;
  	UnlockBufHdr(buf);
  
+ 	/*
+ 	 * Set page verification info immediately before we write the buffer to disk.
+ 	 * Once we have flushed the buffer is marked clean again, meaning it can
+ 	 * be replaced quickly and silently with another data block, so we must
+ 	 * write verification info now. For efficiency, the process of cleaning
+ 	 * and page replacement is asynchronous, so we can't do this *only* when
+ 	 * we are about to replace the buffer, we need to do this for every flush.
+ 	 *
+ 	 * Note that the buffer must not be written to while we set verification,
+ 	 * otherwise it would invalidate the page on-disk. So we must ensure
+ 	 * writes are prevented, see comments for LockBufferForHints().
+ 	 */
+ 	bufBlock = buf->buf_id < 0 ? LocalBufHdrGetBlock(buf) : BufHdrGetBlock(buf);
+ 	PageSetVerificationInfo((Page) bufBlock);
+ 
  	smgrwrite(reln,
  			  buf->tag.forkNum,
  			  buf->tag.blockNum,
***************
*** 1921,1926 **** FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln)
--- 1937,1960 ----
  	 */
  	TerminateBufferIO(buf, true, 0);
  
+ #ifdef USE_ASSERT_CHECKING
+ 	/*
+ 	 * Confirm that all AMs have protected their hints with LockBufferForHints()
+ 	 */
+ 	if (page_checksums)
+ 	{
+ 		bool just_dirtied;
+ 
+ 		LockBufHdr(buf);
+ 		just_dirtied = ((buf->flags & BM_JUST_DIRTIED) == BM_JUST_DIRTIED);
+ 		UnlockBufHdr(buf);
+ 
+ 		Assert(!just_dirtied);
+ 	}
+ #endif
+ 
+ 	/* XXX Assert(buf is not BM_JUST_DIRTIED) */
+ 
  	TRACE_POSTGRESQL_BUFFER_FLUSH_DONE(buf->tag.forkNum,
  									   buf->tag.blockNum,
  									   reln->smgr_rnode.node.spcNode,
***************
*** 2628,2633 **** WaitIO(volatile BufferDesc *buf)
--- 2662,2742 ----
  }
  
  /*
+  * Lock buffer to allow hints to be set, for use by all AMs.
+  *
+  * Many writes to PostgreSQL data pages are referred to as hints because
+  * those changes are not critical and we have elected to avoid WAL-logging
+  * the changes. As an optimisation, PostgreSQL allows hints to be set on
+  * pages locked in shared mode. When we are adding checksums prior to flush
+  * the page musn't change, so we hold the io_in_progress lock exclusively
+  * for the buffer prior to the smgrwrite. Thus setting hints must wait
+  * when we are flushing the buffer. The alternative would be to lock the
+  * whole buffer in exclusive mode when we wanted to set hints, which would
+  * cause more contention that simply holding the io_in_progress lock.
+  *
+  * It might seem possible to handle this optimistically and flush the
+  * buffer and then check to see if the buffer was just dirtied. However,
+  * that might result in the on-disk block having page checksums that don't
+  * match its contents and thus we would need to train ourselves to ignore
+  * "some" page errors. If you feel that is a path to take, then turn off
+  * checksums and be happy.
+  */
+ void
+ LockBufferForHints(Buffer buffer)
+ {
+ 	volatile BufferDesc *buf;
+ 
+ 	Assert(BufferIsValid(buffer));
+ 
+ 	/*
+ 	 * If we aren't writing a checksum for buffers then the answer is true.
+ 	 */
+ 	if (!page_checksums)
+ 		return;
+ 
+ 	/*
+ 	 * If its a local buffer then it can't be IO in progress, since we'd be the
+ 	 * one writing it and not checking to see if it was in progress.
+ 	 */
+ 	if (BufferIsLocal(buffer))
+ 		return;
+ 
+ 	buf = &BufferDescriptors[buffer - 1];
+ 
+ 	LWLockAcquire(buf->io_in_progress_lock, LW_SHARED);
+ }
+ 
+ /*
+  * UnLock buffer to prevent hints being set.
+  *
+  * Don't do a CHECK_INTERRUPTS between BufferLockForHints() and BufferUnlockForHints()
+  */
+ void
+ UnlockBufferForHints(Buffer buffer)
+ {
+ 	volatile BufferDesc *buf;
+ 
+ 	Assert(BufferIsValid(buffer));
+ 
+ 	/*
+ 	 * If we aren't writing a checksum for buffers, ignore.
+ 	 */
+ 	if (!page_checksums)
+ 		return;
+ 
+ 	/*
+ 	 * If its a local buffer then it can't be IO in progress, since we'd be the
+ 	 * one writing it and not checking to see if it was in progress.
+ 	 */
+ 	if (BufferIsLocal(buffer))
+ 		return;
+ 
+ 	buf = &BufferDescriptors[buffer - 1];
+ 
+ 	LWLockRelease(buf->io_in_progress_lock);
+ }
+ 
+ /*
   * StartBufferIO: begin I/O on this buffer
   *	(Assumptions)
   *	My process is executing no IO
*** a/src/backend/storage/page/bufpage.c
--- b/src/backend/storage/page/bufpage.c
***************
*** 16,21 ****
--- 16,25 ----
  
  #include "access/htup.h"
  
+ bool page_checksums = false;
+ 
+ static bool PageVerificationInfoOK(Page page);
+ static uint16 PageCalcChecksum16(Page page);
  
  /* ----------------------------------------------------------------
   *						Page support functions
***************
*** 25,30 ****
--- 29,38 ----
  /*
   * PageInit
   *		Initializes the contents of a page.
+  *		Note that we don't automatically add a checksum, or flag that the
+  * 		page has a checksum field. We start with a normal page layout and defer
+  *		the decision on what page verification will be written just before
+  *		we writethe block to disk later.
   */
  void
  PageInit(Page page, Size pageSize, Size specialSize)
***************
*** 67,86 **** PageInit(Page page, Size pageSize, Size specialSize)
   * will clean up such a page and make it usable.
   */
  bool
! PageHeaderIsValid(PageHeader page)
  {
  	char	   *pagebytes;
  	int			i;
  
  	/* Check normal case */
! 	if (PageGetPageSize(page) == BLCKSZ &&
! 		PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
! 		(page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
! 		page->pd_lower >= SizeOfPageHeaderData &&
! 		page->pd_lower <= page->pd_upper &&
! 		page->pd_upper <= page->pd_special &&
! 		page->pd_special <= BLCKSZ &&
! 		page->pd_special == MAXALIGN(page->pd_special))
  		return true;
  
  	/* Check all-zeroes case */
--- 75,94 ----
   * will clean up such a page and make it usable.
   */
  bool
! PageIsVerified(Page page)
  {
+ 	PageHeader	p = (PageHeader) page;
  	char	   *pagebytes;
  	int			i;
  
  	/* Check normal case */
! 	if (PageVerificationInfoOK(page) &&
! 		(p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
! 		p->pd_lower >= SizeOfPageHeaderData &&
! 		p->pd_lower <= p->pd_upper &&
! 		p->pd_upper <= p->pd_special &&
! 		p->pd_special <= BLCKSZ &&
! 		p->pd_special == MAXALIGN(p->pd_special))
  		return true;
  
  	/* Check all-zeroes case */
***************
*** 827,829 **** PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
--- 835,979 ----
  
  	pfree(itemidbase);
  }
+ 
+ /*
+  * Test whether the page verification information is correct or not.
+  *
+  * IMPORTANT NOTE -
+  * Verification info is not valid at all times on a data page. We set
+  * verification info before we flush page/buffer, and implicitly invalidate
+  * verification info when we write to the page. A heavily accessed buffer
+  * might then spend most of its life with invalid page verification info,
+  * so testing verification info on random pages in the buffer pool will tell
+  * you nothing. The reason for this is that page verification info protects
+  * Postgres data from errors on the filesystems on which we rely. We do not
+  * protect buffers against uncorrectable memory errors, since these have a
+  * very low measured incidence according to research on large server farms,
+  * http://www.google.com/research/pubs/archive/35162.pdf, discussed 2010/12/22.
+  *
+  * To confirm your understanding that means that WAL-logged changes to a page
+  * do NOT update the page verification info, so full page images may not have
+  * correct verification information on them. But those page images have the
+  * WAL CRC covering them and so are verified separately from this mechanism.
+  * WAL replay ignores page verification info unless it writes out or reads in
+  * blocks from disk; restoring full page writes does not check verification
+  * info via this function.
+  *
+  * The best way to understand this is that WAL CRCs protect records entering
+  * the WAL stream, and page verification protects blocks entering and leaving
+  * the buffer pool. They are similar in purpose, yet completely separate.
+  * Together they ensure we are able to detect errors in data leaving and
+  * re-entering PostgreSQL controlled memory.
+  *
+  * Note also that the verification mechanism can vary from page to page.
+  * All we do here is look at what the page itself says is the verification
+  * mechanism and then apply that test. This allows us to run without the CPU
+  * cost of verification if we choose, as well as to provide an upgrade path
+  * for anyone doing direct upgrades using pg_upgrade.
+  *
+  * There is some concern that trusting page data to say how to check page
+  * data is dangerously self-referential. To ensure no mistakes we set two
+  * non-adjacent bits to signify that the page has a checksum and
+  * should be verified when that block is read back into a buffer.
+  * We use two bits in case a multiple bit error removes one of the checksum
+  * flags *and* destroys data, which would lead to skipping the checksum check
+  * and silently accepting bad data.
+  *
+  * Note also that this returns a boolean, not a full damage assessment.
+  */
+ static bool
+ PageVerificationInfoOK(Page page)
+ {
+ 	PageHeader	p = (PageHeader) page;
+ 
+ 	/*
+ 	 * We set two non-adjacent bits to signify that the page has a checksum and
+ 	 * should be verified against that block is read back into a buffer.
+ 	 * We use two bits in case a multiple bit error removes one of the checksum
+ 	 * flags and destroys data, which would lead to skipping the checksum check
+ 	 * and silently accepting bad data.
+ 	 */
+ 	if (PageHasChecksumFlag1(p) && PageHasChecksumFlag2(p))
+ 	{
+ 		if (PageCalcChecksum16(page) == p->pd_verify.pd_checksum16)
+ 			return true;
+ 	}
+ 	else if (!PageHasChecksumFlag1(p) && !PageHasChecksumFlag2(p))
+ 	{
+ 		if (PageGetPageLayoutVersion(p) == PG_PAGE_LAYOUT_VERSION &&
+ 			PageGetPageSize(p) == BLCKSZ)
+ 			return true;
+ 	}
+ 
+ 	return false;
+ }
+ 
+ /*
+  * Set verification info for page.
+  *
+  * Either we set a new checksum, or we set the standard watermark. We must
+  * not leave an old checksum in place. Note that the verification info is
+  * not WAL logged, whereas the data changes to pages are, so data is safe
+  * whether or not we have page_checksums enabled. The purpose of checksums
+  * is to detect page corruption to allow replacement from backup.
+  */
+ void
+ PageSetVerificationInfo(Page page)
+ {
+ 	PageHeader	p = (PageHeader) page;
+ 
+ 	if (page_checksums)
+ 	{
+ 		p->pd_flags |= PD_CHECKSUM;
+ 		p->pd_verify.pd_checksum16 = PageCalcChecksum16(page);
+ 	}
+ 	else if (PageHasChecksumFlag1(p) || PageHasChecksumFlag2(p))
+ 	{
+ 		/* ensure any older checksum info is overwritten with watermark */
+ 		p->pd_flags &= ~PD_CHECKSUM;
+ 		PageSetPageSizeAndVersion(p, BLCKSZ, PG_PAGE_LAYOUT_VERSION);
+ 	}
+ }
+ 
+ /*
+  * Calculate checksum for a PostgreSQL Page. We do this in 3 steps, first
+  * we calculate the checksum for the header, avoiding the verification
+  * info, which will be added afterwards. Next, we add the line pointers up to
+  * the hole in the middle of the block at pd_lower. Last, we add the tail
+  * of the page from pd_upper to the end of page.
+  */
+ static uint16
+ PageCalcChecksum16(Page page)
+ {
+ #define PAGE_VERIFICATION_USES_FLETCHER16 (true)
+ #ifdef PAGE_VERIFICATION_USES_FLETCHER16
+ 	/*
+ 	 * Following calculation is a Flecther's 16 checksum. The calc is isolated
+ 	 * here and tuning and/or replacement algorithms are possible.
+ 	 *
+ 	 * XXX present implementation is raw, untuned calculation, please tweak
+ 	 */
+ 	PageHeader	p = (PageHeader) page;
+ 	uint16 sum1 = 0;
+ 	uint16 sum2 = 0;
+ 	int		i;
+ 
+ #define	COMP_F16(from, to) \
+ for (i = from; i < to; i++) \
+ { \
+ 		sum1 = (sum1 + page[i]) % 255; \
+ 		sum2 = (sum1 + sum2) % 255; \
+ } while (0)
+ 
+ 	COMP_F16(0,
+ 			 offsetof(PageHeaderData, pd_special) + sizeof(LocationIndex));
+ 
+ 	COMP_F16(offsetof(PageHeaderData, pd_prune_xid),
+ 			 p->pd_upper);
+ 
+ 	COMP_F16(p->pd_upper,
+ 			 (int) BLCKSZ);
+ 
+ 	return ((sum2 << 8) | sum1);
+ #endif
+ }
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 830,835 **** static struct config_bool ConfigureNamesBool[] =
--- 830,849 ----
  		NULL, NULL, NULL
  	},
  	{
+ 		{"page_checksums", PGC_SIGHUP, WAL_SETTINGS,
+ 			gettext_noop("Marks database blocks with a checksum before writing them to disk. "),
+ 			gettext_noop("When enabled all database blocks will be marked with a checksums before writing to disk. "
+ 						 "When we read a database block from disk the checksum is checked, if it exists. "
+ 						 "If there is no checksum marked yet then no check is performed, though a "
+ 						 "checksum will be added later when we re-write the database block. "
+ 						 "When disabled checksums will be ignored, even if the block was marked "
+ 						 "with checksum. When disabled checksums will not be added to database blocks.")
+ 		},
+ 		&page_checksums,
+ 		true,
+ 		NULL, NULL, NULL
+ 	},
+ 	{
  		{"full_page_writes", PGC_SIGHUP, WAL_SETTINGS,
  			gettext_noop("Writes full pages to WAL when first modified after a checkpoint."),
  			gettext_noop("A page write in process during an operating system crash might be "
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 150,164 ****
  
  
  #------------------------------------------------------------------------------
! # WRITE AHEAD LOG
  #------------------------------------------------------------------------------
  
! # - Settings -
  
! #wal_level = minimal			# minimal, archive, or hot_standby
! 					# (change requires restart)
  #fsync = on				# turns forced synchronization on or off
  #synchronous_commit = on		# synchronization level; on, off, or local
  #wal_sync_method = fsync		# the default is the first option
  					# supported by the operating system:
  					#   open_datasync
--- 150,170 ----
  
  
  #------------------------------------------------------------------------------
! # WRITE AHEAD LOG & RELIABILITY
  #------------------------------------------------------------------------------
  
! # - Reliability -
  
! #page_checksums = off			# calculate checksum before database I/O
! #full_page_writes = on			# recover from partial page writes
  #fsync = on				# turns forced synchronization on or off
+ 
  #synchronous_commit = on		# synchronization level; on, off, or local
+ 
+ # - Write Ahead Log -
+ 
+ #wal_level = minimal			# minimal, archive, or hot_standby
+ 					# (change requires restart)
  #wal_sync_method = fsync		# the default is the first option
  					# supported by the operating system:
  					#   open_datasync
***************
*** 166,172 ****
  					#   fsync
  					#   fsync_writethrough
  					#   open_sync
- #full_page_writes = on			# recover from partial page writes
  #wal_buffers = -1			# min 32kB, -1 sets based on shared_buffers
  					# (change requires restart)
  #wal_writer_delay = 200ms		# 1-10000 milliseconds
--- 172,177 ----
*** a/src/backend/utils/time/tqual.c
--- b/src/backend/utils/time/tqual.c
***************
*** 119,125 **** SetHintBits(HeapTupleHeader tuple, Buffer buffer,
--- 119,127 ----
  			return;				/* not flushed yet, so don't set hint */
  	}
  
+ 	LockBufferForHints(buffer);
  	tuple->t_infomask |= infomask;
+ 	UnlockBufferForHints(buffer);
  	SetBufferCommitInfoNeedsSave(buffer);
  }
  
*** a/src/include/storage/bufmgr.h
--- b/src/include/storage/bufmgr.h
***************
*** 209,214 **** extern bool ConditionalLockBuffer(Buffer buffer);
--- 209,216 ----
  extern void LockBufferForCleanup(Buffer buffer);
  extern bool ConditionalLockBufferForCleanup(Buffer buffer);
  extern bool HoldingBufferPinThatDelaysRecovery(void);
+ extern void LockBufferForHints(Buffer buffer);
+ extern void UnlockBufferForHints(Buffer buffer);
  
  extern void AbortBufferIO(void);
  
*** a/src/include/storage/bufpage.h
--- b/src/include/storage/bufpage.h
***************
*** 18,23 ****
--- 18,25 ----
  #include "storage/item.h"
  #include "storage/off.h"
  
+ extern bool page_checksums;
+ 
  /*
   * A postgres disk page is an abstraction layered on top of a postgres
   * disk block (which is simply a unit of i/o, see block.h).
***************
*** 130,136 **** typedef struct PageHeaderData
  	LocationIndex pd_lower;		/* offset to start of free space */
  	LocationIndex pd_upper;		/* offset to end of free space */
  	LocationIndex pd_special;	/* offset to start of special space */
! 	uint16		pd_pagesize_version;
  	TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
  	ItemIdData	pd_linp[1];		/* beginning of line pointer array */
  } PageHeaderData;
--- 132,144 ----
  	LocationIndex pd_lower;		/* offset to start of free space */
  	LocationIndex pd_upper;		/* offset to end of free space */
  	LocationIndex pd_special;	/* offset to start of special space */
! 
! 	union
! 	{
! 		uint16		pd_pagesize_version;
! 		uint16		pd_checksum16;
! 	} pd_verify;				/* page verification data */
! 
  	TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
  	ItemIdData	pd_linp[1];		/* beginning of line pointer array */
  } PageHeaderData;
***************
*** 155,161 **** typedef PageHeaderData *PageHeader;
  #define PD_ALL_VISIBLE		0x0004		/* all tuples on page are visible to
  										 * everyone */
  
! #define PD_VALID_FLAG_BITS	0x0007		/* OR of all valid pd_flags bits */
  
  /*
   * Page layout version number 0 is for pre-7.3 Postgres releases.
--- 163,178 ----
  #define PD_ALL_VISIBLE		0x0004		/* all tuples on page are visible to
  										 * everyone */
  
! #define PD_VALID_FLAG_BITS	0x800F		/* OR of all non-checksum pd_flags bits */
! 
! #define PD_CHECKSUM1		0x0008		/* First checksum bit */
! #define PD_CHECKSUM2		0x8000		/* Second checksum bit */
! #define PD_CHECKSUM 		0x8008		/* OR of both checksum flags */
! 
! #define PageHasChecksumFlag1(page) \
! 	((((PageHeader) (page))->pd_flags & PD_CHECKSUM1) == PD_CHECKSUM1)
! #define PageHasChecksumFlag2(page) \
! 	((((PageHeader) (page))->pd_flags & PD_CHECKSUM2) == PD_CHECKSUM2)
  
  /*
   * Page layout version number 0 is for pre-7.3 Postgres releases.
***************
*** 165,170 **** typedef PageHeaderData *PageHeader;
--- 182,189 ----
   * Release 8.3 uses 4; it changed the HeapTupleHeader layout again, and
   *		added the pd_flags field (by stealing some bits from pd_tli),
   *		as well as adding the pd_prune_xid field (which enlarges the header).
+  * Release 9.2 uses 4 as well, though with changed meaning of verification bits.
+  * We deliberately don't bump the page version for that, to allow upgrades.
   */
  #define PG_PAGE_LAYOUT_VERSION		4
  
***************
*** 231,249 **** typedef PageHeaderData *PageHeader;
   * PageGetPageSize
   *		Returns the page size of a page.
   *
!  * this can only be called on a formatted page (unlike
!  * BufferGetPageSize, which can be called on an unformatted page).
!  * however, it can be called on a page that is not stored in a buffer.
   */
! #define PageGetPageSize(page) \
! 	((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
  
  /*
   * PageGetPageLayoutVersion
   *		Returns the page layout version of a page.
   */
  #define PageGetPageLayoutVersion(page) \
! 	(((PageHeader) (page))->pd_pagesize_version & 0x00FF)
  
  /*
   * PageSetPageSizeAndVersion
--- 250,271 ----
   * PageGetPageSize
   *		Returns the page size of a page.
   *
!  * Since PageSizeIsValid() when pagesize == BLCKSZ, just written BLCKSZ.
!  * This can be called on any page, initialised or not, in or out of buffers.
!  * You might think this can vary at runtime but you'd be wrong, since pages
!  * frequently need to occupy buffers and pages are copied from one to another
!  * so there are many hidden assumptions that this simple definition is true.
   */
! #define PageGetPageSize(page) (BLCKSZ)
  
  /*
   * PageGetPageLayoutVersion
   *		Returns the page layout version of a page.
+  *
+  * Must not be used on a page that is flagged for checksums.
   */
  #define PageGetPageLayoutVersion(page) \
! 	(((PageHeader) (page))->pd_verify.pd_pagesize_version & 0x00FF)
  
  /*
   * PageSetPageSizeAndVersion
***************
*** 251,262 **** typedef PageHeaderData *PageHeader;
   *
   * We could support setting these two values separately, but there's
   * no real need for it at the moment.
   */
  #define PageSetPageSizeAndVersion(page, size, version) \
  ( \
  	AssertMacro(((size) & 0xFF00) == (size)), \
  	AssertMacro(((version) & 0x00FF) == (version)), \
! 	((PageHeader) (page))->pd_pagesize_version = (size) | (version) \
  )
  
  /* ----------------
--- 273,286 ----
   *
   * We could support setting these two values separately, but there's
   * no real need for it at the moment.
+  *
+  * Must not be used on a page that is flagged for checksums.
   */
  #define PageSetPageSizeAndVersion(page, size, version) \
  ( \
  	AssertMacro(((size) & 0xFF00) == (size)), \
  	AssertMacro(((version) & 0x00FF) == (version)), \
! 	((PageHeader) (page))->pd_verify.pd_pagesize_version = (size) | (version) \
  )
  
  /* ----------------
***************
*** 368,374 **** do { \
   */
  
  extern void PageInit(Page page, Size pageSize, Size specialSize);
! extern bool PageHeaderIsValid(PageHeader page);
  extern OffsetNumber PageAddItem(Page page, Item item, Size size,
  			OffsetNumber offsetNumber, bool overwrite, bool is_heap);
  extern Page PageGetTempPage(Page page);
--- 392,398 ----
   */
  
  extern void PageInit(Page page, Size pageSize, Size specialSize);
! extern bool PageIsVerified(Page page);
  extern OffsetNumber PageAddItem(Page page, Item item, Size size,
  			OffsetNumber offsetNumber, bool overwrite, bool is_heap);
  extern Page PageGetTempPage(Page page);
***************
*** 381,385 **** extern Size PageGetExactFreeSpace(Page page);
--- 405,410 ----
  extern Size PageGetHeapFreeSpace(Page page);
  extern void PageIndexTupleDelete(Page page, OffsetNumber offset);
  extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems);
+ extern void PageSetVerificationInfo(Page page);
  
  #endif   /* BUFPAGE_H */
