Move code for collation version into provider-specific files.
authorJeff Davis <jdavis@postgresql.org>
Wed, 8 Jan 2025 21:54:07 +0000 (13:54 -0800)
committerJeff Davis <jdavis@postgresql.org>
Wed, 8 Jan 2025 21:54:07 +0000 (13:54 -0800)
Author: Andreas Karlsson
Discussion: https://postgr.es/m/4548a168-62cd-457b-8d06-9ba7b985c477%40proxel.se

src/backend/utils/adt/pg_locale.c
src/backend/utils/adt/pg_locale_builtin.c
src/backend/utils/adt/pg_locale_icu.c
src/backend/utils/adt/pg_locale_libc.c

index d65b9b3bd259a9f9d104f5b26e3dc19fe6417e08..dc8248fb26967039f3667e017edb629d65054cf9 100644 (file)
 #include "utils/pg_locale.h"
 #include "utils/syscache.h"
 
-#ifdef __GLIBC__
-#include <gnu/libc-version.h>
-#endif
-
 #ifdef WIN32
 #include <shlwapi.h>
 #endif
@@ -91,6 +87,7 @@
 
 /* pg_locale_builtin.c */
 extern pg_locale_t create_pg_locale_builtin(Oid collid, MemoryContext context);
+extern char *get_collation_actual_version_builtin(const char *collcollate);
 
 /* pg_locale_icu.c */
 #ifdef USE_ICU
@@ -104,6 +101,7 @@ extern size_t strnxfrm_icu(char *dest, size_t destsize,
 extern size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
                                  const char *src, ssize_t srclen,
                                  pg_locale_t locale);
+extern char *get_collation_actual_version_icu(const char *collcollate);
 #endif
 extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
 
@@ -115,6 +113,7 @@ extern int  strncoll_libc(const char *arg1, ssize_t len1,
 extern size_t strnxfrm_libc(char *dest, size_t destsize,
                            const char *src, ssize_t srclen,
                            pg_locale_t locale);
+extern char *get_collation_actual_version_libc(const char *collcollate);
 
 extern size_t strlower_builtin(char *dst, size_t dstsize, const char *src,
                               ssize_t srclen, pg_locale_t locale);
@@ -1391,100 +1390,14 @@ get_collation_actual_version(char collprovider, const char *collcollate)
 {
    char       *collversion = NULL;
 
-   /*
-    * The only two supported locales (C and C.UTF-8) are both based on memcmp
-    * and are not expected to change, but track the version anyway.
-    *
-    * Note that the character semantics may change for some locales, but the
-    * collation version only tracks changes to sort order.
-    */
    if (collprovider == COLLPROVIDER_BUILTIN)
-   {
-       if (strcmp(collcollate, "C") == 0)
-           return "1";
-       else if (strcmp(collcollate, "C.UTF-8") == 0)
-           return "1";
-       else
-           ereport(ERROR,
-                   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                    errmsg("invalid locale name \"%s\" for builtin provider",
-                           collcollate)));
-   }
-
+       collversion = get_collation_actual_version_builtin(collcollate);
 #ifdef USE_ICU
-   if (collprovider == COLLPROVIDER_ICU)
-   {
-       UCollator  *collator;
-       UVersionInfo versioninfo;
-       char        buf[U_MAX_VERSION_STRING_LENGTH];
-
-       collator = pg_ucol_open(collcollate);
-
-       ucol_getVersion(collator, versioninfo);
-       ucol_close(collator);
-
-       u_versionToString(versioninfo, buf);
-       collversion = pstrdup(buf);
-   }
-   else
+   else if (collprovider == COLLPROVIDER_ICU)
+       collversion = get_collation_actual_version_icu(collcollate);
 #endif
-       if (collprovider == COLLPROVIDER_LIBC &&
-           pg_strcasecmp("C", collcollate) != 0 &&
-           pg_strncasecmp("C.", collcollate, 2) != 0 &&
-           pg_strcasecmp("POSIX", collcollate) != 0)
-   {
-#if defined(__GLIBC__)
-       /* Use the glibc version because we don't have anything better. */
-       collversion = pstrdup(gnu_get_libc_version());
-#elif defined(LC_VERSION_MASK)
-       locale_t    loc;
-
-       /* Look up FreeBSD collation version. */
-       loc = newlocale(LC_COLLATE_MASK, collcollate, NULL);
-       if (loc)
-       {
-           collversion =
-               pstrdup(querylocale(LC_COLLATE_MASK | LC_VERSION_MASK, loc));
-           freelocale(loc);
-       }
-       else
-           ereport(ERROR,
-                   (errmsg("could not load locale \"%s\"", collcollate)));
-#elif defined(WIN32)
-       /*
-        * If we are targeting Windows Vista and above, we can ask for a name
-        * given a collation name (earlier versions required a location code
-        * that we don't have).
-        */
-       NLSVERSIONINFOEX version = {sizeof(NLSVERSIONINFOEX)};
-       WCHAR       wide_collcollate[LOCALE_NAME_MAX_LENGTH];
-
-       MultiByteToWideChar(CP_ACP, 0, collcollate, -1, wide_collcollate,
-                           LOCALE_NAME_MAX_LENGTH);
-       if (!GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
-       {
-           /*
-            * GetNLSVersionEx() wants a language tag such as "en-US", not a
-            * locale name like "English_United States.1252".  Until those
-            * values can be prevented from entering the system, or 100%
-            * reliably converted to the more useful tag format, tolerate the
-            * resulting error and report that we have no version data.
-            */
-           if (GetLastError() == ERROR_INVALID_PARAMETER)
-               return NULL;
-
-           ereport(ERROR,
-                   (errmsg("could not get collation version for locale \"%s\": error code %lu",
-                           collcollate,
-                           GetLastError())));
-       }
-       collversion = psprintf("%lu.%lu,%lu.%lu",
-                              (version.dwNLSVersion >> 8) & 0xFFFF,
-                              version.dwNLSVersion & 0xFF,
-                              (version.dwDefinedVersion >> 8) & 0xFFFF,
-                              version.dwDefinedVersion & 0xFF);
-#endif
-   }
+   else if (collprovider == COLLPROVIDER_LIBC)
+       collversion = get_collation_actual_version_libc(collcollate);
 
    return collversion;
 }
index 53a38b1e93fd829b93db3529a711dc5f235b8c2e..5161915e6b112860ce0f4c6b582876d99281be3d 100644 (file)
@@ -24,6 +24,7 @@
 
 extern pg_locale_t create_pg_locale_builtin(Oid collid,
                                            MemoryContext context);
+extern char *get_collation_actual_version_builtin(const char *collcollate);
 extern size_t strlower_builtin(char *dst, size_t dstsize, const char *src,
                               ssize_t srclen, pg_locale_t locale);
 extern size_t strtitle_builtin(char *dst, size_t dstsize, const char *src,
@@ -148,3 +149,26 @@ create_pg_locale_builtin(Oid collid, MemoryContext context)
 
    return result;
 }
+
+char *
+get_collation_actual_version_builtin(const char *collcollate)
+{
+   /*
+    * The only two supported locales (C and C.UTF-8) are both based on memcmp
+    * and are not expected to change, but track the version anyway.
+    *
+    * Note that the character semantics may change for some locales, but the
+    * collation version only tracks changes to sort order.
+    */
+   if (strcmp(collcollate, "C") == 0)
+       return "1";
+   else if (strcmp(collcollate, "C.UTF-8") == 0)
+       return "1";
+   else
+       ereport(ERROR,
+               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                errmsg("invalid locale name \"%s\" for builtin provider",
+                       collcollate)));
+
+   return NULL;                /* keep compiler quiet */
+}
index d5b5f59fe89b20ee1222a03336a85d2c1e89d003..6e1fb78bbf36f314eb106e66374e912d22b01b69 100644 (file)
@@ -67,6 +67,7 @@ extern size_t strnxfrm_icu(char *dest, size_t destsize,
 extern size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
                                  const char *src, ssize_t srclen,
                                  pg_locale_t locale);
+extern char *get_collation_actual_version_icu(const char *collcollate);
 
 typedef int32_t (*ICU_Convert_Func) (UChar *dest, int32_t destCapacity,
                                     const UChar *src, int32_t srcLength,
@@ -528,6 +529,22 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
    return result;
 }
 
+char *
+get_collation_actual_version_icu(const char *collcollate)
+{
+   UCollator  *collator;
+   UVersionInfo versioninfo;
+   char        buf[U_MAX_VERSION_STRING_LENGTH];
+
+   collator = pg_ucol_open(collcollate);
+
+   ucol_getVersion(collator, versioninfo);
+   ucol_close(collator);
+
+   u_versionToString(versioninfo, buf);
+   return pstrdup(buf);
+}
+
 /*
  * Convert a string in the database encoding into a string of UChars.
  *
index 85dce4508eecd53bcb011f67569b069f9b774cf1..81120061b503efbea3e0b989013c9889fdaa8afb 100644 (file)
 #include "utils/pg_locale.h"
 #include "utils/syscache.h"
 
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
+#ifdef WIN32
+#include <shlwapi.h>
+#endif
+
 /*
  * Size of stack buffer to use for string transformations, used to avoid heap
  * allocations in typical cases. This should be large enough that most strings
@@ -48,6 +56,7 @@ extern int    strncoll_libc(const char *arg1, ssize_t len1,
 extern size_t strnxfrm_libc(char *dest, size_t destsize,
                            const char *src, ssize_t srclen,
                            pg_locale_t locale);
+extern char *get_collation_actual_version_libc(const char *collcollate);
 static locale_t make_libc_collator(const char *collate,
                                   const char *ctype);
 static void report_newlocale_failure(const char *localename);
@@ -610,6 +619,71 @@ strnxfrm_libc(char *dest, size_t destsize, const char *src, ssize_t srclen,
    return result;
 }
 
+char *
+get_collation_actual_version_libc(const char *collcollate)
+{
+   char       *collversion = NULL;
+
+   if (pg_strcasecmp("C", collcollate) != 0 &&
+       pg_strncasecmp("C.", collcollate, 2) != 0 &&
+       pg_strcasecmp("POSIX", collcollate) != 0)
+   {
+#if defined(__GLIBC__)
+       /* Use the glibc version because we don't have anything better. */
+       collversion = pstrdup(gnu_get_libc_version());
+#elif defined(LC_VERSION_MASK)
+       locale_t    loc;
+
+       /* Look up FreeBSD collation version. */
+       loc = newlocale(LC_COLLATE_MASK, collcollate, NULL);
+       if (loc)
+       {
+           collversion =
+               pstrdup(querylocale(LC_COLLATE_MASK | LC_VERSION_MASK, loc));
+           freelocale(loc);
+       }
+       else
+           ereport(ERROR,
+                   (errmsg("could not load locale \"%s\"", collcollate)));
+#elif defined(WIN32)
+       /*
+        * If we are targeting Windows Vista and above, we can ask for a name
+        * given a collation name (earlier versions required a location code
+        * that we don't have).
+        */
+       NLSVERSIONINFOEX version = {sizeof(NLSVERSIONINFOEX)};
+       WCHAR       wide_collcollate[LOCALE_NAME_MAX_LENGTH];
+
+       MultiByteToWideChar(CP_ACP, 0, collcollate, -1, wide_collcollate,
+                           LOCALE_NAME_MAX_LENGTH);
+       if (!GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
+       {
+           /*
+            * GetNLSVersionEx() wants a language tag such as "en-US", not a
+            * locale name like "English_United States.1252".  Until those
+            * values can be prevented from entering the system, or 100%
+            * reliably converted to the more useful tag format, tolerate the
+            * resulting error and report that we have no version data.
+            */
+           if (GetLastError() == ERROR_INVALID_PARAMETER)
+               return NULL;
+
+           ereport(ERROR,
+                   (errmsg("could not get collation version for locale \"%s\": error code %lu",
+                           collcollate,
+                           GetLastError())));
+       }
+       collversion = psprintf("%lu.%lu,%lu.%lu",
+                              (version.dwNLSVersion >> 8) & 0xFFFF,
+                              version.dwNLSVersion & 0xFF,
+                              (version.dwDefinedVersion >> 8) & 0xFFFF,
+                              version.dwDefinedVersion & 0xFF);
+#endif
+   }
+
+   return collversion;
+}
+
 /*
  * strncoll_libc_win32_utf8
  *