diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 5fbbcdaabb1d..006514fb160f 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -730,7 +730,6 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) const char *dblocale = NULL; char *dbicurules = NULL; char dblocprovider = '\0'; - char *canonname; int encoding = -1; bool dbistemplate = false; bool dballowconnections = true; @@ -1064,18 +1063,16 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) errmsg("invalid server encoding %d", encoding))); /* Check that the chosen locales are valid, and get canonical spellings */ - if (!check_locale(LC_COLLATE, dbcollate, &canonname)) + if (!check_locale(LC_COLLATE, dbcollate)) 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."))); - dbcollate = canonname; - if (!check_locale(LC_CTYPE, dbctype, &canonname)) + if (!check_locale(LC_CTYPE, dbctype)) 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."))); - dbctype = canonname; check_encoding_locale_matches(encoding, dbcollate, dbctype); diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 6e43b708c0f7..44621acf9c92 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -172,6 +172,36 @@ static pg_locale_t last_collation_cache_locale = NULL; static char *IsoLocaleName(const char *); #endif +#ifndef WIN32 +/* + * The newlocale() function needs LC_xxx_MASK, but sometimes we have LC_xxx, + * and POSIX doesn't offer a way to translate. + */ +static int +get_lc_category_mask(int category) +{ + switch (category) + { + case LC_COLLATE: + return LC_COLLATE_MASK; + case LC_CTYPE: + return LC_CTYPE_MASK; +#ifdef LC_MESSAGES + case LC_MESSAGES: + return LC_MESSAGES_MASK; +#endif + case LC_MONETARY: + return LC_MONETARY_MASK; + case LC_NUMERIC: + return LC_NUMERIC_MASK; + case LC_TIME: + return LC_TIME_MASK; + default: + return 0; + }; +} +#endif + /* * pg_perm_setlocale * @@ -281,19 +311,11 @@ pg_perm_setlocale(int category, const char *locale) /* * Is the locale name valid for the locale category? - * - * If successful, and canonname isn't NULL, a palloc'd copy of the locale's - * canonical name is stored there. This is especially useful for figuring out - * what locale name "" means (ie, the server environment value). (Actually, - * it seems that on most implementations that's the only thing it's good for; - * we could wish that setlocale gave back a canonically spelled version of - * the locale name, but typically it doesn't.) */ bool -check_locale(int category, const char *locale, char **canonname) +check_locale(int category, const char *locale) { - char *save; - char *res; + locale_t loc; /* Don't let Windows' non-ASCII locale names in. */ if (!pg_is_ascii(locale)) @@ -305,41 +327,42 @@ check_locale(int category, const char *locale, char **canonname) return false; } - if (canonname) - *canonname = NULL; /* in case of failure */ - - save = setlocale(category, NULL); - if (!save) - return false; /* won't happen, we hope */ - - /* save may be pointing at a modifiable scratch variable, see above. */ - save = pstrdup(save); - - /* set the locale with setlocale, to see if it accepts it. */ - res = setlocale(category, locale); - - /* save canonical name if requested. */ - if (res && canonname) - *canonname = pstrdup(res); - - /* restore old value. */ - if (!setlocale(category, save)) - elog(WARNING, "failed to restore old locale \"%s\"", save); - pfree(save); + if (locale[0] == 0) + { + /* + * We only accept explicit locale names, not "". We don't want to + * rely on environment variables in the backend. + */ + return false; + } - /* Don't let Windows' non-ASCII locale names out. */ - if (canonname && *canonname && !pg_is_ascii(*canonname)) + /* + * See if we can open it. Unfortunately we can't always distinguish + * out-of-memory from invalid locale name. + */ + errno = ENOENT; +#ifdef WIN32 + loc = _create_locale(category, locale); + if (loc == (locale_t) 0) + _dosmaperr(GetLastError()); +#else + loc = newlocale(get_lc_category_mask(category), locale, (locale_t) 0); +#endif + if (loc == (locale_t) 0) { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("locale name \"%s\" contains non-ASCII characters", - *canonname))); - pfree(*canonname); - *canonname = NULL; + if (errno == ENOMEM) + elog(ERROR, "out of memory"); + + /* Otherwise assume the locale doesn't exist. */ return false; } +#ifdef WIN32 + _free_locale(loc); +#else + freelocale(loc); +#endif - return (res != NULL); + return true; } @@ -357,7 +380,7 @@ check_locale(int category, const char *locale, char **canonname) bool check_locale_monetary(char **newval, void **extra, GucSource source) { - return check_locale(LC_MONETARY, *newval, NULL); + return check_locale(LC_MONETARY, *newval); } void @@ -369,7 +392,7 @@ assign_locale_monetary(const char *newval, void *extra) bool check_locale_numeric(char **newval, void **extra, GucSource source) { - return check_locale(LC_NUMERIC, *newval, NULL); + return check_locale(LC_NUMERIC, *newval); } void @@ -381,7 +404,7 @@ assign_locale_numeric(const char *newval, void *extra) bool check_locale_time(char **newval, void **extra, GucSource source) { - return check_locale(LC_TIME, *newval, NULL); + return check_locale(LC_TIME, *newval); } void @@ -417,7 +440,7 @@ check_locale_messages(char **newval, void **extra, GucSource source) * On Windows, we can't even check the value, so accept blindly */ #if defined(LC_MESSAGES) && !defined(WIN32) - return check_locale(LC_MESSAGES, *newval, NULL); + return check_locale(LC_MESSAGES, *newval); #else return true; #endif diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index c17fda2bc81d..464788612e06 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2203,8 +2203,6 @@ locale_date_order(const char *locale) * it seems that on most implementations that's the only thing it's good for; * we could wish that setlocale gave back a canonically spelled version of * the locale name, but typically it doesn't.) - * - * this should match the backend's check_locale() function */ static void check_locale_name(int category, const char *locale, char **canonname) diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 7df90f43f60e..32a41913c3fe 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -35,7 +35,7 @@ extern PGDLLIMPORT char *localized_full_months[]; /* is the databases's LC_CTYPE the C locale? */ extern PGDLLIMPORT bool database_ctype_is_c; -extern bool check_locale(int category, const char *locale, char **canonname); +extern bool check_locale(int category, const char *locale); extern char *pg_perm_setlocale(int category, const char *locale); /* diff --git a/src/port/chklocale.c b/src/port/chklocale.c index 034939f7fd2c..d622c09e9bb5 100644 --- a/src/port/chklocale.c +++ b/src/port/chklocale.c @@ -183,6 +183,7 @@ static const struct encoding_match encoding_match_list[] = { {PG_SHIFT_JIS_2004, "SJIS_2004"}, {PG_SQL_ASCII, "US-ASCII"}, + {PG_SQL_ASCII, "646"}, {PG_SQL_ASCII, NULL} /* end marker */ };