From 285bb2a7b6515cbc95519025ea49d9706afc9718 Mon Sep 17 00:00:00 2001
From: Ian Barwick <barwick@gmail.com>
Date: Mon, 10 Aug 2026 23:16:21 +0900
Subject: [PATCH v3] Add data_checksum_state to pg_control_checkpoint

Commit f19c0eccae added the data_checksum_version to the pg_controldata
output, but omitted a corresponding change to the pg_control_checkpoint
SQL function, which reports the same checkpoint information. Have both
the function and pg_controldata emit the textual checksum state rather
than the internal enum value.

Backpatch to v19 where online checksums were introduced.

Author: Ian Barwick <barwick@gmail.com>
Discussion: https://postgr.es/m/CAB8KJ=hb765sE8bKC-6sh=Yp3sCjN8xs474yuBrkwyoTM2pgZA@mail.gmail.com
Backpatch-through: 19
---
 doc/src/sgml/func/func-info.sgml              |  5 +++++
 src/backend/access/rmgrdesc/xlogdesc.c        | 20 +------------------
 src/backend/utils/misc/pg_controldata.c       |  9 ++++++---
 src/bin/pg_controldata/pg_controldata.c       |  4 ++--
 src/common/controldata_utils.c                | 19 ++++++++++++++++++
 src/include/access/xlog.h                     |  1 -
 src/include/catalog/pg_proc.dat               |  6 +++---
 src/include/common/controldata_utils.h        |  1 +
 .../modules/test_checksums/t/001_basic.pl     | 10 ++++++++++
 9 files changed, 47 insertions(+), 28 deletions(-)

diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml
index 122fc740f1a..48bc84978ec 100644
--- a/doc/src/sgml/func/func-info.sgml
+++ b/doc/src/sgml/func/func-info.sgml
@@ -3496,6 +3496,11 @@ acl      | {postgres=arwdDxtm/postgres,foo=r/postgres}
        <entry><type>xid</type></entry>
       </row>
 
+      <row>
+       <entry><structfield>data_checksum_state</structfield></entry>
+       <entry><type>text</type></entry>
+      </row>
+
       <row>
        <entry><structfield>checkpoint_time</structfield></entry>
        <entry><type>timestamp with time zone</type></entry>
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 2468a7d2578..2c70f1f9498 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -18,6 +18,7 @@
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
 #include "catalog/pg_control.h"
+#include "common/controldata_utils.h"
 #include "storage/checksum.h"
 #include "utils/guc.h"
 #include "utils/timestamp.h"
@@ -55,25 +56,6 @@ get_wal_level_string(int wal_level)
 	return wal_level_str;
 }
 
-const char *
-get_checksum_state_string(uint32 state)
-{
-	switch (state)
-	{
-		case PG_DATA_CHECKSUM_VERSION:
-			return "on";
-		case PG_DATA_CHECKSUM_INPROGRESS_OFF:
-			return "inprogress-off";
-		case PG_DATA_CHECKSUM_INPROGRESS_ON:
-			return "inprogress-on";
-		case PG_DATA_CHECKSUM_OFF:
-			return "off";
-	}
-
-	Assert(false);
-	return "?";
-}
-
 void
 xlog2_desc(StringInfo buf, XLogReaderState *record)
 {
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index d229ae35209..5a31bb5ae2a 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -69,8 +69,8 @@ pg_control_system(PG_FUNCTION_ARGS)
 Datum
 pg_control_checkpoint(PG_FUNCTION_ARGS)
 {
-	Datum		values[19];
-	bool		nulls[19];
+	Datum		values[20];
+	bool		nulls[20];
 	TupleDesc	tupdesc;
 	HeapTuple	htup;
 	ControlFileData *ControlFile;
@@ -154,9 +154,12 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 	values[17] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
 	nulls[17] = false;
 
-	values[18] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->checkPointCopy.time));
+	values[18] = CStringGetTextDatum(get_checksum_state_string(ControlFile->checkPointCopy.dataChecksumState));
 	nulls[18] = false;
 
+	values[19] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->checkPointCopy.time));
+	nulls[19] = false;
+
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 6fc87ed114d..875a7f9153c 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -289,8 +289,8 @@ main(int argc, char *argv[])
 		   ControlFile->checkPointCopy.oldestCommitTsXid);
 	printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
 		   ControlFile->checkPointCopy.newestCommitTsXid);
-	printf(_("Latest checkpoint's data_checksum_version:%u\n"),
-		   ControlFile->checkPointCopy.dataChecksumState);
+	printf(_("Latest checkpoint's data_checksum_state:%s\n"),
+		   get_checksum_state_string(ControlFile->checkPointCopy.dataChecksumState));
 	printf(_("Time of latest checkpoint:            %s\n"),
 		   ckpttime_str);
 	printf(_("Fake LSN counter for unlogged rels:   %X/%08X\n"),
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index 0e8e03c566c..2de61124962 100644
--- a/src/common/controldata_utils.c
+++ b/src/common/controldata_utils.c
@@ -282,3 +282,22 @@ update_controlfile(const char *DataDir,
 #endif
 	}
 }
+
+const char *
+get_checksum_state_string(uint32 state)
+{
+	switch (state)
+	{
+		case PG_DATA_CHECKSUM_VERSION:
+			return "on";
+		case PG_DATA_CHECKSUM_INPROGRESS_OFF:
+			return "inprogress-off";
+		case PG_DATA_CHECKSUM_INPROGRESS_ON:
+			return "inprogress-on";
+		case PG_DATA_CHECKSUM_OFF:
+			return "off";
+	}
+
+	Assert(false);
+	return "?";
+}
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 4dd98624204..2f90d656f05 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -256,7 +256,6 @@ extern void SetDataChecksumsOnInProgress(void);
 extern void SetDataChecksumsOn(void);
 extern void SetDataChecksumsOff(void);
 extern const char *show_data_checksums(void);
-extern const char *get_checksum_state_string(uint32 state);
 extern void InitLocalDataChecksumState(void);
 extern void SetLocalDataChecksumState(uint32 data_checksum_version);
 extern bool GetDefaultCharSignedness(void);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f8a021987b5..49d2c15e8aa 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12371,9 +12371,9 @@
   descr => 'pg_controldata checkpoint state information as a function',
   proname => 'pg_control_checkpoint', provolatile => 'v',
   prorettype => 'record', proargtypes => '',
-  proallargtypes => '{pg_lsn,pg_lsn,text,int4,int4,bool,bool,text,oid,xid,xid,xid,oid,xid,xid,oid,xid,xid,timestamptz}',
-  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{checkpoint_lsn,redo_lsn,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,logical_decoding,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,checkpoint_time}',
+  proallargtypes => '{pg_lsn,pg_lsn,text,int4,int4,bool,bool,text,oid,xid,xid,xid,oid,xid,xid,oid,xid,xid,text,timestamptz}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{checkpoint_lsn,redo_lsn,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,logical_decoding,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,data_checksum_state,checkpoint_time}',
   prosrc => 'pg_control_checkpoint' },
 
 { oid => '3443',
diff --git a/src/include/common/controldata_utils.h b/src/include/common/controldata_utils.h
index 6dd0999f805..132b10f51c6 100644
--- a/src/include/common/controldata_utils.h
+++ b/src/include/common/controldata_utils.h
@@ -17,5 +17,6 @@ extern ControlFileData *get_controlfile_by_exact_path(const char *ControlFilePat
 													  bool *crc_ok_p);
 extern void update_controlfile(const char *DataDir,
 							   ControlFileData *ControlFile, bool do_sync);
+extern const char *get_checksum_state_string(uint32 state);
 
 #endif							/* COMMON_CONTROLDATA_UTILS_H */
diff --git a/src/test/modules/test_checksums/t/001_basic.pl b/src/test/modules/test_checksums/t/001_basic.pl
index 72e0d0df46f..5511391639e 100644
--- a/src/test/modules/test_checksums/t/001_basic.pl
+++ b/src/test/modules/test_checksums/t/001_basic.pl
@@ -34,6 +34,11 @@ my $result =
   $node->safe_psql('postgres', "SELECT count(*) FROM t WHERE a > 1 ");
 is($result, '9999', 'ensure checksummed pages can be read back');
 
+# Ensure the new state is registered properly in pg_control_checkpoint()
+$result =
+  $node->safe_psql('postgres', 'SELECT data_checksum_state FROM pg_control_checkpoint();');
+is($result, 'on', 'ensure pg_control_checkpoint reports on state');
+
 # Enable data checksums again which should be a no-op so we explicitly don't
 # wait for any state transition as none should happen here.
 enable_data_checksums($node);
@@ -50,6 +55,11 @@ disable_data_checksums($node, wait => 1);
 $result = $node->safe_psql('postgres', "SELECT count(*) FROM t WHERE a > 1");
 is($result, '10000', 'ensure previously checksummed pages can be read back');
 
+# And ensure the disabled state is shown in pg_control_checkpoint()
+$result =
+  $node->safe_psql('postgres', 'SELECT data_checksum_state FROM pg_control_checkpoint();');
+is($result, 'off', 'ensure pg_control_checkpoint reports off state');
+
 # Re-enable checksums and make sure that the underlying data has changed to
 # ensure that checksums will be different.
 $node->safe_psql('postgres', "UPDATE t SET a = a + 1;");
-- 
2.52.0

