v2-0004-Add-method-structure-toward-ICU-multi-library-sup.patch
text/x-patch
Filename: v2-0004-Add-method-structure-toward-ICU-multi-library-sup.patch
Type: text/x-patch
Part: 3
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0004
Subject: Add method structure, toward ICU multi-library support.
| File | + | − |
|---|---|---|
| src/backend/commands/collationcmds.c | 10 | 7 |
| src/backend/utils/adt/formatting.c | 50 | 17 |
| src/backend/utils/adt/pg_locale.c | 152 | 75 |
| src/include/utils/pg_locale_internal.h | 110 | 3 |
From daef4c5f6ebf6cd73dcae5402feb5ec269a50650 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 7 Dec 2022 11:07:31 -0800
Subject: [PATCH v2 4/6] Add method structure, toward ICU multi-library
support.
Introduce structure pg_icu_library, which holds pointers to each
required ICU method, and store this as part of pg_locale_t. Each call
to an ICU function instead goes through this structure, so that it can
more easily be replaced by a non-builtin ICU library.
This is a step toward support for multiple ICU libraries, to allow
control over precisely which version of a library is used and prevent
problems from subtle changes in collation order.
Author: Thomas Munro, Jeff Davis
---
src/backend/commands/collationcmds.c | 17 +-
src/backend/utils/adt/formatting.c | 67 ++++++--
src/backend/utils/adt/pg_locale.c | 227 +++++++++++++++++--------
src/include/utils/pg_locale_internal.h | 113 +++++++++++-
4 files changed, 322 insertions(+), 102 deletions(-)
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 9e84da4891..16b1bcbdc0 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -560,13 +560,14 @@ get_icu_language_tag(const char *localename)
{
char buf[ULOC_FULLNAME_CAPACITY];
UErrorCode status;
+ pg_icu_library *iculib = get_builtin_icu_library();
status = U_ZERO_ERROR;
- uloc_toLanguageTag(localename, buf, sizeof(buf), true, &status);
+ iculib->toLanguageTag(localename, buf, sizeof(buf), true, &status);
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("could not convert locale name \"%s\" to language tag: %s",
- localename, u_errorName(status))));
+ localename, iculib->errorName(status))));
return pstrdup(buf);
}
@@ -585,11 +586,12 @@ get_icu_locale_comment(const char *localename)
int32 len_uchar;
int32 i;
char *result;
+ pg_icu_library *iculib = get_builtin_icu_library();
status = U_ZERO_ERROR;
- len_uchar = uloc_getDisplayName(localename, "en",
- displayname, lengthof(displayname),
- &status);
+ len_uchar = iculib->getDisplayName(localename, "en",
+ displayname, lengthof(displayname),
+ &status);
if (U_FAILURE(status))
return NULL; /* no good reason to raise an error */
@@ -809,12 +811,13 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
#ifdef USE_ICU
{
int i;
+ pg_icu_library *iculib = get_builtin_icu_library();
/*
* Start the loop at -1 to sneak in the root locale without too much
* code duplication.
*/
- for (i = -1; i < uloc_countAvailable(); i++)
+ for (i = -1; i < iculib->countAvailable(); i++)
{
const char *name;
char *langtag;
@@ -825,7 +828,7 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
if (i == -1)
name = ""; /* ICU root locale */
else
- name = uloc_getAvailable(i);
+ name = iculib->getAvailable(i);
langtag = get_icu_language_tag(name);
iculocstr = U_ICU_VERSION_MAJOR_NUM >= 54 ? langtag : name;
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index a4bc7fa5f5..289aa569de 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1600,6 +1600,11 @@ typedef int32_t (*ICU_Convert_Func) (UChar *dest, int32_t destCapacity,
const UChar *src, int32_t srcLength,
const char *locale,
UErrorCode *pErrorCode);
+typedef int32_t (*ICU_Convert_BI_Func) (UChar *dest, int32_t destCapacity,
+ const UChar *src, int32_t srcLength,
+ UBreakIterator *bi,
+ const char *locale,
+ UErrorCode *pErrorCode);
static int32_t
icu_convert_case(ICU_Convert_Func func, pg_locale_t mylocale,
@@ -1607,6 +1612,7 @@ icu_convert_case(ICU_Convert_Func func, pg_locale_t mylocale,
{
UErrorCode status;
int32_t len_dest;
+ pg_icu_library *iculib = PG_ICU_LIB(mylocale);
len_dest = len_source; /* try first with same length */
*buff_dest = palloc(len_dest * sizeof(**buff_dest));
@@ -1624,18 +1630,42 @@ icu_convert_case(ICU_Convert_Func func, pg_locale_t mylocale,
}
if (U_FAILURE(status))
ereport(ERROR,
- (errmsg("case conversion failed: %s", u_errorName(status))));
+ (errmsg("case conversion failed: %s",
+ iculib->errorName(status))));
return len_dest;
}
+/*
+ * Like icu_convert_case, but func takes a break iterator (which we don't
+ * make use of).
+ */
static int32_t
-u_strToTitle_default_BI(UChar *dest, int32_t destCapacity,
- const UChar *src, int32_t srcLength,
- const char *locale,
- UErrorCode *pErrorCode)
+icu_convert_case_bi(ICU_Convert_BI_Func func, pg_locale_t mylocale,
+ UChar **buff_dest, UChar *buff_source, int32_t len_source)
{
- return u_strToTitle(dest, destCapacity, src, srcLength,
- NULL, locale, pErrorCode);
+ UErrorCode status;
+ int32_t len_dest;
+ pg_icu_library *iculib = PG_ICU_LIB(mylocale);
+
+ len_dest = len_source; /* try first with same length */
+ *buff_dest = palloc(len_dest * sizeof(**buff_dest));
+ status = U_ZERO_ERROR;
+ len_dest = func(*buff_dest, len_dest, buff_source, len_source, NULL,
+ mylocale->ctype, &status);
+ if (status == U_BUFFER_OVERFLOW_ERROR)
+ {
+ /* try again with adjusted length */
+ pfree(*buff_dest);
+ *buff_dest = palloc(len_dest * sizeof(**buff_dest));
+ status = U_ZERO_ERROR;
+ len_dest = func(*buff_dest, len_dest, buff_source, len_source, NULL,
+ mylocale->ctype, &status);
+ }
+ if (U_FAILURE(status))
+ ereport(ERROR,
+ (errmsg("case conversion failed: %s",
+ iculib->errorName(status))));
+ return len_dest;
}
#endif /* USE_ICU */
@@ -1701,11 +1731,12 @@ str_tolower(const char *buff, size_t nbytes, Oid collid)
int32_t len_conv;
UChar *buff_uchar;
UChar *buff_conv;
+ pg_icu_library *iculib = PG_ICU_LIB(mylocale);
- len_uchar = icu_to_uchar(&buff_uchar, buff, nbytes);
- len_conv = icu_convert_case(u_strToLower, mylocale,
+ len_uchar = icu_to_uchar(iculib, &buff_uchar, buff, nbytes);
+ len_conv = icu_convert_case(iculib->strToLower, mylocale,
&buff_conv, buff_uchar, len_uchar);
- icu_from_uchar(&result, buff_conv, len_conv);
+ icu_from_uchar(iculib, &result, buff_conv, len_conv);
pfree(buff_uchar);
pfree(buff_conv);
}
@@ -1823,11 +1854,12 @@ str_toupper(const char *buff, size_t nbytes, Oid collid)
len_conv;
UChar *buff_uchar;
UChar *buff_conv;
+ pg_icu_library *iculib = PG_ICU_LIB(mylocale);
- len_uchar = icu_to_uchar(&buff_uchar, buff, nbytes);
- len_conv = icu_convert_case(u_strToUpper, mylocale,
+ len_uchar = icu_to_uchar(iculib, &buff_uchar, buff, nbytes);
+ len_conv = icu_convert_case(iculib->strToUpper, mylocale,
&buff_conv, buff_uchar, len_uchar);
- icu_from_uchar(&result, buff_conv, len_conv);
+ icu_from_uchar(iculib, &result, buff_conv, len_conv);
pfree(buff_uchar);
pfree(buff_conv);
}
@@ -1946,11 +1978,12 @@ str_initcap(const char *buff, size_t nbytes, Oid collid)
len_conv;
UChar *buff_uchar;
UChar *buff_conv;
+ pg_icu_library *iculib = PG_ICU_LIB(mylocale);
- len_uchar = icu_to_uchar(&buff_uchar, buff, nbytes);
- len_conv = icu_convert_case(u_strToTitle_default_BI, mylocale,
- &buff_conv, buff_uchar, len_uchar);
- icu_from_uchar(&result, buff_conv, len_conv);
+ len_uchar = icu_to_uchar(iculib, &buff_uchar, buff, nbytes);
+ len_conv = icu_convert_case_bi(iculib->strToTitle, mylocale,
+ &buff_conv, buff_uchar, len_uchar);
+ icu_from_uchar(iculib, &result, buff_conv, len_conv);
pfree(buff_uchar);
pfree(buff_conv);
}
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 0a19845df4..4daff8b7b5 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -70,6 +70,8 @@
#ifdef USE_ICU
#include <unicode/ucnv.h>
+#include <unicode/ulocdata.h>
+#include <unicode/ustring.h>
#endif
#ifdef __GLIBC__
@@ -135,6 +137,7 @@ static char *IsoLocaleName(const char *);
static pg_locale_t default_locale = NULL;
#ifdef USE_ICU
+
/*
* Converter object for converting between ICU's UChar strings and C strings
* in database encoding. Since the database encoding doesn't change, we only
@@ -142,13 +145,17 @@ static pg_locale_t default_locale = NULL;
*/
static UConverter *icu_converter = NULL;
-static void init_icu_converter(void);
-static size_t uchar_length(UConverter *converter,
+static void init_icu_converter(pg_icu_library *iculib);
+static size_t uchar_length(pg_icu_library *iculib,
+ UConverter *converter,
const char *str, size_t len);
-static int32_t uchar_convert(UConverter *converter,
+static int32_t uchar_convert(pg_icu_library *iculib,
+ UConverter *converter,
UChar *dest, int32_t destlen,
const char *str, size_t srclen);
-static void icu_set_collation_attributes(UCollator *collator, const char *loc);
+static void icu_set_collation_attributes(pg_icu_library *iculib,
+ UCollator *collator,
+ const char *loc);
#endif
/*
@@ -1455,6 +1462,59 @@ report_newlocale_failure(const char *localename)
}
#endif /* HAVE_LOCALE_T */
+#ifdef USE_ICU
+pg_icu_library *
+get_builtin_icu_library()
+{
+ pg_icu_library *lib;
+
+ /*
+ * These assignments will fail to compile if an incompatible API change is
+ * made to some future version of ICU, at which point we might need to
+ * consider special treatment for different major version ranges, with
+ * intermediate trampoline functions.
+ */
+ lib = palloc0(sizeof(*lib));
+ lib->getICUVersion = u_getVersion;
+ lib->getUnicodeVersion = u_getUnicodeVersion;
+ lib->getCLDRVersion = ulocdata_getCLDRVersion;
+ lib->openCollator = ucol_open;
+ lib->closeCollator = ucol_close;
+ lib->getCollatorVersion = ucol_getVersion;
+ lib->getUCAVersion = ucol_getUCAVersion;
+ lib->versionToString = u_versionToString;
+ lib->strcoll = ucol_strcoll;
+ lib->strcollUTF8 = ucol_strcollUTF8;
+ lib->getSortKey = ucol_getSortKey;
+ lib->nextSortKeyPart = ucol_nextSortKeyPart;
+ lib->setUTF8 = uiter_setUTF8;
+ lib->errorName = u_errorName;
+ lib->strToUpper = u_strToUpper;
+ lib->strToLower = u_strToLower;
+ lib->strToTitle = u_strToTitle;
+ lib->setAttribute = ucol_setAttribute;
+ lib->openConverter = ucnv_open;
+ lib->closeConverter = ucnv_close;
+ lib->fromUChars = ucnv_fromUChars;
+ lib->toUChars = ucnv_toUChars;
+ lib->toLanguageTag = uloc_toLanguageTag;
+ lib->getDisplayName = uloc_getDisplayName;
+ lib->countAvailable = uloc_countAvailable;
+ lib->getAvailable = uloc_getAvailable;
+
+ /*
+ * Also assert the size of a couple of types used as output buffers, as a
+ * canary to tell us to add extra padding in the (unlikely) event that a
+ * later release makes these values smaller.
+ */
+ StaticAssertStmt(U_MAX_VERSION_STRING_LENGTH == 20,
+ "u_versionToString output buffer size changed incompatibly");
+ StaticAssertStmt(U_MAX_VERSION_LENGTH == 4,
+ "ucol_getVersion output buffer size changed incompatibly");
+
+ return lib;
+}
+#endif
/*
* Construct a new pg_locale_t object.
@@ -1547,20 +1607,22 @@ pg_newlocale(char provider, bool isdefault, bool deterministic,
{
UCollator *collator;
UErrorCode status;
+ pg_icu_library *iculib = get_builtin_icu_library();
/* collator may be leaked if we encounter an error */
status = U_ZERO_ERROR;
- collator = ucol_open(collate, &status);
+ collator = iculib->openCollator(collate, &status);
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("could not open collator for locale \"%s\": %s",
- collate, u_errorName(status))));
+ collate, iculib->errorName(status))));
if (U_ICU_VERSION_MAJOR_NUM < 54)
- icu_set_collation_attributes(collator, collate);
+ icu_set_collation_attributes(iculib, collator, collate);
result->info.icu.ucol = collator;
+ result->info.icu.lib = iculib;
}
#endif
else
@@ -1607,6 +1669,10 @@ pg_movelocale(MemoryContext mcxt, pg_locale_t *plocale)
{
/* not in a memory context; just reassign the pointer */
result->info.icu.ucol = locale->info.icu.ucol;
+
+ result->info.icu.lib = MemoryContextAlloc(mcxt, sizeof(pg_icu_library));
+ memcpy(result->info.icu.lib, locale->info.icu.lib, sizeof(pg_icu_library));
+ pfree(locale->info.icu.lib);
}
#endif
else
@@ -1656,7 +1722,9 @@ pg_freelocale(pg_locale_t locale)
#ifdef USE_ICU
else if (locale->provider == COLLPROVIDER_ICU)
{
- ucol_close(locale->info.icu.ucol);
+ pg_icu_library *iculib = PG_ICU_LIB(locale);
+ iculib->closeCollator(locale->info.icu.ucol);
+ pfree(locale->info.icu.lib);
}
#endif
else
@@ -1858,17 +1926,18 @@ get_collation_actual_version(char collprovider, const char *collcollate)
UErrorCode status;
UVersionInfo versioninfo;
char buf[U_MAX_VERSION_STRING_LENGTH];
+ pg_icu_library *iculib = get_builtin_icu_library();
status = U_ZERO_ERROR;
- collator = ucol_open(collcollate, &status);
+ collator = iculib->openCollator(collcollate, &status);
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("could not open collator for locale \"%s\": %s",
- collcollate, u_errorName(status))));
- ucol_getVersion(collator, versioninfo);
- ucol_close(collator);
+ collcollate, iculib->errorName(status))));
+ iculib->getCollatorVersion(collator, versioninfo);
+ iculib->closeCollator(collator);
- u_versionToString(versioninfo, buf);
+ iculib->versionToString(versioninfo, buf);
collversion = pstrdup(buf);
}
else
@@ -2120,16 +2189,17 @@ pg_strncoll_icu_no_utf8(const char *arg1, size_t len1,
UChar *uchar1,
*uchar2;
int result;
+ pg_icu_library *iculib = PG_ICU_LIB(locale);
Assert(locale->provider == COLLPROVIDER_ICU);
#ifdef HAVE_UCOL_STRCOLLUTF8
Assert(GetDatabaseEncoding() != PG_UTF8);
#endif
- init_icu_converter();
+ init_icu_converter(iculib);
- ulen1 = uchar_length(icu_converter, arg1, len1);
- ulen2 = uchar_length(icu_converter, arg2, len2);
+ ulen1 = uchar_length(iculib, icu_converter, arg1, len1);
+ ulen2 = uchar_length(iculib, icu_converter, arg2, len2);
bufsize1 = (ulen1 + 1) * sizeof(UChar);
bufsize2 = (ulen2 + 1) * sizeof(UChar);
@@ -2140,12 +2210,12 @@ pg_strncoll_icu_no_utf8(const char *arg1, size_t len1,
uchar1 = (UChar *) buf;
uchar2 = (UChar *) (buf + bufsize1);
- ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
- ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);
+ ulen1 = uchar_convert(iculib, icu_converter, uchar1, ulen1 + 1, arg1, len1);
+ ulen2 = uchar_convert(iculib, icu_converter, uchar2, ulen2 + 1, arg2, len2);
- result = ucol_strcoll(locale->info.icu.ucol,
- uchar1, ulen1,
- uchar2, ulen2);
+ result = iculib->strcoll(locale->info.icu.ucol,
+ uchar1, ulen1,
+ uchar2, ulen2);
if (buf != sbuf)
pfree(buf);
@@ -2166,6 +2236,7 @@ pg_strncoll_icu(const char *arg1, size_t len1, const char *arg2, size_t len2,
pg_locale_t locale)
{
int result;
+ pg_icu_library *iculib = PG_ICU_LIB(locale);
Assert(locale->provider == COLLPROVIDER_ICU);
@@ -2175,13 +2246,14 @@ pg_strncoll_icu(const char *arg1, size_t len1, const char *arg2, size_t len2,
UErrorCode status;
status = U_ZERO_ERROR;
- result = ucol_strcollUTF8(locale->info.icu.ucol,
- arg1, len1,
- arg2, len2,
- &status);
+ result = iculib->strcollUTF8(locale->info.icu.ucol,
+ arg1, len1,
+ arg2, len2,
+ &status);
if (U_FAILURE(status))
ereport(ERROR,
- (errmsg("collation failed: %s", u_errorName(status))));
+ (errmsg("collation failed: %s",
+ iculib->errorName(status))));
}
else
#endif
@@ -2360,12 +2432,13 @@ pg_strnxfrm_icu(char *dest, const char *src, size_t srclen, size_t destsize,
int32_t ulen;
size_t uchar_bsize;
Size result_bsize;
+ pg_icu_library *iculib = PG_ICU_LIB(locale);
Assert(locale->provider == COLLPROVIDER_ICU);
- init_icu_converter();
+ init_icu_converter(iculib);
- ulen = uchar_length(icu_converter, src, srclen);
+ ulen = uchar_length(iculib, icu_converter, src, srclen);
uchar_bsize = (ulen + 1) * sizeof(UChar);
@@ -2374,11 +2447,11 @@ pg_strnxfrm_icu(char *dest, const char *src, size_t srclen, size_t destsize,
uchar = (UChar *) buf;
- ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
+ ulen = uchar_convert(iculib, icu_converter, uchar, ulen + 1, src, srclen);
- result_bsize = ucol_getSortKey(locale->info.icu.ucol,
- uchar, ulen,
- (uint8_t *) dest, destsize);
+ result_bsize = iculib->getSortKey(locale->info.icu.ucol,
+ uchar, ulen,
+ (uint8_t *) dest, destsize);
if (buf != sbuf)
pfree(buf);
@@ -2407,13 +2480,14 @@ pg_strnxfrm_prefix_icu_no_utf8(char *dest, const char *src, size_t srclen,
UChar *uchar = NULL;
size_t uchar_bsize;
Size result_bsize;
+ pg_icu_library *iculib = PG_ICU_LIB(locale);
Assert(locale->provider == COLLPROVIDER_ICU);
Assert(GetDatabaseEncoding() != PG_UTF8);
- init_icu_converter();
+ init_icu_converter(iculib);
- ulen = uchar_length(icu_converter, src, srclen);
+ ulen = uchar_length(iculib, icu_converter, src, srclen);
uchar_bsize = (ulen + 1) * sizeof(UChar);
@@ -2422,21 +2496,19 @@ pg_strnxfrm_prefix_icu_no_utf8(char *dest, const char *src, size_t srclen,
uchar = (UChar *) buf;
- ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
+ ulen = uchar_convert(iculib, icu_converter, uchar, ulen + 1, src, srclen);
uiter_setString(&iter, uchar, ulen);
state[0] = state[1] = 0; /* won't need that again */
status = U_ZERO_ERROR;
- result_bsize = ucol_nextSortKeyPart(locale->info.icu.ucol,
- &iter,
- state,
- (uint8_t *) dest,
- destsize,
- &status);
+ result_bsize = iculib->nextSortKeyPart(
+ locale->info.icu.ucol, &iter, state,
+ (uint8_t *) dest, destsize, &status);
+
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("sort key generation failed: %s",
- u_errorName(status))));
+ iculib->errorName(status))));
return result_bsize;
}
@@ -2445,6 +2517,7 @@ static size_t
pg_strnxfrm_prefix_icu(char *dest, const char *src, size_t srclen,
size_t destsize, pg_locale_t locale)
{
+ pg_icu_library *iculib = PG_ICU_LIB(locale);
size_t result;
Assert(locale->provider == COLLPROVIDER_ICU);
@@ -2455,19 +2528,17 @@ pg_strnxfrm_prefix_icu(char *dest, const char *src, size_t srclen,
uint32_t state[2];
UErrorCode status;
- uiter_setUTF8(&iter, src, srclen);
+ iculib->setUTF8(&iter, src, srclen);
state[0] = state[1] = 0; /* won't need that again */
status = U_ZERO_ERROR;
- result = ucol_nextSortKeyPart(locale->info.icu.ucol,
- &iter,
- state,
- (uint8_t *) dest,
- destsize,
- &status);
+ result = iculib->nextSortKeyPart(
+ locale->info.icu.ucol, &iter, state,
+ (uint8_t *) dest, destsize, &status);
+
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("sort key generation failed: %s",
- u_errorName(status))));
+ iculib->errorName(status))));
}
else
result = pg_strnxfrm_prefix_icu_no_utf8(dest, src, srclen, destsize,
@@ -2667,7 +2738,7 @@ pg_strnxfrm_prefix(char *dest, size_t destsize, const char *src,
#ifdef USE_ICU
static void
-init_icu_converter(void)
+init_icu_converter(pg_icu_library *iculib)
{
const char *icu_encoding_name;
UErrorCode status;
@@ -2684,11 +2755,11 @@ init_icu_converter(void)
pg_encoding_to_char(GetDatabaseEncoding()))));
status = U_ZERO_ERROR;
- conv = ucnv_open(icu_encoding_name, &status);
+ conv = iculib->openConverter(icu_encoding_name, &status);
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("could not open ICU converter for encoding \"%s\": %s",
- icu_encoding_name, u_errorName(status))));
+ icu_encoding_name, iculib->errorName(status))));
icu_converter = conv;
}
@@ -2697,14 +2768,15 @@ init_icu_converter(void)
* Find length, in UChars, of given string if converted to UChar string.
*/
static size_t
-uchar_length(UConverter *converter, const char *str, size_t len)
+uchar_length(pg_icu_library *iculib, UConverter *converter, const char *str, size_t len)
{
UErrorCode status = U_ZERO_ERROR;
int32_t ulen;
- ulen = ucnv_toUChars(converter, NULL, 0, str, len, &status);
+ ulen = iculib->toUChars(converter, NULL, 0, str, len, &status);
if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
ereport(ERROR,
- (errmsg("%s failed: %s", "ucnv_toUChars", u_errorName(status))));
+ (errmsg("%s failed: %s", "ucnv_toUChars",
+ iculib->errorName(status))));
return ulen;
}
@@ -2713,16 +2785,17 @@ uchar_length(UConverter *converter, const char *str, size_t len)
* return the length (in UChars).
*/
static int32_t
-uchar_convert(UConverter *converter, UChar *dest, int32_t destlen,
- const char *src, size_t srclen)
+uchar_convert(pg_icu_library *iculib, UConverter *converter, UChar *dest,
+ int32_t destlen, const char *src, size_t srclen)
{
UErrorCode status = U_ZERO_ERROR;
int32_t ulen;
status = U_ZERO_ERROR;
- ulen = ucnv_toUChars(converter, dest, destlen, src, srclen, &status);
+ ulen = iculib->toUChars(converter, dest, destlen, src, srclen, &status);
if (U_FAILURE(status))
ereport(ERROR,
- (errmsg("%s failed: %s", "ucnv_toUChars", u_errorName(status))));
+ (errmsg("%s failed: %s", "ucnv_toUChars",
+ iculib->errorName(status))));
return ulen;
}
@@ -2739,16 +2812,17 @@ uchar_convert(UConverter *converter, UChar *dest, int32_t destlen,
* result length instead.
*/
int32_t
-icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
+icu_to_uchar(pg_icu_library *iculib, UChar **buff_uchar, const char *buff,
+ size_t nbytes)
{
int32_t len_uchar;
- init_icu_converter();
+ init_icu_converter(iculib);
- len_uchar = uchar_length(icu_converter, buff, nbytes);
+ len_uchar = uchar_length(iculib, icu_converter, buff, nbytes);
*buff_uchar = palloc((len_uchar + 1) * sizeof(**buff_uchar));
- len_uchar = uchar_convert(icu_converter,
+ len_uchar = uchar_convert(iculib, icu_converter,
*buff_uchar, len_uchar + 1, buff, nbytes);
return len_uchar;
@@ -2766,30 +2840,32 @@ icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
* The result string is nul-terminated.
*/
int32_t
-icu_from_uchar(char **result, const UChar *buff_uchar, int32_t len_uchar)
+icu_from_uchar(pg_icu_library *iculib, char **result, const UChar *buff_uchar,
+ int32_t len_uchar)
{
UErrorCode status;
int32_t len_result;
- init_icu_converter();
+ init_icu_converter(iculib);
status = U_ZERO_ERROR;
- len_result = ucnv_fromUChars(icu_converter, NULL, 0,
- buff_uchar, len_uchar, &status);
+ len_result = iculib->fromUChars(icu_converter, NULL, 0,
+ buff_uchar, len_uchar, &status);
if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
ereport(ERROR,
(errmsg("%s failed: %s", "ucnv_fromUChars",
- u_errorName(status))));
+ iculib->errorName(status))));
*result = palloc(len_result + 1);
status = U_ZERO_ERROR;
- len_result = ucnv_fromUChars(icu_converter, *result, len_result + 1,
- buff_uchar, len_uchar, &status);
+ len_result = iculib->fromUChars(icu_converter, *result,
+ len_result + 1, buff_uchar,
+ len_uchar, &status);
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("%s failed: %s", "ucnv_fromUChars",
- u_errorName(status))));
+ iculib->errorName(status))));
return len_result;
}
@@ -2805,7 +2881,8 @@ icu_from_uchar(char **result, const UChar *buff_uchar, int32_t len_uchar)
*/
pg_attribute_unused()
static void
-icu_set_collation_attributes(UCollator *collator, const char *loc)
+icu_set_collation_attributes(pg_icu_library *iculib, UCollator *collator,
+ const char *loc)
{
char *str = asc_tolower(loc, strlen(loc));
@@ -2879,7 +2956,7 @@ icu_set_collation_attributes(UCollator *collator, const char *loc)
status = U_ILLEGAL_ARGUMENT_ERROR;
if (status == U_ZERO_ERROR)
- ucol_setAttribute(collator, uattr, uvalue, &status);
+ iculib->setAttribute(collator, uattr, uvalue, &status);
/*
* Pretend the error came from ucol_open(), for consistent error
@@ -2888,7 +2965,7 @@ icu_set_collation_attributes(UCollator *collator, const char *loc)
if (U_FAILURE(status))
ereport(ERROR,
(errmsg("could not open collator for locale \"%s\": %s",
- loc, u_errorName(status))));
+ loc, iculib->errorName(status))));
}
}
}
diff --git a/src/include/utils/pg_locale_internal.h b/src/include/utils/pg_locale_internal.h
index 33465ad92d..54445d8b87 100644
--- a/src/include/utils/pg_locale_internal.h
+++ b/src/include/utils/pg_locale_internal.h
@@ -14,6 +14,8 @@
#define _PG_LOCALE_INTERNAL_
#ifdef USE_ICU
+#include <unicode/ubrk.h>
+#include <unicode/ucnv.h>
#include <unicode/ucol.h>
#endif
@@ -30,6 +32,104 @@
#endif
#endif
+#ifdef USE_ICU
+/*
+ * An ICU library version that we're either linked against or have loaded at
+ * runtime.
+ */
+typedef struct pg_icu_library
+{
+ int major_version;
+ int minor_version;
+ void (*getICUVersion) (UVersionInfo info);
+ void (*getUnicodeVersion) (UVersionInfo into);
+ void (*getCLDRVersion) (UVersionInfo info, UErrorCode *status);
+ UCollator *(*openCollator) (const char *loc, UErrorCode *status);
+ void (*closeCollator) (UCollator *coll);
+ void (*getCollatorVersion) (const UCollator *coll, UVersionInfo info);
+ void (*getUCAVersion) (const UCollator *coll, UVersionInfo info);
+ void (*versionToString) (const UVersionInfo versionArray,
+ char *versionString);
+ UCollationResult (*strcoll) (const UCollator *coll,
+ const UChar *source,
+ int32_t sourceLength,
+ const UChar *target,
+ int32_t targetLength);
+ UCollationResult (*strcollUTF8) (const UCollator *coll,
+ const char *source,
+ int32_t sourceLength,
+ const char *target,
+ int32_t targetLength,
+ UErrorCode *status);
+ int32_t (*getSortKey) (const UCollator *coll,
+ const UChar *source,
+ int32_t sourceLength,
+ uint8_t *result,
+ int32_t resultLength);
+ int32_t (*nextSortKeyPart) (const UCollator *coll,
+ UCharIterator *iter,
+ uint32_t state[2],
+ uint8_t *dest,
+ int32_t count,
+ UErrorCode *status);
+ void (*setUTF8) (UCharIterator *iter,
+ const char *s,
+ int32_t length);
+ const char *(*errorName) (UErrorCode code);
+ int32_t (*strToUpper) (UChar *dest,
+ int32_t destCapacity,
+ const UChar *src,
+ int32_t srcLength,
+ const char *locale,
+ UErrorCode *pErrorCode);
+ int32_t (*strToLower) (UChar *dest,
+ int32_t destCapacity,
+ const UChar *src,
+ int32_t srcLength,
+ const char *locale,
+ UErrorCode *pErrorCode);
+ int32_t (*strToTitle) (UChar *dest,
+ int32_t destCapacity,
+ const UChar *src,
+ int32_t srcLength,
+ UBreakIterator *titleIter,
+ const char *locale,
+ UErrorCode *pErrorCode);
+ void (*setAttribute) (UCollator *coll,
+ UColAttribute attr,
+ UColAttributeValue value,
+ UErrorCode *status);
+ UConverter *(*openConverter) (const char *converterName,
+ UErrorCode * err);
+ void (*closeConverter) (UConverter *converter);
+ int32_t (*fromUChars) (UConverter *cnv,
+ char *dest,
+ int32_t destCapacity,
+ const UChar *src,
+ int32_t srcLength,
+ UErrorCode *pErrorCode);
+ int32_t (*toUChars) (UConverter *cnv,
+ UChar *dest,
+ int32_t destCapacity,
+ const char *src,
+ int32_t srcLength,
+ UErrorCode *pErrorCode);
+ int32_t (*toLanguageTag) (const char *localeID,
+ char *langtag,
+ int32_t langtagCapacity,
+ UBool strict,
+ UErrorCode *err);
+ int32_t (*getDisplayName) (const char *localeID,
+ const char *inLocaleID,
+ UChar *result,
+ int32_t maxResultSize,
+ UErrorCode *err);
+ int32_t (*countAvailable) (void);
+ const char *(*getAvailable) (int32_t n);
+} pg_icu_library;
+
+#endif
+
/*
* We define our own wrapper around locale_t so we can keep the same
* function signatures for all builds, while not having to create a
@@ -53,7 +153,8 @@ struct pg_locale_struct
#ifdef USE_ICU
struct
{
- UCollator *ucol;
+ UCollator *ucol;
+ pg_icu_library *lib;
} icu;
#endif
int dummy; /* in case we have neither LOCALE_T nor ICU */
@@ -61,8 +162,14 @@ struct pg_locale_struct
};
#ifdef USE_ICU
-extern int32_t icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes);
-extern int32_t icu_from_uchar(char **result, const UChar *buff_uchar, int32_t len_uchar);
+#define PG_ICU_LIB(x) ((x)->info.icu.lib)
+#define PG_ICU_COL(x) ((x)->info.icu.ucol)
+
+extern pg_icu_library *get_builtin_icu_library(void);
+extern int32_t icu_to_uchar(pg_icu_library *lib, UChar **buff_uchar,
+ const char *buff, size_t nbytes);
+extern int32_t icu_from_uchar(pg_icu_library *lib, char **result,
+ const UChar *buff_uchar, int32_t len_uchar);
#endif
#endif /* _PG_LOCALE_INTERNAL_ */
--
2.34.1