On Mon, Apr 27, 2020 at 1:20 PM Amit Kapila <amit.kapil...@gmail.com> 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;

Reply via email to