Move code for collation version into provider-specific files.
authorJeff Davis <[email protected]>
Wed, 8 Jan 2025 21:54:07 +0000 (13:54 -0800)
committerJeff Davis <[email protected]>
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
  *