From 1aab6805919b84a2f2477132d96f2147ed36150e Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Fri, 21 Feb 2025 10:23:39 -0800 Subject: [PATCH] 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=false) can pg_upgrade properly without the prerequisite of acquiring an x86 VM. Reviewed-by: Noah Misch Discussion: https://postgr.es/m/CB11ADBC-0C3F-4FE0-A678-666EE80CBB07%40amazon.com --- doc/src/sgml/ref/pgupgrade.sgml | 53 +++++++++++++++++++++ src/bin/pg_upgrade/check.c | 12 +++++ src/bin/pg_upgrade/option.c | 12 +++++ src/bin/pg_upgrade/pg_upgrade.c | 10 +++- src/bin/pg_upgrade/pg_upgrade.h | 3 ++ src/bin/pg_upgrade/t/005_char_signedness.pl | 17 +++++++ 6 files changed, 105 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml index 4d9ca2a5616..6f29ffad76b 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -285,6 +285,59 @@ PostgreSQL documentation + + option + + + Manually set the default char signedness of new clusters. Possible values + are signed and unsigned. + + + In the C language, the default signedness of the char type + (when not explicitly specified) varies across platforms. For example, + char defaults to signed char on x86 CPUs but + to unsigned char on ARM CPUs. + + + Starting from PostgreSQL 18, database clusters + maintain their own default char signedness setting, which can be used to + ensure consistent behavior across platforms with different default char + signedness. By default, pg_upgrade preserves + the char signedness setting when upgrading from an existing cluster. + However, when upgrading from PostgreSQL 17 or + earlier, pg_upgrade adopts the char signedness + of the platform on which it was built. + + + This option allows you to explicitly set the default char signedness for + the new cluster, overriding any inherited values. There are two specific + scenarios where this option is relevant: + + + + If you are planning to migrate to a different platform after the upgrade, + you should not use this option. The default behavior is right in this case. + Instead, perform the upgrade on the original platform without this flag, + and then migrate the cluster afterward. This is the recommended and safest + approach. + + + + + If you have already migrated the cluster to a platform with different + char signedness (for example, from an x86-based system to an ARM-based + system), you should use this option to specify the signedness matching + the original platform's default char signedness. Additionally, it's + essential not to modify any data files between migrating data files and + running pg_upgrade. pg_upgrade + should be the first operation that starts the cluster on the new platform. + + + + + + + diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c index 7ca1d8fffc9..d6f629dd3a2 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -838,6 +838,18 @@ check_cluster_versions(void) GET_MAJOR_VERSION(new_cluster.bin_version)) pg_fatal("New cluster data and binary directories are from different major versions."); + /* + * Since from version 18, newly created database clusters always have + * 'signed' default char-signedness, it makes less sense to use + * --set-char-signedness option for upgrading from version 18 or later. + * Users who want to change the default char signedness of the new + * cluster, they can use pg_resetwal manually before the upgrade. + */ + if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1800 && + user_opts.char_signedness != -1) + pg_fatal("%s option cannot be used to upgrade from PostgreSQL %s and later.", + "--set-char-signedness", "18"); + check_ok(); } diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c index 3fd487086a5..fe716c4c805 100644 --- a/src/bin/pg_upgrade/option.c +++ b/src/bin/pg_upgrade/option.c @@ -61,6 +61,7 @@ parseCommandLine(int argc, char *argv[]) {"copy-file-range", no_argument, NULL, 3}, {"sync-method", required_argument, NULL, 4}, {"no-statistics", no_argument, NULL, 5}, + {"set-char-signedness", required_argument, NULL, 6}, {NULL, 0, NULL, 0} }; @@ -72,6 +73,7 @@ parseCommandLine(int argc, char *argv[]) user_opts.do_sync = true; user_opts.transfer_mode = TRANSFER_MODE_COPY; user_opts.do_statistics = true; + user_opts.char_signedness = -1; os_info.progname = get_progname(argv[0]); @@ -218,6 +220,14 @@ parseCommandLine(int argc, char *argv[]) user_opts.do_statistics = false; break; + case 6: + 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); @@ -313,6 +323,8 @@ 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(_(" --no-statistics do not import statistics from old cluster\n")); + printf(_(" --set-char-signedness=OPTION set new cluster char signedness to \"signed\" or\n")); + printf(_(" \"unsigned\"\n")); printf(_(" --sync-method=METHOD set method for syncing files to disk\n")); printf(_(" -?, --help show this help, then exit\n")); printf(_("\n" diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c index cc7357b5599..d95c491fb57 100644 --- a/src/bin/pg_upgrade/pg_upgrade.c +++ b/src/bin/pg_upgrade/pg_upgrade.c @@ -399,8 +399,14 @@ set_new_cluster_char_signedness(void) { bool new_char_signedness; - /* Inherit the source database's signedness */ - new_char_signedness = old_cluster.controldata.default_char_signedness; + /* + * Use the specified char signedness if specified. Otherwise we inherit + * the source database's signedness. + */ + if (user_opts.char_signedness != -1) + new_char_signedness = (user_opts.char_signedness == 1); + else + 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) diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index 6937d49b5e7..f4e375d27c7 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -334,6 +334,9 @@ typedef struct char *socketdir; /* directory to use for Unix sockets */ char *sync_method; bool do_statistics; /* carry over statistics from old cluster */ + int char_signedness; /* default char signedness: -1 for initial + * value, 1 for "signed" and 0 for + * "unsigned" */ } UserOpts; typedef struct diff --git a/src/bin/pg_upgrade/t/005_char_signedness.pl b/src/bin/pg_upgrade/t/005_char_signedness.pl index 05c3014a27d..c024106863e 100644 --- a/src/bin/pg_upgrade/t/005_char_signedness.pl +++ b/src/bin/pg_upgrade/t/005_char_signedness.pl @@ -40,6 +40,23 @@ command_like( qr/Default char data signedness:\s+unsigned/, 'updated default char signedness is unsigned in control file'); +# Cannot use --set-char-signedness option for upgrading from v18+ +command_fails( + [ + 'pg_upgrade', '--no-sync', + '-d', $old->data_dir, + '-D', $new->data_dir, + '-b', $old->config_data('--bindir'), + '-B', $new->config_data('--bindir'), + '-s', $new->host, + '-p', $old->port, + '-P', $new->port, + '-set-char-signedness', 'signed', + $mode + ], + '--set-char-signedness option cannot be used for upgrading from v18 or later' +); + # pg_upgrade should be successful. command_ok( [ -- 2.30.2