summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
authorJeff Davis2023-03-28 23:15:59 +0000
committerJeff Davis2023-03-28 23:34:29 +0000
commit1671f990dd669c0b72e45c7bef0fd579a10676ed (patch)
tree002e9ca8c4cd35b2f641de6c308f78b701f3b21a /src/bin
parentb7cea58822c67724effc711ae28e4077a01a7cd6 (diff)
Validate ICU locales.
For ICU collations, ensure that the locale's language exists in ICU, and that the locale can be opened. Basic validation helps avoid minor mistakes and misspellings, which often fall back to the root locale instead of the intended locale. It's even more important to avoid such mistakes in ICU versions 54 and earlier, where the same (misspelled) locale string could fall back to different locales depending on the environment. Discussion: https://postgr.es/m/11b1eeb7e7667fdd4178497aeb796c48d26e69b9.camel@j-davis.com Discussion: https://postgr.es/m/df2efad0cae7c65180df8e5ebb709e5eb4f2a82b.camel@j-davis.com Reviewed-by: Peter Eisentraut
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/initdb/initdb.c58
-rw-r--r--src/bin/initdb/t/001_initdb.pl18
2 files changed, 74 insertions, 2 deletions
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 6b20a962ecc..d5ac25904da 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2243,6 +2243,58 @@ check_icu_locale_encoding(int user_enc)
}
/*
+ * Perform best-effort check that the locale is a valid one. Should be
+ * consistent with pg_locale.c, except that it doesn't need to open the
+ * collator (that will happen during post-bootstrap initialization).
+ */
+static void
+icu_validate_locale(const char *loc_str)
+{
+#ifdef USE_ICU
+ UErrorCode status;
+ char lang[ULOC_LANG_CAPACITY];
+ bool found = false;
+
+ /* validate that we can extract the language */
+ status = U_ZERO_ERROR;
+ uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
+ if (U_FAILURE(status))
+ {
+ pg_fatal("could not get language from locale \"%s\": %s",
+ loc_str, u_errorName(status));
+ return;
+ }
+
+ /* check for special language name */
+ if (strcmp(lang, "") == 0 ||
+ strcmp(lang, "root") == 0 || strcmp(lang, "und") == 0 ||
+ strcmp(lang, "c") == 0 || strcmp(lang, "posix") == 0)
+ found = true;
+
+ /* search for matching language within ICU */
+ for (int32_t i = 0; !found && i < uloc_countAvailable(); i++)
+ {
+ const char *otherloc = uloc_getAvailable(i);
+ char otherlang[ULOC_LANG_CAPACITY];
+
+ status = U_ZERO_ERROR;
+ uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
+ if (U_FAILURE(status))
+ continue;
+
+ if (strcmp(lang, otherlang) == 0)
+ found = true;
+ }
+
+ if (!found)
+ pg_fatal("locale \"%s\" has unknown language \"%s\"",
+ loc_str, lang);
+#else
+ pg_fatal("ICU is not supported in this build");
+#endif
+}
+
+/*
* Determine default ICU locale by opening the default collator and reading
* its locale.
*
@@ -2344,9 +2396,11 @@ setlocales(void)
printf(_("Using default ICU locale \"%s\".\n"), icu_locale);
}
+ icu_validate_locale(icu_locale);
+
/*
- * In supported builds, the ICU locale ID will be checked by the
- * backend during post-bootstrap initialization.
+ * In supported builds, the ICU locale ID will be opened during
+ * post-bootstrap initialization, which will perform extra checks.
*/
#ifndef USE_ICU
pg_fatal("ICU is not supported in this build");
diff --git a/src/bin/initdb/t/001_initdb.pl b/src/bin/initdb/t/001_initdb.pl
index b97420f7e88..db7995fe28d 100644
--- a/src/bin/initdb/t/001_initdb.pl
+++ b/src/bin/initdb/t/001_initdb.pl
@@ -128,6 +128,24 @@ if ($ENV{with_icu} eq 'yes')
],
qr/error: encoding mismatch/,
'fails for encoding not supported by ICU');
+
+ command_fails_like(
+ [
+ 'initdb', '--no-sync',
+ '--locale-provider=icu',
+ '--icu-locale=nonsense-nowhere', "$tempdir/dataX"
+ ],
+ qr/error: locale "nonsense-nowhere" has unknown language "nonsense"/,
+ 'fails for nonsense language');
+
+ command_fails_like(
+ [
+ 'initdb', '--no-sync',
+ '--locale-provider=icu',
+ '--icu-locale=@colNumeric=lower', "$tempdir/dataX"
+ ],
+ qr/could not open collator for locale "\@colNumeric=lower": U_ILLEGAL_ARGUMENT_ERROR/,
+ 'fails for invalid collation argument');
}
else
{