From 49b24b4c67b43765d06ff699cd481c2abe094c8d Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Wed, 2 Oct 2024 15:11:23 -0700
Subject: [PATCH v2 3/5] pg_upgrade: Inherit default char signedness from old
 cluster.

Commit XXX introduced the 'default_char_signedness' field in
controlfile. Newly created database clusters always set this field to
'signed'.

This change ensures that pg_upgrade updates the
'default_char_signedness' to 'unsigned' if the source database cluster
has signedness=false. For source clusters from v17 or earlier, which
lack the 'default_char_signedness' information, pg_upgrade assumes the
source cluster was initialized on the same platform where pg_upgrae is
running. It then sets the 'default_char_signedness' value according to
the current platform's default character signedness.

Reviewed-by:
Discussion: https://postgr.es/m/CB11ADBC-0C3F-4FE0-A678-666EE80CBB07%40amazon.com
---
 src/bin/pg_upgrade/controldata.c | 26 ++++++++++++++++++++-
 src/bin/pg_upgrade/pg_upgrade.c  | 40 ++++++++++++++++++++++++++++++++
 src/bin/pg_upgrade/pg_upgrade.h  |  1 +
 3 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 854c6887a2..6c63b25647 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -62,6 +62,7 @@ get_control_data(ClusterInfo *cluster)
 	bool		got_date_is_int = false;
 	bool		got_data_checksum_version = false;
 	bool		got_cluster_state = false;
+	bool		got_default_char_signedness = false;
 	char	   *lc_collate = NULL;
 	char	   *lc_ctype = NULL;
 	char	   *lc_monetary = NULL;
@@ -206,6 +207,13 @@ get_control_data(ClusterInfo *cluster)
 		got_data_checksum_version = true;
 	}
 
+	/* Only in <= 17 */
+	if (GET_MAJOR_VERSION(cluster->major_version) <= 1700)
+	{
+		cluster->controldata.default_char_signedness = true;
+		got_default_char_signedness = true;
+	}
+
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -501,6 +509,17 @@ get_control_data(ClusterInfo *cluster)
 			cluster->controldata.data_checksum_version = str2uint(p);
 			got_data_checksum_version = true;
 		}
+		else if ((p = strstr(bufin, "char data signedness:")) != NULL)
+		{
+			p = strchr(p, ':');
+
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem", __LINE__);
+
+			p++;				/* remove ':' char */
+			cluster->controldata.default_char_signedness = strstr(p, "signed") != NULL;
+			got_default_char_signedness = true;
+		}
 	}
 
 	rc = pclose(output);
@@ -572,7 +591,8 @@ get_control_data(ClusterInfo *cluster)
 		!got_index || !got_toast ||
 		(!got_large_object &&
 		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
-		!got_date_is_int || !got_data_checksum_version)
+		!got_date_is_int || !got_data_checksum_version ||
+		!got_default_char_signedness)
 	{
 		if (cluster == &old_cluster)
 			pg_log(PG_REPORT,
@@ -641,6 +661,10 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_data_checksum_version)
 			pg_log(PG_REPORT, "  data checksum version");
 
+		/* value added in Postgres 18 */
+		if (!got_default_char_signedness)
+			pg_log(PG_REPORT, "  default char signedness");
+
 		pg_fatal("Cannot continue without required control information, terminating");
 	}
 }
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 663235816f..68cbac46ce 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -39,6 +39,7 @@
 #include "postgres_fe.h"
 
 #include <time.h>
+#include <limits.h>				/* for CHAR_MIN */
 
 #include "catalog/pg_class_d.h"
 #include "common/file_perm.h"
@@ -54,6 +55,7 @@
  */
 #define RESTORE_TRANSACTION_SIZE 1000
 
+static void set_new_cluster_char_signedness(void);
 static void set_locale_and_encoding(void);
 static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
@@ -154,6 +156,7 @@ main(int argc, char **argv)
 	 */
 
 	copy_xact_xlog_xid();
+	set_new_cluster_char_signedness();
 
 	/* New now using xids of the old system */
 
@@ -388,6 +391,43 @@ setup(char *argv0)
 	}
 }
 
+static void
+set_new_cluster_char_signedness(void)
+{
+	bool		new_char_signedness;
+
+	prep_status("Setting the default char signedness for new cluster");
+
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 17)
+	{
+		/*
+		 * Pre-v18 database clusters don't have the default char signedness
+		 * information. We use the char signedness of the platform where
+		 * pg_upgrade was built.
+		 */
+#if CHAR_MIN != 0
+		new_char_signedness = true;
+#else
+		new_char_signedness = false;
+#endif
+	}
+	else
+	{
+		/* Set the source database signedness */
+		new_char_signedness = old_cluster.controldata.default_char_signedness;
+	}
+
+	/* Change the char signedness of the new cluster, if necessary */
+	if (new_cluster.controldata.default_char_signedness != new_char_signedness)
+	{
+		exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+				  "\"%s/pg_resetwal\" --char-signedness %s \"%s\"",
+				  new_cluster.bindir,
+				  new_char_signedness ? "signed" : "unsigned",
+				  new_cluster.pgdata);
+		check_ok();
+	}
+}
 
 /*
  * Copy locale and encoding information into the new cluster's template0.
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 53f693c2d4..a2cbf2d6d9 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -245,6 +245,7 @@ typedef struct
 	bool		date_is_int;
 	bool		float8_pass_by_value;
 	uint32		data_checksum_version;
+	bool		default_char_signedness;
 } ControlData;
 
 /*
-- 
2.39.3

