From 5718e5e5e1d40abdcefac99866f907db93989587 Mon Sep 17 00:00:00 2001
From: David Christensen <david@endpoint.com>
Date: Wed, 15 Feb 2017 15:18:57 -0600
Subject: [PATCH] Add pg_disable_checksums() and supporting infrastructure

Extracted from a larger patch, this patch provides the basic infrastructure for turning
data checksums off in a cluster.  This also sets up the necessary pg_control fields to
support the necessary multiple states for handling the transitions.

We do a few things:

- Change "data_checksums" from a simple boolean to "data_checksum_state", an enum type
  for all of the potentially-required states for this feature (as well as enabling).

- Add pg_control support for parsing/displaying the new setting.

- Distinguish between the possible checksum states and be specific about whether we care
  about checksum read failures or write failures at all call sites, turning
  DataChecksumsEnabled() into two functions: DataChecksumsEnabledReads() and
  DataChecksumsEnabledWrites().

- Create a superuser function pg_disable_checksums() to perform the actual disabling of
  this in the cluster.

- Add WAL replay/support of the checksum state.

I have *not* changed the default in initdb to enable checksums, but this would be
trivial.
---
 src/backend/access/rmgrdesc/Makefile       |   3 +-
 src/backend/access/rmgrdesc/checksumdesc.c |  51 ++++++++++
 src/backend/access/transam/rmgr.c          |   1 +
 src/backend/access/transam/xlog.c          | 158 ++++++++++++++++++++++++++++-
 src/backend/commands/dbcommands.c          |   1 +
 src/backend/storage/page/bufpage.c         |   6 +-
 src/backend/storage/page/checksum.c        |   3 +
 src/backend/utils/misc/guc.c               |  51 +++++++---
 src/bin/pg_controldata/pg_controldata.c    |  19 ++++
 src/bin/pg_resetwal/pg_resetwal.c          |   2 +
 src/bin/pg_waldump/rmgrdesc.c              |   1 +
 src/include/access/checksumxlog.h          |  28 +++++
 src/include/access/rmgrlist.h              |   1 +
 src/include/access/xlog.h                  |   7 +-
 src/include/access/xlog_internal.h         |   2 +-
 src/include/catalog/pg_control.h           |  10 +-
 src/include/catalog/pg_proc.h              |   2 +
 src/include/storage/checksum.h             |  15 +++
 18 files changed, 335 insertions(+), 26 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/checksumdesc.c
 create mode 100644 src/include/access/checksumxlog.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 5514db1..e07e79d 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o \
+	   checksumdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/checksumdesc.c b/src/backend/access/rmgrdesc/checksumdesc.c
new file mode 100644
index 0000000..e056f2b
--- /dev/null
+++ b/src/backend/access/rmgrdesc/checksumdesc.c
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * checksumdesc.c
+ *	  rmgr descriptor routines for commands/checksum.c
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/checksumdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/checksumxlog.h"
+
+void
+checksum_desc(StringInfo buf, XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	/* not sure if we need anything here */
+	return;
+}
+
+const char *
+checksum_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_CHECKSUM_DISABLE:
+			id = "DISABLE";
+			break;
+		case XLOG_CHECKSUM_ENABLE:
+			id = "ENABLE";
+			break;
+		case XLOG_CHECKSUM_ENFORCE:
+			id = "ENFORCE";
+			break;
+		case XLOG_CHECKSUM_REVALIDATE:
+			id = "REVALIDATE";
+			break;
+	}
+
+	return id;
+}
+
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 9368b56..038ca2b 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -7,6 +7,7 @@
  */
 #include "postgres.h"
 
+#include "access/checksumxlog.h"
 #include "access/clog.h"
 #include "access/commit_ts.h"
 #include "access/ginxlog.h"
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f23e108..ff6a30f 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -23,6 +23,7 @@
 
 #include "access/clog.h"
 #include "access/commit_ts.h"
+#include "access/checksumxlog.h"
 #include "access/multixact.h"
 #include "access/rewriteheap.h"
 #include "access/subtrans.h"
@@ -916,6 +917,8 @@ static void WALInsertLockAcquireExclusive(void);
 static void WALInsertLockRelease(void);
 static void WALInsertLockUpdateInsertingAt(XLogRecPtr insertingAt);
 
+static void LogChecksumStateChange(ChecksumState state);
+
 /*
  * 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
@@ -4628,8 +4631,7 @@ ReadControlFile(void)
 #endif
 
 	/* Make the initdb settings visible as GUC variables, too */
-	SetConfigOption("data_checksums", DataChecksumsEnabled() ? "yes" : "no",
-					PGC_INTERNAL, PGC_S_OVERRIDE);
+	data_checksums = ControlFile->data_checksum_state;
 }
 
 void
@@ -4685,13 +4687,36 @@ GetSystemIdentifier(void)
 }
 
 /*
- * Are checksums enabled for data pages?
+ * Returns the checksum state
+ */
+ChecksumState
+DataChecksumsState(void)
+{
+	Assert(ControlFile != NULL);
+	return ControlFile->data_checksum_state;
+}
+
+/*
+ * Are read checksums enabled for data pages?
+ */
+bool
+DataChecksumsEnabledReads(void)
+{
+	Assert(ControlFile != NULL);
+	return (ControlFile->data_checksum_version > 0 &&
+			ControlFile->data_checksum_state >= CHECKSUMS_ENFORCING);
+}
+
+/*
+ * Are write checksums enabled for data pages?
  */
+
 bool
-DataChecksumsEnabled(void)
+DataChecksumsEnabledWrites(void)
 {
 	Assert(ControlFile != NULL);
-	return (ControlFile->data_checksum_version > 0);
+	return (ControlFile->data_checksum_version > 0 &&
+			(ControlFile->data_checksum_state != CHECKSUMS_DISABLED));
 }
 
 /*
@@ -5075,6 +5100,7 @@ BootStrapXLOG(void)
 	ControlFile->wal_log_hints = wal_log_hints;
 	ControlFile->track_commit_timestamp = track_commit_timestamp;
 	ControlFile->data_checksum_version = bootstrap_data_checksum_version;
+	ControlFile->data_checksum_state = bootstrap_data_checksum_version == 0 ? CHECKSUMS_DISABLED : CHECKSUMS_ENFORCING;
 
 	/* some additional ControlFile fields are set in WriteControlFile() */
 
@@ -12016,3 +12042,125 @@ XLogRequestWalReceiverReply(void)
 {
 	doRequestWalReceiverReply = true;
 }
+
+/*
+ * Disable checksums in a cluster
+ * We might need a "DISABLING" state, but hopefully not
+ */
+Datum
+pg_disable_checksums(PG_FUNCTION_ARGS)
+{
+	bool		status = false;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to disable checksums"))));
+
+	LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
+
+	switch (ControlFile->data_checksum_state)
+	{
+		case CHECKSUMS_DISABLED:
+			ereport(NOTICE,
+					(errmsg("checksums already disabled; skipping")));
+			break;
+		case CHECKSUMS_ENABLING:
+		case CHECKSUMS_ENFORCING:
+		case CHECKSUMS_REVALIDATING:
+
+
+			ControlFile->data_checksum_state = CHECKSUMS_DISABLED;
+			/* ControlFile->data_checksum_version = 0; */
+			data_checksums = CHECKSUMS_DISABLED;
+			UpdateControlFile();
+			status = true;
+
+			ereport(NOTICE,
+					(errmsg("disabled checksums")));
+			break;
+
+	}
+	LWLockRelease(ControlFileLock);
+
+	LogChecksumStateChange(CHECKSUMS_DISABLED);
+
+	PG_RETURN_BOOL(status);
+}
+
+
+/*
+ * Log a change in the checksum state
+ */
+void LogChecksumStateChange(ChecksumState state)
+{
+	uint8 message;
+
+	Assert(state >= CHECKSUMS_DISABLED && state <= CHECKSUMS_REVALIDATING);
+	XLogBeginInsert();
+
+	switch (state) {
+		case CHECKSUMS_DISABLED:
+			message = XLOG_CHECKSUM_DISABLE;
+			break;
+		case CHECKSUMS_ENABLING:
+			message = XLOG_CHECKSUM_ENABLE;
+			break;
+		case CHECKSUMS_ENFORCING:
+			message = XLOG_CHECKSUM_ENFORCE;
+			break;
+		case CHECKSUMS_REVALIDATING:
+			message = XLOG_CHECKSUM_REVALIDATE;
+			break;
+	}
+
+	XLogInsert(RM_CHECKSUM_ID, message);
+}
+
+
+/*
+ * Replay the ChecksumState state change
+ *
+ * Essentially we just change the pg_control field, which means we need the
+ * lock and then change the field in question.
+ */
+
+void
+checksum_redo(XLogReaderState *record) {
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+	ChecksumState state, oldState;
+
+	LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
+
+	oldState = ControlFile->data_checksum_state;
+	elog(NOTICE, "checksum_redo: running redo on info %u", info);
+
+	/* TODO: some sanity-checking of the checksum states here? */
+
+	switch (info)
+	{
+		case XLOG_CHECKSUM_DISABLE:
+			state = CHECKSUMS_DISABLED;
+			break;
+		case XLOG_CHECKSUM_ENABLE:
+			state = CHECKSUMS_ENABLING;
+			break;
+		case XLOG_CHECKSUM_ENFORCE:
+			state = CHECKSUMS_ENFORCING;
+			break;
+		case XLOG_CHECKSUM_REVALIDATE:
+			state = CHECKSUMS_REVALIDATING;
+			break;
+		default:
+			LWLockRelease(ControlFileLock);
+			elog(PANIC, "checksum_redo: unknown op code %u", info);
+			break;
+	}
+
+	ControlFile->data_checksum_state = state;
+
+	data_checksums = state;
+
+	UpdateControlFile();
+	LWLockRelease(ControlFileLock);	
+}
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 1ebacbc..59f19d8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -56,6 +56,7 @@
 #include "storage/ipc.h"
 #include "storage/procarray.h"
 #include "storage/smgr.h"
+#include "storage/checksum.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6fc5fa4..0a0ed51 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -93,7 +93,7 @@ PageIsVerified(Page page, BlockNumber blkno)
 	 */
 	if (!PageIsNew(page))
 	{
-		if (DataChecksumsEnabled())
+		if (DataChecksumsEnabledReads())
 		{
 			checksum = pg_checksum_page((char *) page, blkno);
 
@@ -1145,7 +1145,7 @@ PageSetChecksumCopy(Page page, BlockNumber blkno)
 	static char *pageCopy = NULL;
 
 	/* If we don't need a checksum, just return the passed-in data */
-	if (PageIsNew(page) || !DataChecksumsEnabled())
+	if (PageIsNew(page) || !DataChecksumsEnabledWrites())
 		return (char *) page;
 
 	/*
@@ -1172,7 +1172,7 @@ void
 PageSetChecksumInplace(Page page, BlockNumber blkno)
 {
 	/* If we don't need a checksum, just return */
-	if (PageIsNew(page) || !DataChecksumsEnabled())
+	if (PageIsNew(page) || !DataChecksumsEnabledWrites())
 		return;
 
 	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno);
diff --git a/src/backend/storage/page/checksum.c b/src/backend/storage/page/checksum.c
index b96440a..9fe8959 100644
--- a/src/backend/storage/page/checksum.c
+++ b/src/backend/storage/page/checksum.c
@@ -21,3 +21,6 @@
  * that file from the exported Postgres headers.  (Compare our CRC code.)
  */
 #include "storage/checksum_impl.h"
+
+/* global variable to store global checksum state */
+ChecksumState			data_checksums = CHECKSUMS_DISABLED;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5d8fb2e..1b790ca 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -67,6 +67,7 @@
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/bufmgr.h"
+#include "storage/checksum.h"
 #include "storage/dsm_impl.h"
 #include "storage/standby.h"
 #include "storage/fd.h"
@@ -116,6 +117,7 @@ extern char *default_tablespace;
 extern char *temp_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
+extern ChecksumState	data_checksums;
 
 #ifdef TRACE_SYNCSCAN
 extern bool trace_syncscan;
@@ -190,6 +192,7 @@ static void assign_application_name(const char *newval, void *extra);
 static bool check_cluster_name(char **newval, void **extra, GucSource source);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
+static const char *show_data_checksum_mode(void);
 
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
@@ -417,6 +420,17 @@ static const struct config_enum_entry password_encryption_options[] = {
 	{"no", PASSWORD_TYPE_PLAINTEXT, true},
 	{"1", PASSWORD_TYPE_MD5, true},
 	{"0", PASSWORD_TYPE_PLAINTEXT, true},
+};
+
+/*
+ * Although only "on", "off", and "force" are documented, we
+ * accept all the likely variants of "on" and "off".
+ */
+static const struct config_enum_entry data_checksum_options[] = {
+	{"disabled", CHECKSUMS_DISABLED, false},
+	{"enabling", CHECKSUMS_ENABLING, false},
+	{"enforcing", CHECKSUMS_ENFORCING, false},
+	{"revalidating", CHECKSUMS_REVALIDATING, false},
 	{NULL, 0, false}
 };
 
@@ -515,7 +529,6 @@ static int	max_identifier_length;
 static int	block_size;
 static int	segment_size;
 static int	wal_block_size;
-static bool data_checksums;
 static int	wal_segment_size;
 static bool integer_datetimes;
 static bool assert_enabled;
@@ -1631,17 +1644,6 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
-		{"data_checksums", PGC_INTERNAL, PRESET_OPTIONS,
-			gettext_noop("Shows whether data checksums are turned on for this cluster."),
-			NULL,
-			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
-		},
-		&data_checksums,
-		false,
-		NULL, NULL, NULL
-	},
-
-	{
 		{"syslog_sequence_numbers", PGC_SIGHUP, LOGGING_WHERE,
 			gettext_noop("Add sequence number to syslog messages to avoid duplicate suppression."),
 			NULL
@@ -3847,6 +3849,18 @@ static struct config_enum ConfigureNamesEnum[] =
 	},
 
 	{
+		{"data_checksums", PGC_INTERNAL, PRESET_OPTIONS,
+			gettext_noop("Shows the status of data_checksums in this cluster."),
+			NULL,
+			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+
+		},
+		&data_checksums,
+		CHECKSUMS_DISABLED, data_checksum_options,
+		NULL, NULL, show_data_checksum_mode
+	},
+
+	{
 		{"force_parallel_mode", PGC_USERSET, QUERY_TUNING_OTHER,
 			gettext_noop("Forces use of parallel query facilities."),
 			gettext_noop("If possible, run query using a parallel worker and with parallel restrictions.")
@@ -10457,4 +10471,17 @@ show_log_file_mode(void)
 	return buf;
 }
 
+
+static const char *
+show_data_checksum_mode(void)
+{
+	static char *states[] = {"disabled", "enabling", "enabled", "revalidating", "invalid"};
+	int			state = DataChecksumsState();
+
+	if (state < CHECKSUMS_DISABLED || state > CHECKSUMS_REVALIDATING)
+		state = 4;				/* invalid */
+
+	return states[state];
+}
+
 #include "guc-file.c"
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 20077a6..8563b45 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -67,6 +67,23 @@ dbState(DBState state)
 }
 
 static const char *
+checksumState(ChecksumState state)
+{
+	switch (state)
+	{
+		case CHECKSUMS_DISABLED:
+			return _("disabled");
+		case CHECKSUMS_ENABLING:
+			return _("enabling");
+		case CHECKSUMS_ENFORCING:
+			return _("enforcing");
+		case CHECKSUMS_REVALIDATING:
+			return _("revalidating");
+	}
+	return _("unrecognized status code");
+}
+
+static const char *
 wal_level_str(WalLevel wal_level)
 {
 	switch (wal_level)
@@ -301,5 +318,7 @@ main(int argc, char *argv[])
 		   (ControlFile->float8ByVal ? _("by value") : _("by reference")));
 	printf(_("Data page checksum version:           %u\n"),
 		   ControlFile->data_checksum_version);
+	printf(_("Data page checksum status:            %s\n"),
+		   checksumState(ControlFile->data_checksum_state));
 	return 0;
 }
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 96b7097..a50768f 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -696,6 +696,8 @@ PrintControlValues(bool guessed)
 		   (ControlFile.float8ByVal ? _("by value") : _("by reference")));
 	printf(_("Data page checksum version:           %u\n"),
 		   ControlFile.data_checksum_version);
+	printf(_("Data page checksum state:             %u\n"),
+		   ControlFile.data_checksum_state);
 }
 
 
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 852d8ca..e1988bc 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -9,6 +9,7 @@
 #include "postgres.h"
 
 #include "access/brin_xlog.h"
+#include "access/checksumxlog.h"
 #include "access/clog.h"
 #include "access/commit_ts.h"
 #include "access/generic_xlog.h"
diff --git a/src/include/access/checksumxlog.h b/src/include/access/checksumxlog.h
new file mode 100644
index 0000000..1488b9c
--- /dev/null
+++ b/src/include/access/checksumxlog.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * checksumxlog.h
+ *	  checksum xlog routines
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/checksumxlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CHECKSUM_XLOG_H
+#define CHECKSUM_XLOG_H
+
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+
+#define XLOG_CHECKSUM_DISABLE		0x00
+#define XLOG_CHECKSUM_ENABLE		0x10
+#define XLOG_CHECKSUM_ENFORCE		0x20
+#define XLOG_CHECKSUM_REVALIDATE	0x30
+
+extern void checksum_redo(XLogReaderState *record);
+extern void checksum_desc(StringInfo buf, XLogReaderState *record);
+extern const char *checksum_identify(uint8 info);
+
+#endif
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index b892aea..87e8a76 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
 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, generic_mask)
 PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
+PG_RMGR(RM_CHECKSUM_ID, "Checksum", checksum_redo, checksum_desc, checksum_identify, NULL, NULL, NULL)
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 9f036c7..98c018e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -19,6 +19,7 @@
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
 #include "storage/fd.h"
+#include "storage/checksum.h"
 
 
 /* Sync methods */
@@ -153,7 +154,7 @@ extern PGDLLIMPORT int wal_level;
  * of the bits make it to disk, but the checksum wouldn't match.  Also WAL-log
  * them if forced by wal_log_hints=on.
  */
-#define XLogHintBitIsNeeded() (DataChecksumsEnabled() || wal_log_hints)
+#define XLogHintBitIsNeeded() (DataChecksumsEnabledWrites() || wal_log_hints)
 
 /* Do we need to WAL-log information required only for Hot Standby and logical replication? */
 #define XLogStandbyInfoActive() (wal_level >= WAL_LEVEL_REPLICA)
@@ -256,7 +257,9 @@ extern char *XLogFileNameP(TimeLineID tli, XLogSegNo segno);
 
 extern void UpdateControlFile(void);
 extern uint64 GetSystemIdentifier(void);
-extern bool DataChecksumsEnabled(void);
+extern ChecksumState DataChecksumsState(void);
+extern bool DataChecksumsEnabledReads(void);
+extern bool DataChecksumsEnabledWrites(void);
 extern XLogRecPtr GetFakeLSNForUnloggedRel(void);
 extern Size XLOGShmemSize(void);
 extern void XLOGShmemInit(void);
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 578bff5..d2aba7d 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -31,7 +31,7 @@
 /*
  * Each page of XLOG file has a header like this:
  */
-#define XLOG_PAGE_MAGIC 0xD095	/* can be used as WAL version indicator */
+#define XLOG_PAGE_MAGIC 0xD096	/* can be used as WAL version indicator */
 
 typedef struct XLogPageHeaderData
 {
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 23731e9..db57d62 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -18,10 +18,10 @@
 #include "access/xlogdefs.h"
 #include "pgtime.h"				/* for pg_time_t */
 #include "port/pg_crc32c.h"
-
+#include "storage/checksum.h"
 
 /* Version identifier for this pg_control format */
-#define PG_CONTROL_VERSION	960
+#define PG_CONTROL_VERSION	1000
 
 /*
  * Body of CheckPoint XLOG records.  This is declared here because we keep
@@ -225,6 +225,12 @@ typedef struct ControlFileData
 	/* Are data pages protected by checksums? Zero if no checksum version */
 	uint32		data_checksum_version;
 
+	/*
+	 * What state the cluster is in for the in-place upgrade of the checksum
+	 * setting
+	 */
+	ChecksumState data_checksum_state;
+
 	/* CRC of all above ... MUST BE LAST! */
 	pg_crc32c	crc;
 } ControlFileData;
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bb7053a..8c9a891 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3173,6 +3173,8 @@ DESCR("export a snapshot");
 DATA(insert OID = 3810 (  pg_is_in_recovery		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 16 "" _null_ _null_ _null_ _null_ _null_ pg_is_in_recovery _null_ _null_ _null_ ));
 DESCR("true if server is in recovery");
 
+DATA(insert OID = 3353 (  pg_disable_checksums		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 16 "" _null_ _null_ _null_ _null_ _null_ pg_disable_checksums _null_ _null_ _null_ ));
+DESCR("disables cluster checksums");
 DATA(insert OID = 3820 ( pg_last_wal_receive_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_last_wal_receive_location _null_ _null_ _null_ ));
 DESCR("current wal flush location");
 DATA(insert OID = 3821 ( pg_last_wal_replay_location	PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_last_wal_replay_location _null_ _null_ _null_ ));
diff --git a/src/include/storage/checksum.h b/src/include/storage/checksum.h
index 682043f..14229cc 100644
--- a/src/include/storage/checksum.h
+++ b/src/include/storage/checksum.h
@@ -15,10 +15,25 @@
 
 #include "storage/block.h"
 
+
+/*
+ * Checksum state indicator.  Note this is stored in pg_control; if you change
+ * it, you must bump PG_CONTROL_VERSION
+ */
+typedef enum ChecksumState
+{
+	CHECKSUMS_DISABLED = 0,
+	CHECKSUMS_ENABLING,
+	CHECKSUMS_ENFORCING,
+	CHECKSUMS_REVALIDATING
+}	ChecksumState;
+
+
 /*
  * Compute the checksum for a Postgres page.  The page must be aligned on a
  * 4-byte boundary.
  */
 extern uint16 pg_checksum_page(char *page, BlockNumber blkno);
+extern ChecksumState	data_checksums;
 
 #endif   /* CHECKSUM_H */
-- 
2.8.4 (Apple Git-73)

