From 7fb63f46bf762b699ceb291c8f1f68a3bee17c18 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha Date: Tue, 12 Jul 2022 09:23:06 -0400 Subject: [PATCH] WIN32 pg_import_system_collations --- src/backend/commands/collationcmds.c | 204 ++++++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 50 deletions(-) diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index fcfc02d..240fa50 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -575,6 +575,135 @@ get_icu_locale_comment(const char *localename) #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#ifdef WIN32 +#define ENUM_SYSTEM_LOCALE +#endif + + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a collation if the input locale is valid for so. + * Also keeps track of the number of valid locales and collations created. + */ +static int +CollationFromLocale(char *isolocale, char *localebuf, int *nvalid, + int *ncreated, int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokmål" or "français"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(localebuf)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); + return -1; + } + + enc = pg_get_encoding_from_locale(localebuf, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", + localebuf); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(isolocale, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + + +#ifdef ENUM_SYSTEM_LOCALE +/* Parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale. + */ +static BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + char isolocale[NAMEDATALEN]; + int result; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Simply make it POSIX looking. + */ + strcpy(isolocale, localebuf); + hyphen = strchr(isolocale, '-'); + if (hyphen) + *hyphen = '_'; + + CollationFromLocale(isolocale, localebuf, param->nvalid, param->ncreated, + param->nspid); + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + + /* * pg_import_system_collations: add known system collations to pg_collation */ @@ -633,58 +762,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokmål" or "français"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = CollationFromLocale(localebuf, localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, NULL, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -816,5 +896,29 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* USE_ICU */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) ¶m, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + PG_RETURN_INT32(ncreated); } -- 2.11.0