From ba34fcfe69c79688df5597ccf0a5be28eec8f946 Mon Sep 17 00:00:00 2001 From: Bohyun Lee Date: Wed, 28 Oct 2026 14:05:27 +0100 Subject: [PATCH] pg_upgrade: preserve template0 ICU rules Copy template0's daticurules alongside its other locale attributes. Escape the rules with PQescapeLiteral() and include them in the existing UPDATE so executeQueryOrDie() retains the full query in error reports. Grow the query and logging buffers with StringInfo to avoid truncating long rules or their diagnostics. Preserve errno when formatting retries. Remove target-version branches from set_locale_and_encoding(), since pg_upgrade only supports the current version as its target. Keep the source-version checks for catalog differences. --- src/bin/pg_upgrade/info.c | 18 ++++++-- src/bin/pg_upgrade/pg_upgrade.c | 74 +++++++++++++++------------------ src/bin/pg_upgrade/pg_upgrade.h | 1 + src/bin/pg_upgrade/server.c | 32 +++++++++----- src/bin/pg_upgrade/util.c | 23 +++++++++- 5 files changed, 91 insertions(+), 57 deletions(-) diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c index 5c59cfb32e8..0a88764b078 100644 --- a/src/bin/pg_upgrade/info.c +++ b/src/bin/pg_upgrade/info.c @@ -339,23 +339,28 @@ get_template0_info(ClusterInfo *cluster) int i_datcollate; int i_datctype; int i_datlocale; + int i_daticurules; if (GET_MAJOR_VERSION(cluster->major_version) >= 1700) dbres = executeQueryOrDie(conn, "SELECT encoding, datlocprovider, " - " datcollate, datctype, datlocale " + " datcollate, datctype, datlocale, daticurules " "FROM pg_catalog.pg_database " "WHERE datname='template0'"); else if (GET_MAJOR_VERSION(cluster->major_version) >= 1500) dbres = executeQueryOrDie(conn, "SELECT encoding, datlocprovider, " - " datcollate, datctype, daticulocale AS datlocale " + " datcollate, datctype, daticulocale AS datlocale, " + " %s AS daticurules " "FROM pg_catalog.pg_database " - "WHERE datname='template0'"); + "WHERE datname='template0'", + GET_MAJOR_VERSION(cluster->major_version) >= 1600 ? + "daticurules" : "NULL"); else dbres = executeQueryOrDie(conn, "SELECT encoding, 'c' AS datlocprovider, " - " datcollate, datctype, NULL AS datlocale " + " datcollate, datctype, NULL AS datlocale, " + " NULL AS daticurules " "FROM pg_catalog.pg_database " "WHERE datname='template0'"); @@ -370,6 +375,7 @@ get_template0_info(ClusterInfo *cluster) i_datcollate = PQfnumber(dbres, "datcollate"); i_datctype = PQfnumber(dbres, "datctype"); i_datlocale = PQfnumber(dbres, "datlocale"); + i_daticurules = PQfnumber(dbres, "daticurules"); locale->db_encoding = atoi(PQgetvalue(dbres, 0, i_datencoding)); locale->db_collprovider = PQgetvalue(dbres, 0, i_datlocprovider)[0]; @@ -379,6 +385,10 @@ get_template0_info(ClusterInfo *cluster) locale->db_locale = NULL; else locale->db_locale = pg_strdup(PQgetvalue(dbres, 0, i_datlocale)); + if (PQgetisnull(dbres, 0, i_daticurules)) + locale->db_icu_rules = NULL; + else + locale->db_icu_rules = pg_strdup(PQgetvalue(dbres, 0, i_daticurules)); cluster->template0 = locale; diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c index 6d01084e4b0..bc605e0ade8 100644 --- a/src/bin/pg_upgrade/pg_upgrade.c +++ b/src/bin/pg_upgrade/pg_upgrade.c @@ -473,9 +473,9 @@ set_new_cluster_char_signedness(void) /* * Copy locale and encoding information into the new cluster's template0. * - * We need to copy the encoding, datlocprovider, datcollate, datctype, and - * datlocale. We don't need datcollversion because that's never set for - * template0. + * We need to copy the encoding, datlocprovider, datcollate, datctype, + * datlocale, and daticurules. We don't need datcollversion because that's + * never set for template0. */ static void set_locale_and_encoding(void) @@ -484,6 +484,7 @@ set_locale_and_encoding(void) char *datcollate_literal; char *datctype_literal; char *datlocale_literal = NULL; + char *daticurules_literal = NULL; DbLocaleInfo *locale = old_cluster.template0; prep_status("Setting locale and encoding for new cluster"); @@ -505,50 +506,41 @@ set_locale_and_encoding(void) else datlocale_literal = "NULL"; - /* update template0 in new cluster */ - if (GET_MAJOR_VERSION(new_cluster.major_version) >= 1700) - PQclear(executeQueryOrDie(conn_new_template1, - "UPDATE pg_catalog.pg_database " - " SET encoding = %d, " - " datlocprovider = '%c', " - " datcollate = %s, " - " datctype = %s, " - " datlocale = %s " - " WHERE datname = 'template0' ", - locale->db_encoding, - locale->db_collprovider, - datcollate_literal, - datctype_literal, - datlocale_literal)); - else if (GET_MAJOR_VERSION(new_cluster.major_version) >= 1500) - PQclear(executeQueryOrDie(conn_new_template1, - "UPDATE pg_catalog.pg_database " - " SET encoding = %d, " - " datlocprovider = '%c', " - " datcollate = %s, " - " datctype = %s, " - " daticulocale = %s " - " WHERE datname = 'template0' ", - locale->db_encoding, - locale->db_collprovider, - datcollate_literal, - datctype_literal, - datlocale_literal)); + if (locale->db_icu_rules) + { + daticurules_literal = PQescapeLiteral(conn_new_template1, + locale->db_icu_rules, + strlen(locale->db_icu_rules)); + if (daticurules_literal == NULL) + pg_fatal("could not escape ICU rules for new cluster's template0: %s", + PQerrorMessage(conn_new_template1)); + } else - PQclear(executeQueryOrDie(conn_new_template1, - "UPDATE pg_catalog.pg_database " - " SET encoding = %d, " - " datcollate = %s, " - " datctype = %s " - " WHERE datname = 'template0' ", - locale->db_encoding, - datcollate_literal, - datctype_literal)); + daticurules_literal = "NULL"; + + /* update template0 in new cluster */ + PQclear(executeQueryOrDie(conn_new_template1, + "UPDATE pg_catalog.pg_database " + " SET encoding = %d, " + " datlocprovider = '%c', " + " datcollate = %s, " + " datctype = %s, " + " datlocale = %s, " + " daticurules = %s " + " WHERE datname = 'template0' ", + locale->db_encoding, + locale->db_collprovider, + datcollate_literal, + datctype_literal, + datlocale_literal, + daticurules_literal)); PQfreemem(datcollate_literal); PQfreemem(datctype_literal); if (locale->db_locale) PQfreemem(datlocale_literal); + if (locale->db_icu_rules) + PQfreemem(daticurules_literal); PQfinish(conn_new_template1); diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index 30a2e263d2d..427fb302381 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -195,6 +195,7 @@ typedef struct char *db_ctype; char db_collprovider; char *db_locale; + char *db_icu_rules; int db_encoding; } DbLocaleInfo; diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c index 7da9dffe585..a75daf09137 100644 --- a/src/bin/pg_upgrade/server.c +++ b/src/bin/pg_upgrade/server.c @@ -11,6 +11,7 @@ #include "common/connect.h" #include "fe_utils/string_utils.h" +#include "lib/stringinfo.h" #include "libpq/pqcomm.h" #include "pg_upgrade.h" @@ -123,30 +124,41 @@ cluster_conn_opts(ClusterInfo *cluster) PGresult * executeQueryOrDie(PGconn *conn, const char *fmt, ...) { - static char query[QUERY_ALLOC]; - va_list args; + int save_errno = errno; + StringInfoData query; PGresult *result; ExecStatusType status; - va_start(args, fmt); - vsnprintf(query, sizeof(query), fmt, args); - va_end(args); + initStringInfo(&query); + for (;;) + { + va_list args; + int needed; + + errno = save_errno; + va_start(args, fmt); + needed = appendStringInfoVA(&query, fmt, args); + va_end(args); + if (needed == 0) + break; + enlargeStringInfo(&query, needed); + } - pg_log(PG_VERBOSE, "executing: %s", query); - result = PQexec(conn, query); + pg_log(PG_VERBOSE, "executing: %s", query.data); + result = PQexec(conn, query.data); status = PQresultStatus(result); if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK)) { - pg_log(PG_REPORT, "SQL command failed\n%s\n%s", query, + pg_log(PG_REPORT, "SQL command failed\n%s\n%s", query.data, PQerrorMessage(conn)); PQclear(result); PQfinish(conn); printf(_("Failure, exiting\n")); exit(1); } - else - return result; + pg_free(query.data); + return result; } diff --git a/src/bin/pg_upgrade/util.c b/src/bin/pg_upgrade/util.c index e7d7ab56445..abdb35764de 100644 --- a/src/bin/pg_upgrade/util.c +++ b/src/bin/pg_upgrade/util.c @@ -12,6 +12,7 @@ #include #include "common/username.h" +#include "lib/stringinfo.h" #include "pg_upgrade.h" LogOpts log_opts; @@ -175,13 +176,30 @@ prep_status_progress(const char *fmt, ...) static void pg_log_v(eLogType type, const char *fmt, va_list ap) { - char message[QUERY_ALLOC]; + int save_errno = errno; + StringInfoData buf; + char *message; /* No incoming message should end in newline; we add that here. */ Assert(fmt); Assert(fmt[0] == '\0' || fmt[strlen(fmt) - 1] != '\n'); - vsnprintf(message, sizeof(message), _(fmt), ap); + fmt = _(fmt); + initStringInfo(&buf); + for (;;) + { + va_list ap2; + int needed; + + errno = save_errno; + va_copy(ap2, ap); + needed = appendStringInfoVA(&buf, fmt, ap2); + va_end(ap2); + if (needed == 0) + break; + enlargeStringInfo(&buf, needed); + } + message = buf.data; /* PG_VERBOSE and PG_STATUS are only output in verbose mode */ /* fopen() on log_opts.internal might have failed, so check it */ @@ -251,6 +269,7 @@ pg_log_v(eLogType type, const char *fmt, va_list ap) /* No default:, we want a warning for omitted cases */ } + pg_free(message); fflush(stdout); } base-commit: 76a55702d89ba9f5d0198c2d5d3d2d62357a8880 -- 2.50.1 (Apple Git-155)