v5-0006-Support-multiple-libc-collation-provider-librarie.patch
text/x-patch
Filename: v5-0006-Support-multiple-libc-collation-provider-librarie.patch
Type: text/x-patch
Part: 1
Patch
Format: format-patch
Series: patch v5-0006
Subject: Support multiple libc collation provider libraries.
| File | + | − |
|---|---|---|
| src/backend/utils/adt/formatting.c | 37 | 13 |
| src/backend/utils/adt/pg_locale.c | 155 | 44 |
| src/include/utils/pg_locale_internal.h | 82 | 3 |
From 83a4f4d1a4a885075488f400cc2b7249495c560a Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 14 Dec 2022 15:12:35 -0800
Subject: [PATCH v5 6/7] Support multiple libc collation provider libraries.
Introduce structure pg_libc_library, which holds pointers to each
required libc method related to locale_t, encoding, collation, or
ctype. Store this as part of pg_locale_t. Each call to one of these
libc functions instead goes through this structure, so that it can
more easily be replaced by a non-builtin libc library.
Offer a hook to allow an extension to control which libc library
is loaded.
Adds a test module to test the hook functionality.
TODO: loose ends in regex code and possibly other places where libc
functions are called directly.
Author: Jeff Davis
---
src/backend/utils/adt/formatting.c | 50 +++++--
src/backend/utils/adt/pg_locale.c | 199 +++++++++++++++++++------
src/include/utils/pg_locale_internal.h | 85 ++++++++++-
3 files changed, 274 insertions(+), 60 deletions(-)
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 7f318fe2c7..e9020fef29 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -69,7 +69,6 @@
#include <math.h>
#include <float.h>
#include <limits.h>
-#include <wctype.h>
#ifdef USE_ICU
#include <unicode/ustring.h>
@@ -1729,10 +1728,16 @@ str_tolower(const char *buff, size_t nbytes, Oid collid)
{
#ifdef HAVE_LOCALE_T
if (mylocale)
- workspace[curr_char] = towlower_l(workspace[curr_char], mylocale->info.libc.lt);
+ {
+ pg_libc_library *libc = PG_LIBC_LIB(mylocale);
+ workspace[curr_char] = libc->c_towlower_l(workspace[curr_char], mylocale->info.libc.lt);
+ }
else
#endif
- workspace[curr_char] = towlower(workspace[curr_char]);
+ {
+ pg_libc_library *libc = get_default_libc_library();
+ workspace[curr_char] = libc->c_towlower(workspace[curr_char]);
+ }
}
/*
@@ -1762,7 +1767,11 @@ str_tolower(const char *buff, size_t nbytes, Oid collid)
{
#ifdef HAVE_LOCALE_T
if (mylocale)
- *p = tolower_l((unsigned char) *p, mylocale->info.libc.lt);
+ {
+ pg_libc_library *libc = PG_LIBC_LIB(mylocale);
+ *p = libc->c_tolower_l((unsigned char) *p,
+ mylocale->info.libc.lt);
+ }
else
#endif
*p = pg_tolower((unsigned char) *p);
@@ -1852,10 +1861,16 @@ str_toupper(const char *buff, size_t nbytes, Oid collid)
{
#ifdef HAVE_LOCALE_T
if (mylocale)
- workspace[curr_char] = towupper_l(workspace[curr_char], mylocale->info.libc.lt);
+ {
+ pg_libc_library *libc = PG_LIBC_LIB(mylocale);
+ workspace[curr_char] = libc->c_towupper_l(workspace[curr_char], mylocale->info.libc.lt);
+ }
else
#endif
- workspace[curr_char] = towupper(workspace[curr_char]);
+ {
+ pg_libc_library *libc = get_default_libc_library();
+ workspace[curr_char] = libc->c_towupper(workspace[curr_char]);
+ }
}
/*
@@ -1885,7 +1900,11 @@ str_toupper(const char *buff, size_t nbytes, Oid collid)
{
#ifdef HAVE_LOCALE_T
if (mylocale)
- *p = toupper_l((unsigned char) *p, mylocale->info.libc.lt);
+ {
+ pg_libc_library *libc = PG_LIBC_LIB(mylocale);
+ *p = libc->c_toupper_l((unsigned char) *p,
+ mylocale->info.libc.lt);
+ }
else
#endif
*p = pg_toupper((unsigned char) *p);
@@ -1977,19 +1996,21 @@ str_initcap(const char *buff, size_t nbytes, Oid collid)
#ifdef HAVE_LOCALE_T
if (mylocale)
{
+ pg_libc_library *libc = PG_LIBC_LIB(mylocale);
if (wasalnum)
- workspace[curr_char] = towlower_l(workspace[curr_char], mylocale->info.libc.lt);
+ workspace[curr_char] = libc->c_towlower_l(workspace[curr_char], mylocale->info.libc.lt);
else
- workspace[curr_char] = towupper_l(workspace[curr_char], mylocale->info.libc.lt);
+ workspace[curr_char] = libc->c_towupper_l(workspace[curr_char], mylocale->info.libc.lt);
wasalnum = iswalnum_l(workspace[curr_char], mylocale->info.libc.lt);
}
else
#endif
{
+ pg_libc_library *libc = get_default_libc_library();
if (wasalnum)
- workspace[curr_char] = towlower(workspace[curr_char]);
+ workspace[curr_char] = libc->c_towlower(workspace[curr_char]);
else
- workspace[curr_char] = towupper(workspace[curr_char]);
+ workspace[curr_char] = libc->c_towupper(workspace[curr_char]);
wasalnum = iswalnum(workspace[curr_char]);
}
}
@@ -2022,10 +2043,13 @@ str_initcap(const char *buff, size_t nbytes, Oid collid)
#ifdef HAVE_LOCALE_T
if (mylocale)
{
+ pg_libc_library *libc = PG_LIBC_LIB(mylocale);
if (wasalnum)
- *p = tolower_l((unsigned char) *p, mylocale->info.libc.lt);
+ *p = libc->c_tolower_l((unsigned char) *p,
+ mylocale->info.libc.lt);
else
- *p = toupper_l((unsigned char) *p, mylocale->info.libc.lt);
+ *p = libc->c_toupper_l((unsigned char) *p,
+ mylocale->info.libc.lt);
wasalnum = isalnum_l((unsigned char) *p, mylocale->info.libc.lt);
}
else
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index f65cd22fbd..f30c463881 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -115,19 +115,19 @@ char *localized_full_months[12 + 1];
/*
* The precise version of a collation provider library is important, because
* subtle changes in collation between library versions can corrupt
- * indexes. This hook allows control over how collation provider libraries are
- * loaded.
+ * indexes. These hooks allows control over how collation provider libraries
+ * are loaded.
*
* If the hook is not set, or if it returns NULL, Postgres constructs the
- * pg_icu_library structure from the symbols Postgres is linked with at build
- * time.
+ * pg_libc_library or pg_icu_library structures from the symbols Postgres is
+ * linked with at build time.
*
- * If the hook is set, it can instead construct the pg_icu_library structure
- * using custom logic. Ordinarily, this custom logic would involve finding a
- * specific known version of the collation provider library, and dynamically
- * loading the necessary symbols. If the collation version obtained from the
- * library does not match the collation version recorded in the catalog,
- * Postgres will issue a WARNING.
+ * If the hook is set, it can instead construct the pg_libc_library or
+ * pg_icu_library structures using custom logic. Ordinarily, this custom logic
+ * would involve finding a specific known version of the collation provider
+ * library, and dynamically loading the necessary symbols. If the collation
+ * version obtained from the library does not match the collation version
+ * recorded in the catalog, Postgres will issue a WARNING.
*
* The returned structure must be allocated in TopMemoryContext, and the
* associated symbols must remain valid permanently. It's expected that the
@@ -139,10 +139,12 @@ char *localized_full_months[12 + 1];
* consider what other information can be provided to allow for greater
* control over which library is loaded.
*/
+get_libc_library_hook_type get_libc_library_hook = NULL;
#ifdef USE_ICU
get_icu_library_hook_type get_icu_library_hook = NULL;
#endif
+static pg_libc_library *builtin_libc_library = NULL;
#ifdef USE_ICU
static pg_icu_library *builtin_icu_library = NULL;
#endif
@@ -173,6 +175,7 @@ static char *IsoLocaleName(const char *);
* Database default locale.
*/
static pg_locale_t default_locale = NULL;
+static pg_libc_library *default_libc = NULL;
#ifdef USE_ICU
static pg_icu_library *default_icu = NULL;
#endif
@@ -1392,7 +1395,7 @@ lc_collate_is_c(Oid collation)
if (result >= 0)
return (bool) result;
- localeptr = setlocale(LC_COLLATE, NULL);
+ localeptr = default_libc->c_setlocale(LC_COLLATE, NULL);
if (!localeptr)
elog(ERROR, "invalid LC_COLLATE setting");
@@ -1445,7 +1448,7 @@ lc_ctype_is_c(Oid collation)
if (result >= 0)
return (bool) result;
- localeptr = setlocale(LC_CTYPE, NULL);
+ localeptr = default_libc->c_setlocale(LC_CTYPE, NULL);
if (!localeptr)
elog(ERROR, "invalid LC_CTYPE setting");
@@ -1503,6 +1506,82 @@ report_newlocale_failure(const char *localename)
}
#endif /* HAVE_LOCALE_T */
+static pg_libc_library *
+get_builtin_libc_library()
+{
+ pg_libc_library *lib = NULL;
+
+ if (builtin_libc_library != NULL)
+ return builtin_libc_library;
+
+ lib = MemoryContextAllocZero(TopMemoryContext, sizeof(pg_libc_library));
+#if defined(__GLIBC__)
+ lib->libc_version = gnu_get_libc_version;
+#elif defined(WIN32)
+ lib->GetNLSVersionEx = GetNLSVersionEx;
+#endif
+ lib->c_setlocale = setlocale;
+#ifdef HAVE_LOCALE_T
+#ifndef WIN32
+ lib->c_newlocale = newlocale;
+ lib->c_freelocale = freelocale;
+ lib->c_uselocale = uselocale;
+#ifdef LC_VERSION_MASK
+ lib->c_querylocale = querylocale;
+#endif /* LC_VERSION_MASK */
+#else
+ lib->_create_locale = _create_locale;
+#endif /* WIN32 */
+#endif /* HAVE_LOCALE_T */
+ lib->c_wcstombs = wcstombs;
+ lib->c_mbstowcs = mbstowcs;
+#ifdef HAVE_LOCALE_T
+#ifdef HAVE_WCSTOMBS_L
+ lib->c_wcstombs_l = wcstombs_l;
+#endif
+#ifdef HAVE_MBSTOWCS_L
+ lib->c_mbstowcs_l = mbstowcs_l;
+#endif
+#endif
+ lib->c_strcoll = strcoll;
+ lib->c_wcscoll = wcscoll;
+ lib->c_strxfrm = strxfrm;
+#ifdef HAVE_LOCALE_T
+ lib->c_strcoll_l = strcoll_l;
+ lib->c_wcscoll_l = wcscoll_l;
+ lib->c_strxfrm_l = strxfrm_l;
+#endif
+ lib->c_tolower = tolower;
+ lib->c_toupper = toupper;
+ lib->c_iswalnum = iswalnum;
+ lib->c_towlower = towlower;
+ lib->c_towupper = towupper;
+#ifdef HAVE_LOCALE_T
+ lib->c_tolower_l = tolower_l;
+ lib->c_toupper_l = toupper_l;
+ lib->c_iswalnum_l = iswalnum_l;
+ lib->c_towlower_l = towlower_l;
+ lib->c_towupper_l = towupper_l;
+#endif
+
+ builtin_libc_library = lib;
+ return lib;
+}
+
+static pg_libc_library *
+get_libc_library(const char *collate, const char *ctype, const char *version)
+{
+ pg_libc_library *lib = NULL;
+
+ if (get_libc_library_hook != NULL)
+ lib = get_libc_library_hook(collate, ctype, version);
+
+ if(!lib)
+ lib = get_builtin_libc_library();
+
+ return lib;
+}
+
#ifdef USE_ICU
static pg_icu_library *
get_builtin_icu_library()
@@ -1614,6 +1693,7 @@ pg_newlocale(char provider, bool deterministic, const char *collate,
if (provider == COLLPROVIDER_LIBC)
{
#ifdef HAVE_LOCALE_T
+ pg_libc_library *libc = get_libc_library(collate, ctype, version);
locale_t loc;
/* newlocale's result may be leaked if we encounter an error */
@@ -1623,10 +1703,10 @@ pg_newlocale(char provider, bool deterministic, const char *collate,
/* Normal case where they're the same */
errno = 0;
#ifndef WIN32
- loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
+ loc = libc->c_newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
NULL);
#else
- loc = _create_locale(LC_ALL, collate);
+ loc = libc->_create_locale(LC_ALL, collate);
#endif
if (!loc)
report_newlocale_failure(collate);
@@ -1638,11 +1718,11 @@ pg_newlocale(char provider, bool deterministic, const char *collate,
locale_t loc1;
errno = 0;
- loc1 = newlocale(LC_COLLATE_MASK, collate, NULL);
+ loc1 = libc->c_newlocale(LC_COLLATE_MASK, collate, NULL);
if (!loc1)
report_newlocale_failure(collate);
errno = 0;
- loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
+ loc = libc->c_newlocale(LC_CTYPE_MASK, ctype, loc1);
if (!loc)
report_newlocale_failure(ctype);
#else
@@ -1659,6 +1739,7 @@ pg_newlocale(char provider, bool deterministic, const char *collate,
}
result->info.libc.lt = loc;
+ result->info.libc.lib = libc;
#else /* not HAVE_LOCALE_T */
/* platform that doesn't support locale_t */
ereport(ERROR,
@@ -1710,6 +1791,12 @@ pg_locale_deterministic(pg_locale_t locale)
return locale->deterministic;
}
+pg_libc_library *
+get_default_libc_library()
+{
+ return default_libc;
+}
+
#ifdef USE_ICU
pg_icu_library *
get_default_icu_library()
@@ -1725,6 +1812,19 @@ void
init_default_locale(char provider, const char *collate, const char *ctype,
const char *iculocale, const char *version)
{
+ default_libc = get_libc_library(collate, ctype, version);
+
+ /*
+ * If it's the builtin libc, pg_perm_setlocale() will handle
+ * setlocale(). Otherwise, it must be done here so that the collate and
+ * ctype take effect.
+ */
+ if (default_libc != builtin_libc_library)
+ {
+ default_libc->c_setlocale(LC_COLLATE, collate);
+ default_libc->c_setlocale(LC_CTYPE, ctype);
+ }
+
#ifdef USE_ICU
default_icu = get_icu_library(iculocale, version);
#endif
@@ -1913,19 +2013,23 @@ get_collation_actual_version(char collprovider, const char *collcollate)
pg_strncasecmp("C.", collcollate, 2) != 0 &&
pg_strcasecmp("POSIX", collcollate) != 0)
{
+ pg_libc_library *libc = get_libc_library(collcollate, NULL, NULL);
+
#if defined(__GLIBC__)
/* Use the glibc version because we don't have anything better. */
- collversion = pstrdup(gnu_get_libc_version());
+ if (libc->libc_version != NULL)
+ collversion = pstrdup(libc->libc_version());
#elif defined(LC_VERSION_MASK)
locale_t loc;
/* Look up FreeBSD collation version. */
- loc = newlocale(LC_COLLATE, collcollate, NULL);
+ loc = libc->c_newlocale(LC_COLLATE, collcollate, NULL);
if (loc)
{
- collversion =
- pstrdup(querylocale(LC_COLLATE_MASK | LC_VERSION_MASK, loc));
- freelocale(loc);
+ const char *query = libc->c_querylocale(
+ LC_COLLATE_MASK | LC_VERSION_MASK, loc);
+ collversion = pstrdup(query);
+ libc->c_freelocale(loc);
}
else
ereport(ERROR,
@@ -1941,7 +2045,7 @@ get_collation_actual_version(char collprovider, const char *collcollate)
MultiByteToWideChar(CP_ACP, 0, collcollate, -1, wide_collcollate,
LOCALE_NAME_MAX_LENGTH);
- if (!GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
+ if (!libc->GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
{
/*
* GetNLSVersionEx() wants a language tag such as "en-US", not a
@@ -2031,10 +2135,14 @@ pg_strncoll_libc_win32_utf8(const char *arg1, size_t len1, const char *arg2,
errno = 0;
#ifdef HAVE_LOCALE_T
if (locale)
- result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->info.libc.lt);
+ {
+ pg_libc_library *libc = PG_LIBC_LIB(locale);
+ result = libc->c_wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->info.libc.lt);
+ }
else
#endif
- result = wcscoll((LPWSTR) a1p, (LPWSTR) a2p);
+ result = default_libc->c_wcscoll((LPWSTR) a1p, (LPWSTR) a2p);
+
if (result == 2147483647) /* _NLSCMPERROR; missing from mingw
* headers */
ereport(ERROR,
@@ -2060,7 +2168,6 @@ static int
pg_strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale)
{
int result;
-
Assert(!locale || locale->provider == COLLPROVIDER_LIBC);
#ifdef WIN32
if (GetDatabaseEncoding() == PG_UTF8)
@@ -2074,14 +2181,16 @@ pg_strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale)
if (locale)
{
#ifdef HAVE_LOCALE_T
- result = strcoll_l(arg1, arg2, locale->info.libc.lt);
+ pg_libc_library *libc = PG_LIBC_LIB(locale);
+
+ result = libc->c_strcoll_l(arg1, arg2, locale->info.libc.lt);
#else
/* shouldn't happen */
elog(ERROR, "unsupported collprovider: %c", locale->provider);
#endif
}
else
- result = strcoll(arg1, arg2);
+ result = default_libc->c_strcoll(arg1, arg2);
return result;
}
@@ -2327,17 +2436,15 @@ pg_strxfrm_libc(char *dest, const char *src, size_t destsize,
{
Assert(!locale || locale->provider == COLLPROVIDER_LIBC);
-#ifdef TRUST_STXFRM
#ifdef HAVE_LOCALE_T
if (locale)
- return strxfrm_l(dest, src, destsize, locale->info.libc.lt);
+ {
+ pg_libc_library *libc = PG_LIBC_LIB(locale);
+ return libc->c_strxfrm_l(dest, src, destsize, locale->info.libc.lt);
+ }
else
#endif
- return strxfrm(dest, src, destsize);
-#else
- /* shouldn't happen */
- elog(ERROR, "unsupported collprovider: %c", locale->provider);
-#endif
+ return default_libc->c_strxfrm(dest, src, destsize);
}
static size_t
@@ -3010,21 +3117,23 @@ wchar2char(char *to, const wchar_t *from, size_t tolen, pg_locale_t locale)
if (locale == (pg_locale_t) 0)
{
/* Use wcstombs directly for the default locale */
- result = wcstombs(to, from, tolen);
+ result = default_libc->c_wcstombs(to, from, tolen);
}
else
{
#ifdef HAVE_LOCALE_T
+ pg_libc_library *libc = PG_LIBC_LIB(locale);
+
#ifdef HAVE_WCSTOMBS_L
/* Use wcstombs_l for nondefault locales */
- result = wcstombs_l(to, from, tolen, locale->info.libc.lt);
+ result = libc->c_wcstombs_l(to, from, tolen, locale->info.libc.lt);
#else /* !HAVE_WCSTOMBS_L */
/* We have to temporarily set the locale as current ... ugh */
- locale_t save_locale = uselocale(locale->info.libc.lt);
+ locale_t save_locale = libc->c_uselocale(locale->info.libc.lt);
- result = wcstombs(to, from, tolen);
+ result = libc->c_wcstombs(to, from, tolen);
- uselocale(save_locale);
+ libc->c_uselocale(save_locale);
#endif /* HAVE_WCSTOMBS_L */
#else /* !HAVE_LOCALE_T */
/* Can't have locale != 0 without HAVE_LOCALE_T */
@@ -3087,21 +3196,23 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
if (locale == (pg_locale_t) 0)
{
/* Use mbstowcs directly for the default locale */
- result = mbstowcs(to, str, tolen);
+ result = default_libc->c_mbstowcs(to, str, tolen);
}
else
{
#ifdef HAVE_LOCALE_T
+ pg_libc_library *libc = PG_LIBC_LIB(locale);
+
#ifdef HAVE_MBSTOWCS_L
/* Use mbstowcs_l for nondefault locales */
- result = mbstowcs_l(to, str, tolen, locale->info.libc.lt);
+ result = libc->c_mbstowcs_l(to, str, tolen, locale->info.libc.lt);
#else /* !HAVE_MBSTOWCS_L */
/* We have to temporarily set the locale as current ... ugh */
- locale_t save_locale = uselocale(locale->info.libc.lt);
+ locale_t save_locale = libc->c_uselocale(locale->info.libc.lt);
- result = mbstowcs(to, str, tolen);
+ result = libc->c_mbstowcs(to, str, tolen);
- uselocale(save_locale);
+ libc->c_uselocale(save_locale);
#endif /* HAVE_MBSTOWCS_L */
#else /* !HAVE_LOCALE_T */
/* Can't have locale != 0 without HAVE_LOCALE_T */
diff --git a/src/include/utils/pg_locale_internal.h b/src/include/utils/pg_locale_internal.h
index afb6c24d64..66a0e92a76 100644
--- a/src/include/utils/pg_locale_internal.h
+++ b/src/include/utils/pg_locale_internal.h
@@ -13,6 +13,9 @@
#ifndef _PG_LOCALE_INTERNAL_
#define _PG_LOCALE_INTERNAL_
+#include <wchar.h>
+#include <wctype.h>
+
#ifdef USE_ICU
#include <unicode/ubrk.h>
#include <unicode/ucnv.h>
@@ -32,6 +35,75 @@
#endif
#endif
+typedef struct pg_libc_library
+{
+ /* version */
+#if defined(__GLIBC__)
+ const char *(*libc_version) (void);
+#elif defined(WIN32)
+ BOOL (*GetNLSVersionEx) (NLS_FUNCTION function, LPCWSTR lpLocaleName,
+ LPNLSVERSIONINFOEX lpVersionInformation);
+#endif
+
+ /* locale */
+ char *(*c_setlocale) (int category, const char *locale);
+#ifdef HAVE_LOCALE_T
+#ifndef WIN32
+ locale_t (*c_newlocale) (int category_mask, const char *locale,
+ locale_t base);
+ void (*c_freelocale) (locale_t locobj);
+ locale_t (*c_uselocale) (locale_t newloc);
+#ifdef LC_VERSION_MASK
+ const char *(*c_querylocale) (int mask, locale_t locale);
+#endif
+#else
+ locale_t (*_create_locale) (int category, const char *locale);
+#endif
+#endif
+
+ /* encoding */
+ size_t (*c_wcstombs) (char *dest, const wchar_t *src, size_t n);
+ size_t (*c_mbstowcs) (wchar_t *dest, const char *src, size_t n);
+#ifdef HAVE_LOCALE_T
+#ifdef HAVE_WCSTOMBS_L
+ size_t (*c_wcstombs_l) (char *dest, const wchar_t *src, size_t n,
+ locale_t loc);
+#endif
+#ifdef HAVE_MBSTOWCS_L
+ size_t (*c_mbstowcs_l) (wchar_t *dest, const char *src, size_t n,
+ locale_t loc);
+#endif
+#endif
+
+ /* collation */
+ int (*c_strcoll) (const char *s1, const char *s2);
+ int (*c_wcscoll) (const wchar_t *ws1, const wchar_t *ws2);
+ size_t (*c_strxfrm) (char *s1, const char * s2, size_t n);
+#ifdef HAVE_LOCALE_T
+ int (*c_strcoll_l) (const char *s1, const char *s2, locale_t locale);
+ int (*c_wcscoll_l) (const wchar_t *ws1, const wchar_t *ws2,
+ locale_t locale);
+ size_t (*c_strxfrm_l) (char *s1, const char * s2, size_t n,
+ locale_t locale);
+#endif
+
+ /* ctype */
+ int (*c_tolower) (int c);
+ int (*c_toupper) (int c);
+ int (*c_iswalnum) (wint_t wc);
+ wint_t (*c_towlower) (wint_t wc);
+ wint_t (*c_towupper) (wint_t wc);
+#ifdef HAVE_LOCALE_T
+ int (*c_tolower_l) (int c, locale_t locale);
+ int (*c_toupper_l) (int c, locale_t locale);
+ int (*c_iswalnum_l) (wint_t wc, locale_t locale);
+ wint_t (*c_towlower_l) (wint_t wc, locale_t locale);
+ wint_t (*c_towupper_l) (wint_t wc, locale_t locale);
+#endif
+} pg_libc_library;
+
+#define PG_LIBC_LIB(x) ((x)->info.libc.lib)
+
#ifdef USE_ICU
/*
* An ICU library version that we're either linked against or have loaded at
@@ -146,12 +218,13 @@ struct pg_locale_struct
char *ctype;
union
{
-#ifdef HAVE_LOCALE_T
struct
{
+#ifdef HAVE_LOCALE_T
locale_t lt;
- } libc;
#endif
+ pg_libc_library *lib;
+ } libc;
#ifdef USE_ICU
struct
{
@@ -159,10 +232,16 @@ struct pg_locale_struct
pg_icu_library *lib;
} icu;
#endif
- int dummy; /* in case we have neither LOCALE_T nor ICU */
} info;
};
+typedef pg_libc_library *(*get_libc_library_hook_type)(
+ const char *collate, const char *ctype, const char *version);
+
+extern PGDLLIMPORT get_libc_library_hook_type get_libc_library_hook;
+
+extern pg_libc_library *get_default_libc_library(void);
+
#ifdef USE_ICU
typedef pg_icu_library *(*get_icu_library_hook_type)(
--
2.34.1