From b61837a49f4b4ea12e7ced8e3039ae98908c7c0b Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Tue, 2 Oct 2012 11:42:34 -0400 Subject: [PATCH] In pg_upgrade, try to convert the locale names to canonical form before comparison; also report the old/new values if they don't match. Backpatch to 9.2. --- contrib/pg_upgrade/check.c | 72 ++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/contrib/pg_upgrade/check.c b/contrib/pg_upgrade/check.c index bed10f8fb4..e4fec34195 100644 --- a/contrib/pg_upgrade/check.c +++ b/contrib/pg_upgrade/check.c @@ -21,6 +21,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster); static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster); static void check_for_reg_data_type_usage(ClusterInfo *cluster); static void get_bin_version(ClusterInfo *cluster); +static char *get_canonical_locale_name(int category, const char *locale); /* @@ -359,8 +360,23 @@ set_locale_and_encoding(ClusterInfo *cluster) i_datcollate = PQfnumber(res, "datcollate"); i_datctype = PQfnumber(res, "datctype"); - ctrl->lc_collate = pg_strdup(PQgetvalue(res, 0, i_datcollate)); - ctrl->lc_ctype = pg_strdup(PQgetvalue(res, 0, i_datctype)); + if (GET_MAJOR_VERSION(cluster->major_version) < 902) + { + /* + * Pre-9.2 did not canonicalize the supplied locale names + * to match what the system returns, while 9.2+ does, so + * convert pre-9.2 to match. + */ + ctrl->lc_collate = get_canonical_locale_name(LC_COLLATE, + pg_strdup(PQgetvalue(res, 0, i_datcollate))); + ctrl->lc_ctype = get_canonical_locale_name(LC_CTYPE, + pg_strdup(PQgetvalue(res, 0, i_datctype))); + } + else + { + ctrl->lc_collate = pg_strdup(PQgetvalue(res, 0, i_datcollate)); + ctrl->lc_ctype = pg_strdup(PQgetvalue(res, 0, i_datctype)); + } PQclear(res); } @@ -390,16 +406,23 @@ static void check_locale_and_encoding(ControlData *oldctrl, ControlData *newctrl) { - /* These are often defined with inconsistent case, so use pg_strcasecmp(). */ + /* + * These are often defined with inconsistent case, so use pg_strcasecmp(). + * They also often use inconsistent hyphenation, which we cannot fix, e.g. + * UTF-8 vs. UTF8, so at least we display the mismatching values. + */ if (pg_strcasecmp(oldctrl->lc_collate, newctrl->lc_collate) != 0) pg_log(PG_FATAL, - "old and new cluster lc_collate values do not match\n"); + "lc_collate cluster values do not match: old \"%s\", new \"%s\"\n", + oldctrl->lc_collate, newctrl->lc_collate); if (pg_strcasecmp(oldctrl->lc_ctype, newctrl->lc_ctype) != 0) pg_log(PG_FATAL, - "old and new cluster lc_ctype values do not match\n"); + "lc_ctype cluster values do not match: old \"%s\", new \"%s\"\n", + oldctrl->lc_ctype, newctrl->lc_ctype); if (pg_strcasecmp(oldctrl->encoding, newctrl->encoding) != 0) pg_log(PG_FATAL, - "old and new cluster encoding values do not match\n"); + "encoding cluster values do not match: old \"%s\", new \"%s\"\n", + oldctrl->encoding, newctrl->encoding); } @@ -931,3 +954,40 @@ get_bin_version(ClusterInfo *cluster) cluster->bin_version = (pre_dot * 100 + post_dot) * 100; } + + +/* + * get_canonical_locale_name + * + * Send the locale name to the system, and hope we get back a canonical + * version. This should match the backend's check_locale() function. + */ +static char * +get_canonical_locale_name(int category, const char *locale) +{ + char *save; + char *res; + + save = setlocale(category, NULL); + if (!save) + pg_log(PG_FATAL, "failed to get the current locale\n"); + + /* 'save' may be pointing at a modifiable scratch variable, so copy it. */ + save = pg_strdup(save); + + /* set the locale with setlocale, to see if it accepts it. */ + res = setlocale(category, locale); + + if (!res) + pg_log(PG_FATAL, "failed to get system local name for \"%s\"\n", res); + + res = pg_strdup(res); + + /* restore old value. */ + if (!setlocale(category, save)) + pg_log(PG_FATAL, "failed to restore old locale \"%s\"\n", save); + + free(save); + + return res; +}