1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00

Move code for collation version into provider-specific files.

Author: Andreas Karlsson
Discussion: https://postgr.es/m/4548a168-62cd-457b-8d06-9ba7b985c477%40proxel.se
This commit is contained in:
Jeff Davis
2025-01-08 13:54:07 -08:00
parent 3c49d462db
commit 4f5cef2607
4 changed files with 123 additions and 95 deletions

View File

@@ -69,10 +69,6 @@
#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;
}

View 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 */
}

View 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.
*

View File

@@ -25,6 +25,14 @@
#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
*