>From the discussion here: https://www.postgresql.org/message-id/cafcrh--rtqbobpjyfdmpd9kycyxsxkplw7lhxymyhhxa2xo...@mail.gmail.com
the CREATE DATABASE command has a tendency to throw errors in confusing ways when using non-libc providers. I have attached a patch 0001 that fixes a misleading hint, but it's still not great. When using ICU or the builtin provider, it still requires coming up with some valid locale name for LC_COLLATE and LC_CTYPE, even though those have little or no effect. And because LOCALE is the fallback when LC_COLLATE and/or LC_CTYPE are unspecified, it's confusing to the user because they aren't even trying to specify a libc locale name at all. The solution, as I see it, is: * Force the environment variables LC_COLLATE=C and LC_CTYPE=C unconditionally, and pg_perm_setlocale() them. This requires closing a few loose ends, but it should be doable[1]. Even the libc provider uses the "_l()" functions already, and no longer depends on setlocale(). * When datlocprovider<>'c', force datcollate and datctype to NULL. * If the user specifies LC_CTYPE or LC_COLLATE to CREATE DATABASE, and the provider is not libc, then ignore LC_COLLATE/LC_CTYPE and emit a WARNING, rather than trying to set it based on LOCALE and getting an error. Regards, Jeff Davis [1] https://www.postgresql.org/message-id/cd3517c7-ddb8-454e-9dd5-70e3d84ff6a2%40eisentraut.org
From fea7ab4f0495330fae56f069520de374d75ae0b8 Mon Sep 17 00:00:00 2001 From: Jeff Davis <j...@j-davis.com> Date: Thu, 5 Jun 2025 16:40:53 -0700 Subject: [PATCH v1] Improve CREATE DATABASE error message for invalid libc locale. --- src/backend/commands/dbcommands.c | 41 +++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 5fbbcdaabb1..c95eb945016 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -1065,16 +1065,41 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) /* Check that the chosen locales are valid, and get canonical spellings */ if (!check_locale(LC_COLLATE, dbcollate, &canonname)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate), - errhint("If the locale name is specific to ICU, use ICU_LOCALE."))); + { + if (dblocprovider == COLLPROVIDER_BUILTIN) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate), + errhint("If the locale name is specific to the builtin provider, use BUILTIN_LOCALE."))); + else if (dblocprovider == COLLPROVIDER_ICU) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate), + errhint("If the locale name is specific to the ICU provider, use ICU_LOCALE."))); + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate))); + } dbcollate = canonname; if (!check_locale(LC_CTYPE, dbctype, &canonname)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype), - errhint("If the locale name is specific to ICU, use ICU_LOCALE."))); + { + if (dblocprovider == COLLPROVIDER_BUILTIN) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype), + errhint("If the locale name is specific to the builtin provider, use BUILTIN_LOCALE."))); + else if (dblocprovider == COLLPROVIDER_ICU) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype), + errhint("If the locale name is specific to the ICU provider, use ICU_LOCALE."))); + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype))); + } + dbctype = canonname; check_encoding_locale_matches(encoding, dbcollate, dbctype); -- 2.43.0