Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a8238f8

Browse files
pg_upgrade: Preserve default char signedness value from old cluster.
Commit 44fe30f 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_upgrade is running. It then sets the 'default_char_signedness' value according to the current platform's default character signedness. Reviewed-by: Noah Misch <noah@leadboat.com> Discussion: https://postgr.es/m/CB11ADBC-0C3F-4FE0-A678-666EE80CBB07%40amazon.com
1 parent 30666d1 commit a8238f8

File tree

4 files changed

+142
-1
lines changed

4 files changed

+142
-1
lines changed

src/bin/pg_upgrade/controldata.c

+43-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "postgres_fe.h"
1111

1212
#include <ctype.h>
13+
#include <limits.h> /* for CHAR_MIN */
1314

1415
#include "common/string.h"
1516
#include "pg_upgrade.h"
@@ -62,6 +63,7 @@ get_control_data(ClusterInfo *cluster)
6263
bool got_date_is_int = false;
6364
bool got_data_checksum_version = false;
6465
bool got_cluster_state = false;
66+
bool got_default_char_signedness = false;
6567
char *lc_collate = NULL;
6668
char *lc_ctype = NULL;
6769
char *lc_monetary = NULL;
@@ -501,6 +503,25 @@ get_control_data(ClusterInfo *cluster)
501503
cluster->controldata.data_checksum_version = str2uint(p);
502504
got_data_checksum_version = true;
503505
}
506+
else if ((p = strstr(bufin, "Default char data signedness:")) != NULL)
507+
{
508+
p = strchr(p, ':');
509+
510+
if (p == NULL || strlen(p) <= 1)
511+
pg_fatal("%d: controldata retrieval problem", __LINE__);
512+
513+
/* Skip the colon and any whitespace after it */
514+
p++;
515+
while (isspace((unsigned char) *p))
516+
p++;
517+
518+
/* The value should be either 'signed' or 'unsigned' */
519+
if (strcmp(p, "signed") != 0 && strcmp(p, "unsigned") != 0)
520+
pg_fatal("%d: controldata retrieval problem", __LINE__);
521+
522+
cluster->controldata.default_char_signedness = strcmp(p, "signed") == 0;
523+
got_default_char_signedness = true;
524+
}
504525
}
505526

506527
rc = pclose(output);
@@ -561,6 +582,21 @@ get_control_data(ClusterInfo *cluster)
561582
}
562583
}
563584

585+
/*
586+
* Pre-v18 database clusters don't have the default char signedness
587+
* information. We use the char signedness of the platform where
588+
* pg_upgrade was built.
589+
*/
590+
if (cluster->controldata.cat_ver < DEFAULT_CHAR_SIGNEDNESS_CAT_VER)
591+
{
592+
Assert(!got_default_char_signedness);
593+
#if CHAR_MIN != 0
594+
cluster->controldata.default_char_signedness = true;
595+
#else
596+
cluster->controldata.default_char_signedness = false;
597+
#endif
598+
}
599+
564600
/* verify that we got all the mandatory pg_control data */
565601
if (!got_xid || !got_oid ||
566602
!got_multi || !got_oldestxid ||
@@ -572,7 +608,9 @@ get_control_data(ClusterInfo *cluster)
572608
!got_index || !got_toast ||
573609
(!got_large_object &&
574610
cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
575-
!got_date_is_int || !got_data_checksum_version)
611+
!got_date_is_int || !got_data_checksum_version ||
612+
(!got_default_char_signedness &&
613+
cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
576614
{
577615
if (cluster == &old_cluster)
578616
pg_log(PG_REPORT,
@@ -641,6 +679,10 @@ get_control_data(ClusterInfo *cluster)
641679
if (!got_data_checksum_version)
642680
pg_log(PG_REPORT, " data checksum version");
643681

682+
/* value added in Postgres 18 */
683+
if (!got_default_char_signedness)
684+
pg_log(PG_REPORT, " default char signedness");
685+
644686
pg_fatal("Cannot continue without required control information, terminating");
645687
}
646688
}

src/bin/pg_upgrade/pg_upgrade.c

+28
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
*/
5555
#define RESTORE_TRANSACTION_SIZE 1000
5656

57+
static void set_new_cluster_char_signedness(void);
5758
static void set_locale_and_encoding(void);
5859
static void prepare_new_cluster(void);
5960
static void prepare_new_globals(void);
@@ -154,6 +155,7 @@ main(int argc, char **argv)
154155
*/
155156

156157
copy_xact_xlog_xid();
158+
set_new_cluster_char_signedness();
157159

158160
/* New now using xids of the old system */
159161

@@ -388,6 +390,32 @@ setup(char *argv0)
388390
}
389391
}
390392

393+
/*
394+
* Set the new cluster's default char signedness using the old cluster's
395+
* value.
396+
*/
397+
static void
398+
set_new_cluster_char_signedness(void)
399+
{
400+
bool new_char_signedness;
401+
402+
/* Inherit the source database's signedness */
403+
new_char_signedness = old_cluster.controldata.default_char_signedness;
404+
405+
/* Change the char signedness of the new cluster, if necessary */
406+
if (new_cluster.controldata.default_char_signedness != new_char_signedness)
407+
{
408+
prep_status("Setting the default char signedness for new cluster");
409+
410+
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
411+
"\"%s/pg_resetwal\" --char-signedness %s \"%s\"",
412+
new_cluster.bindir,
413+
new_char_signedness ? "signed" : "unsigned",
414+
new_cluster.pgdata);
415+
416+
check_ok();
417+
}
418+
}
391419

392420
/*
393421
* Copy locale and encoding information into the new cluster's template0.

src/bin/pg_upgrade/pg_upgrade.h

+6
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ extern char *output_files[];
125125
*/
126126
#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
127127

128+
/*
129+
* The control file was changed to have the default char signedness,
130+
* commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
131+
*/
132+
#define DEFAULT_CHAR_SIGNEDNESS_CAT_VER 202502212
128133

129134
/*
130135
* Each relation is represented by a relinfo structure.
@@ -245,6 +250,7 @@ typedef struct
245250
bool date_is_int;
246251
bool float8_pass_by_value;
247252
uint32 data_checksum_version;
253+
bool default_char_signedness;
248254
} ControlData;
249255

250256
/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright (c) 2025, PostgreSQL Global Development Group
2+
3+
# Tests for handling the default char signedness during upgrade.
4+
5+
use strict;
6+
use warnings FATAL => 'all';
7+
8+
use PostgreSQL::Test::Cluster;
9+
use PostgreSQL::Test::Utils;
10+
use Test::More;
11+
12+
# Can be changed to test the other modes
13+
my $mode = $ENV{PG_TEST_PG_UPGRADE_MODE} || '--copy';
14+
15+
# Initialize old and new clusters
16+
my $old = PostgreSQL::Test::Cluster->new('old');
17+
my $new = PostgreSQL::Test::Cluster->new('new');
18+
$old->init();
19+
$new->init();
20+
21+
# Check the default char signedness of both the old and the new clusters.
22+
# Newly created clusters unconditionally use 'signed'.
23+
command_like(
24+
[ 'pg_controldata', $old->data_dir ],
25+
qr/Default char data signedness:\s+signed/,
26+
'default char signedness of old cluster is signed in control file');
27+
command_like(
28+
[ 'pg_controldata', $new->data_dir ],
29+
qr/Default char data signedness:\s+signed/,
30+
'default char signedness of new cluster is signed in control file');
31+
32+
# Set the old cluster's default char signedness to unsigned for test.
33+
command_ok(
34+
[ 'pg_resetwal', '--char-signedness', 'unsigned', '-f', $old->data_dir ],
35+
"set old cluster's default char signedness to unsigned");
36+
37+
# Check if the value is successfully updated.
38+
command_like(
39+
[ 'pg_controldata', $old->data_dir ],
40+
qr/Default char data signedness:\s+unsigned/,
41+
'updated default char signedness is unsigned in control file');
42+
43+
# pg_upgrade should be successful.
44+
command_ok(
45+
[
46+
'pg_upgrade', '--no-sync',
47+
'-d', $old->data_dir,
48+
'-D', $new->data_dir,
49+
'-b', $old->config_data('--bindir'),
50+
'-B', $new->config_data('--bindir'),
51+
'-s', $new->host,
52+
'-p', $old->port,
53+
'-P', $new->port,
54+
$mode
55+
],
56+
'run of pg_upgrade');
57+
58+
# Check if the default char signedness of the new cluster inherited
59+
# the old cluster's value.
60+
command_like(
61+
[ 'pg_controldata', $new->data_dir ],
62+
qr/Default char data signedness:\s+unsigned/,
63+
'the default char signedness is updated during pg_upgrade');
64+
65+
done_testing();

0 commit comments

Comments
 (0)