From a70c3b59afd6715fbbd566c3b66ca2c42dfdd25d Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Wed, 2 Oct 2024 15:12:27 -0700
Subject: [PATCH v2 4/5] pg_upgrade: Add --set-char-signedness to set the
 default char signedness of new cluster.

This change adds a new option --set-char-signedness to pg_upgrade. It
enables user to set arbitrary signedness during pg_upgrade. This helps
cases where user who knew they copied the v17 source cluster from
x86 (signedness=true) to ARM (signedness=falese) can pg_upgrade
properly without the prerequisite of acquiring an x86 VM.

Reviewed-by:
Discussion: https://postgr.es/m/CB11ADBC-0C3F-4FE0-A678-666EE80CBB07%40amazon.com
---
 doc/src/sgml/ref/pgupgrade.sgml | 18 ++++++++++++++++++
 src/bin/pg_upgrade/option.c     | 12 +++++++++++-
 src/bin/pg_upgrade/pg_upgrade.c |  7 ++++++-
 src/bin/pg_upgrade/pg_upgrade.h |  3 +++
 4 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 4777381dac..91c96d70d7 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -276,6 +276,24 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--set-char-signedness=</option><replaceable>option</replaceable></term>
+      <listitem>
+       <para>
+        Manually set the default char signedness of new clusters. Possible values
+        are <literal>signed</literal> and <literal>unsigned</literal>.
+       </para>
+       <para>
+        The signedness of the 'char' type in C is implementation-dependent. For instance,
+        'signed char' is used by default on x86 CPUs, while 'unsigned char' is used on aarch
+        CPUs. <application>pg_upgrade</application> automatically inherits the char
+        signedness from the old cluster. This option is useful for upgrading the cluster
+        that user knew they copied it to a platform having different char signedness
+        (e.g. from x86 to aarch).
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-?</option></term>
       <term><option>--help</option></term>
diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c
index 6f41d63eed..9708e2e745 100644
--- a/src/bin/pg_upgrade/option.c
+++ b/src/bin/pg_upgrade/option.c
@@ -60,6 +60,7 @@ parseCommandLine(int argc, char *argv[])
 		{"copy", no_argument, NULL, 2},
 		{"copy-file-range", no_argument, NULL, 3},
 		{"sync-method", required_argument, NULL, 4},
+		{"set-char-signedness", required_argument, NULL, 5},
 
 		{NULL, 0, NULL, 0}
 	};
@@ -70,6 +71,7 @@ parseCommandLine(int argc, char *argv[])
 
 	user_opts.do_sync = true;
 	user_opts.transfer_mode = TRANSFER_MODE_COPY;
+	user_opts.char_signedness = -1;
 
 	os_info.progname = get_progname(argv[0]);
 
@@ -211,7 +213,14 @@ parseCommandLine(int argc, char *argv[])
 					exit(1);
 				user_opts.sync_method = pg_strdup(optarg);
 				break;
-
+			case 5:
+				if (pg_strcasecmp(optarg, "signed") == 0)
+					user_opts.char_signedness = 1;
+				else if (pg_strcasecmp(optarg, "unsigned") == 0)
+					user_opts.char_signedness = 0;
+				else
+					pg_fatal("invalid argument for option %s", "--set-char-signedness");
+				break;
 			default:
 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
 						os_info.progname);
@@ -307,6 +316,7 @@ usage(void)
 	printf(_("  --copy                        copy files to new cluster (default)\n"));
 	printf(_("  --copy-file-range             copy files to new cluster with copy_file_range\n"));
 	printf(_("  --sync-method=METHOD          set method for syncing files to disk\n"));
+	printf(_("  --set-char-signedness=OPTION  set new cluster char signedness to \"signed\" or \"unsigned\""));
 	printf(_("  -?, --help                    show this help, then exit\n"));
 	printf(_("\n"
 			 "Before running pg_upgrade you must:\n"
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 68cbac46ce..6be853672c 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -398,7 +398,12 @@ set_new_cluster_char_signedness(void)
 
 	prep_status("Setting the default char signedness for new cluster");
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 17)
+	if (user_opts.char_signedness != -1)
+	{
+		/* Use the specified char signedness */
+		new_char_signedness = (user_opts.char_signedness == 1);
+	}
+	else if (GET_MAJOR_VERSION(old_cluster.major_version) <= 17)
 	{
 		/*
 		 * Pre-v18 database clusters don't have the default char signedness
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index a2cbf2d6d9..2fbed64d7f 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -328,6 +328,9 @@ typedef struct
 	int			jobs;			/* number of processes/threads to use */
 	char	   *socketdir;		/* directory to use for Unix sockets */
 	char	   *sync_method;
+	int			char_signedness;	/* default char signedness: -1 for initial
+									 * value, 1 for "signed" and 0 for
+									 * "unsigned" */
 } UserOpts;
 
 typedef struct
-- 
2.39.3

