On Mon, Apr 27, 2020 at 1:20 PM Amit Kapila <[email protected]> wrote:
> I think we should backpatch this till 9.5 as I could see the changes
> made by commit 0fb54de9 to support MSVC2015 are present in that branch
> and the same is mentioned in the commit message. Would you like to
> prepare patches (and test those) for back-branches?
>
I do not have means to test these patches using Visual Studio previous to
2012, but please find attached patches for 9.5-9.6 and 10-11-12 as of
version 14. The extension is 'txt' not to break the cfbot.
> I have made few cosmetic changes in the attached patch which includes
> adding/editing a few comments, ran pgindent, etc. I have replaced the
> reference of "IETF-standardized" with "Unix-style" as we are already
> using it at other places in the comments as well.
LGTM.
Regards,
Juan José Santamaría Flecha
>
>
diff --git a/src/backend/utils/adt/pg_locale.c
b/src/backend/utils/adt/pg_locale.c
index b9e01b6..10eb0ab 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -220,6 +220,7 @@ pg_perm_setlocale(int category, const char *locale)
result = IsoLocaleName(locale);
if (result == NULL)
result = (char *) locale;
+ elog(DEBUG3, "IsoLocaleName() executed; locale:
\"%s\"", result);
#endif /* WIN32 */
break;
#endif /* LC_MESSAGES */
@@ -953,25 +954,106 @@ cache_locale_time(void)
* string. Furthermore, msvcr110.dll changed the undocumented _locale_t
* content to carry locale names instead of locale identifiers.
*
- * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
- * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
- * Unix-style values of the lc_messages GUC can elicit localized messages. In
- * particular, every lc_messages setting that initdb can select automatically
- * will yield only C-locale messages. XXX This could be fixed by running the
- * fully-qualified locale name through a lookup table.
+ * Visual Studio 2015 should still be able to do the same as Visual Studio
+ * 2012, but the declaration of locale_name is missing in _locale_t, causing
+ * this code compilation to fail, hence this falls back instead on to
+ * enumerating all system locales by using EnumSystemLocalesEx to find the
+ * required loacle name. If the input argument is in Unix-style then we can
+ * get ISO Locale name directly by using GetLocaleInfoEx() with LCType as
+ * LOCALE_SNAME.
+ *
+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol in
+ * releases before Windows 8. IsoLocaleName() always fails in a MinGW-built
+ * postgres.exe, so only Unix-style values of the lc_messages GUC can elicit
+ * localized messages. In particular, every lc_messages setting that initdb
+ * can select automatically will yield only C-locale messages. XXX This could
+ * be fixed by running the fully-qualified locale name through a lookup table.
*
* This function returns a pointer to a static buffer bearing the converted
* name or NULL if conversion fails.
*
- * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
- * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
+ * [1] https://docs.microsoft.com/en-us/windows/win32/intl/locale-identifiers
+ * [2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
*/
+
+#if _MSC_VER >= 1900
+/*
+ * Callback function for EnumSystemLocalesEx() in IsoLocaleName().
+ *
+ * This search is done by enumerating all system locales, trying to match a
+ * locale with the format: <Language>[_<Country>], e.g. English[_United States]
+ *
+ * The input is a three wchar_t array as an LPARAM. The first element is the
+ * locale_name we want to match, the second element is an allocated buffer
+ * where the Unix-style locale is copied if a match is found, and the third
+ * element is the search status, 1 if a match was found, 0 otherwise.
+ */
+static BOOL CALLBACK
+search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
+{
+ wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
+ wchar_t **argv;
+
+ (void) (dwFlags);
+
+ argv = (wchar_t **) lparam;
+ *argv[2] = (wchar_t) 0;
+
+ memset(test_locale, 0, sizeof(test_locale));
+
+ /* Get the name of the <Language> in English */
+ if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
+ test_locale,
LOCALE_NAME_MAX_LENGTH))
+ {
+ /*
+ * If the enumerated locale does not have a hyphen ("en") OR
the
+ * lc_message input does not have an underscore ("English"), we
only
+ * need to compare the <Language> tags.
+ */
+ if (wcsrchr(pStr, '-') == NULL || wcsrchr(argv[0], '_') == NULL)
+ {
+ if (_wcsicmp(argv[0], test_locale) == 0)
+ {
+ wcscpy(argv[1], pStr);
+ *argv[2] = (wchar_t) 1;
+ return FALSE;
+ }
+ }
+
+ /*
+ * We have to compare a full <Language>_<Country> tag, so we
append
+ * the underscore and name of the country/region in English,
e.g.
+ * "English_United States".
+ */
+ else
+ {
+ size_t len;
+
+ wcscat(test_locale, L"_");
+ len = wcslen(test_locale);
+ if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
+ test_locale +
len,
+
LOCALE_NAME_MAX_LENGTH - len))
+ {
+ if (_wcsicmp(argv[0], test_locale) == 0)
+ {
+ wcscpy(argv[1], pStr);
+ *argv[2] = (wchar_t) 1;
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+#endif /* _MSC_VER >= 1900 */
+
static char *
IsoLocaleName(const char *winlocname)
{
#if (_MSC_VER >= 1400) /* VC8.0 or later */
- static char iso_lc_messages[32];
- _locale_t loct = NULL;
+ static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
if (pg_strcasecmp("c", winlocname) == 0 ||
pg_strcasecmp("posix", winlocname) == 0)
@@ -979,18 +1061,74 @@ IsoLocaleName(const char *winlocname)
strcpy(iso_lc_messages, "C");
return iso_lc_messages;
}
-
- loct = _create_locale(LC_CTYPE, winlocname);
- if (loct != NULL)
+ else
{
#if (_MSC_VER >= 1700) /* Visual Studio 2012 or later */
- size_t rc;
+ size_t rc = -1;
char *hyphen;
+#if (_MSC_VER >= 1900) /* Visual Studio 2015 or later */
+ wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
+ wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
+ char *period;
+ int len;
+ int ret_val;
+
+ /*
+ * Valid locales have the following syntax:
+ * <Language>[_<Country>[.<CodePage>]]
+ *
+ * GetLocaleInfoEx can only take locale name without code-page
and for
+ * the purpose of this API the code-page doesn't matter.
+ */
+ period = strchr(winlocname, '.');
+ if (period != NULL)
+ len = period - winlocname;
+ else
+ len = pg_mbstrlen(winlocname);
+ memset(wc_locale_name, 0, sizeof(wc_locale_name));
+ memset(buffer, 0, sizeof(buffer));
+ MultiByteToWideChar(CP_ACP, 0, winlocname, len, wc_locale_name,
+ LOCALE_NAME_MAX_LENGTH);
+
+ /*
+ * If the lc_messages is already an Unix-style string, we have a
+ * direct match with LOCALE_SNAME, e.g. en-US, en_US.
+ */
+ ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME,
(LPWSTR) &buffer,
+
LOCALE_NAME_MAX_LENGTH);
+ if (!ret_val)
+ {
+ /*
+ * Search for a locale in the system that matches
language and
+ * county names.
+ */
+ wchar_t *argv[3];
+
+ argv[0] = wc_locale_name;
+ argv[1] = buffer;
+ argv[2] = (wchar_t *) &ret_val;
+ EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS,
(LPARAM) argv,
+ NULL);
+ }
+
+ if (ret_val)
+ {
+ /* Locale names use only ASCII, any conversion locale
suffices. */
+ rc = wchar2char(iso_lc_messages, buffer,
sizeof(iso_lc_messages),
+ NULL);
+ }
+#else /* Visual Studio 2015
or later */
+ _locale_t loct;
+
+ loct = _create_locale(LC_CTYPE, winlocname);
+ if (loct != NULL)
+ {
+ rc = wchar2char(iso_lc_messages,
loct->locinfo->locale_name[LC_CTYPE],
+
sizeof(iso_lc_messages), NULL);
+ _free_locale(loct);
+ }
+#endif /* Visual Studio 2015
or later */
- /* Locale names use only ASCII, any conversion locale suffices.
*/
- rc = wchar2char(iso_lc_messages,
loct->locinfo->locale_name[LC_CTYPE],
- sizeof(iso_lc_messages), NULL);
- _free_locale(loct);
if (rc == -1 || rc == sizeof(iso_lc_messages))
return NULL;
@@ -1010,22 +1148,27 @@ IsoLocaleName(const char *winlocname)
hyphen = strchr(iso_lc_messages, '-');
if (hyphen)
*hyphen = '_';
-#else
+#else /* Visual Studio 2012
or later */
char isolang[32],
isocrty[32];
LCID lcid;
+ _locale_t loct;
- lcid = loct->locinfo->lc_handle[LC_CTYPE];
- if (lcid == 0)
- lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,
SUBLANG_ENGLISH_US), SORT_DEFAULT);
- _free_locale(loct);
+ loct = _create_locale(LC_CTYPE, winlocname);
+ if (loct != NULL)
+ {
+ lcid = loct->locinfo->lc_handle[LC_CTYPE];
+ if (lcid == 0)
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,
SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ _free_locale(loct);
+ }
if (!GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, isolang,
sizeof(isolang)))
return NULL;
if (!GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, isocrty,
sizeof(isocrty)))
return NULL;
snprintf(iso_lc_messages, sizeof(iso_lc_messages) - 1, "%s_%s",
isolang, isocrty);
-#endif
+#endif /* Visual Studio 2012
or later */
return iso_lc_messages;
}
return NULL;
diff --git a/src/backend/utils/adt/pg_locale.c
b/src/backend/utils/adt/pg_locale.c
index e0ebea7..d4c30c9 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -215,6 +215,7 @@ pg_perm_setlocale(int category, const char *locale)
result = IsoLocaleName(locale);
if (result == NULL)
result = (char *) locale;
+ elog(DEBUG3, "IsoLocaleName() executed; locale:
\"%s\"", result);
#endif /* WIN32 */
break;
#endif /* LC_MESSAGES */
@@ -948,25 +949,106 @@ cache_locale_time(void)
* string. Furthermore, msvcr110.dll changed the undocumented _locale_t
* content to carry locale names instead of locale identifiers.
*
- * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
- * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
- * Unix-style values of the lc_messages GUC can elicit localized messages. In
- * particular, every lc_messages setting that initdb can select automatically
- * will yield only C-locale messages. XXX This could be fixed by running the
- * fully-qualified locale name through a lookup table.
+ * Visual Studio 2015 should still be able to do the same as Visual Studio
+ * 2012, but the declaration of locale_name is missing in _locale_t, causing
+ * this code compilation to fail, hence this falls back instead on to
+ * enumerating all system locales by using EnumSystemLocalesEx to find the
+ * required loacle name. If the input argument is in Unix-style then we can
+ * get ISO Locale name directly by using GetLocaleInfoEx() with LCType as
+ * LOCALE_SNAME.
+ *
+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol in
+ * releases before Windows 8. IsoLocaleName() always fails in a MinGW-built
+ * postgres.exe, so only Unix-style values of the lc_messages GUC can elicit
+ * localized messages. In particular, every lc_messages setting that initdb
+ * can select automatically will yield only C-locale messages. XXX This could
+ * be fixed by running the fully-qualified locale name through a lookup table.
*
* This function returns a pointer to a static buffer bearing the converted
* name or NULL if conversion fails.
*
- * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
- * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
+ * [1] https://docs.microsoft.com/en-us/windows/win32/intl/locale-identifiers
+ * [2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
*/
+
+#if _MSC_VER >= 1900
+/*
+ * Callback function for EnumSystemLocalesEx() in IsoLocaleName().
+ *
+ * This search is done by enumerating all system locales, trying to match a
+ * locale with the format: <Language>[_<Country>], e.g. English[_United States]
+ *
+ * The input is a three wchar_t array as an LPARAM. The first element is the
+ * locale_name we want to match, the second element is an allocated buffer
+ * where the Unix-style locale is copied if a match is found, and the third
+ * element is the search status, 1 if a match was found, 0 otherwise.
+ */
+static BOOL CALLBACK
+search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
+{
+ wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
+ wchar_t **argv;
+
+ (void) (dwFlags);
+
+ argv = (wchar_t **) lparam;
+ *argv[2] = (wchar_t) 0;
+
+ memset(test_locale, 0, sizeof(test_locale));
+
+ /* Get the name of the <Language> in English */
+ if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
+ test_locale,
LOCALE_NAME_MAX_LENGTH))
+ {
+ /*
+ * If the enumerated locale does not have a hyphen ("en") OR
the
+ * lc_message input does not have an underscore ("English"), we
only
+ * need to compare the <Language> tags.
+ */
+ if (wcsrchr(pStr, '-') == NULL || wcsrchr(argv[0], '_') == NULL)
+ {
+ if (_wcsicmp(argv[0], test_locale) == 0)
+ {
+ wcscpy(argv[1], pStr);
+ *argv[2] = (wchar_t) 1;
+ return FALSE;
+ }
+ }
+
+ /*
+ * We have to compare a full <Language>_<Country> tag, so we
append
+ * the underscore and name of the country/region in English,
e.g.
+ * "English_United States".
+ */
+ else
+ {
+ size_t len;
+
+ wcscat(test_locale, L"_");
+ len = wcslen(test_locale);
+ if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
+ test_locale +
len,
+
LOCALE_NAME_MAX_LENGTH - len))
+ {
+ if (_wcsicmp(argv[0], test_locale) == 0)
+ {
+ wcscpy(argv[1], pStr);
+ *argv[2] = (wchar_t) 1;
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+#endif /* _MSC_VER >= 1900 */
+
static char *
IsoLocaleName(const char *winlocname)
{
#if (_MSC_VER >= 1400) /* VC8.0 or later */
- static char iso_lc_messages[32];
- _locale_t loct = NULL;
+ static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
if (pg_strcasecmp("c", winlocname) == 0 ||
pg_strcasecmp("posix", winlocname) == 0)
@@ -974,18 +1056,74 @@ IsoLocaleName(const char *winlocname)
strcpy(iso_lc_messages, "C");
return iso_lc_messages;
}
-
- loct = _create_locale(LC_CTYPE, winlocname);
- if (loct != NULL)
+ else
{
#if (_MSC_VER >= 1700) /* Visual Studio 2012 or later */
- size_t rc;
+ size_t rc = -1;
char *hyphen;
+#if (_MSC_VER >= 1900) /* Visual Studio 2015 or later */
+ wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
+ wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
+ char *period;
+ int len;
+ int ret_val;
+
+ /*
+ * Valid locales have the following syntax:
+ * <Language>[_<Country>[.<CodePage>]]
+ *
+ * GetLocaleInfoEx can only take locale name without code-page
and for
+ * the purpose of this API the code-page doesn't matter.
+ */
+ period = strchr(winlocname, '.');
+ if (period != NULL)
+ len = period - winlocname;
+ else
+ len = pg_mbstrlen(winlocname);
- /* Locale names use only ASCII, any conversion locale suffices.
*/
- rc = wchar2char(iso_lc_messages,
loct->locinfo->locale_name[LC_CTYPE],
- sizeof(iso_lc_messages), NULL);
- _free_locale(loct);
+ memset(wc_locale_name, 0, sizeof(wc_locale_name));
+ memset(buffer, 0, sizeof(buffer));
+ MultiByteToWideChar(CP_ACP, 0, winlocname, len, wc_locale_name,
+ LOCALE_NAME_MAX_LENGTH);
+
+ /*
+ * If the lc_messages is already an Unix-style string, we have a
+ * direct match with LOCALE_SNAME, e.g. en-US, en_US.
+ */
+ ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME,
(LPWSTR) &buffer,
+
LOCALE_NAME_MAX_LENGTH);
+ if (!ret_val)
+ {
+ /*
+ * Search for a locale in the system that matches
language and
+ * county names.
+ */
+ wchar_t *argv[3];
+
+ argv[0] = wc_locale_name;
+ argv[1] = buffer;
+ argv[2] = (wchar_t *) &ret_val;
+ EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS,
(LPARAM) argv,
+ NULL);
+ }
+
+ if (ret_val)
+ {
+ /* Locale names use only ASCII, any conversion locale
suffices. */
+ rc = wchar2char(iso_lc_messages, buffer,
sizeof(iso_lc_messages),
+ NULL);
+ }
+#else /* Visual Studio 2015
or later */
+ _locale_t loct;
+
+ loct = _create_locale(LC_CTYPE, winlocname);
+ if (loct != NULL)
+ {
+ rc = wchar2char(iso_lc_messages,
loct->locinfo->locale_name[LC_CTYPE],
+
sizeof(iso_lc_messages), NULL);
+ _free_locale(loct);
+ }
+#endif /* Visual Studio 2015
or later */
if (rc == -1 || rc == sizeof(iso_lc_messages))
return NULL;
@@ -1005,22 +1143,27 @@ IsoLocaleName(const char *winlocname)
hyphen = strchr(iso_lc_messages, '-');
if (hyphen)
*hyphen = '_';
-#else
+#else /* Visual Studio 2012
or later */
char isolang[32],
isocrty[32];
LCID lcid;
+ _locale_t loct;
- lcid = loct->locinfo->lc_handle[LC_CTYPE];
- if (lcid == 0)
- lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,
SUBLANG_ENGLISH_US), SORT_DEFAULT);
- _free_locale(loct);
+ loct = _create_locale(LC_CTYPE, winlocname);
+ if (loct != NULL)
+ {
+ lcid = loct->locinfo->lc_handle[LC_CTYPE];
+ if (lcid == 0)
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,
SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ _free_locale(loct);
+ }
if (!GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, isolang,
sizeof(isolang)))
return NULL;
if (!GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, isocrty,
sizeof(isocrty)))
return NULL;
snprintf(iso_lc_messages, sizeof(iso_lc_messages) - 1, "%s_%s",
isolang, isocrty);
-#endif
+#endif /* Visual Studio 2012
or later */
return iso_lc_messages;
}
return NULL;