v6-0005-Avoid-global-LC_CTYPE-dependency-in-pg_locale_icu.patch
text/x-patch
Filename: v6-0005-Avoid-global-LC_CTYPE-dependency-in-pg_locale_icu.patch
Type: text/x-patch
Part: 4
From af958c9318ade598d74ea1e7ae720c287c83dee0 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Sun, 26 Oct 2025 15:12:38 -0700
Subject: [PATCH v6 5/9] Avoid global LC_CTYPE dependency in pg_locale_icu.c.
ICU still depends on libc for compatibility with certain historical
behavior for single-byte encodings. Make the dependency explicit by
holding a locale_t object in the pg_locale_t object, so that at least
it does not depend on the global LC_CTYPE setting.
---
src/backend/utils/adt/pg_locale_icu.c | 66 ++++++++++++++++++++++-----
src/include/utils/pg_locale.h | 1 +
2 files changed, 56 insertions(+), 11 deletions(-)
diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c
index 449e3bbb7a6..da250a23630 100644
--- a/src/backend/utils/adt/pg_locale_icu.c
+++ b/src/backend/utils/adt/pg_locale_icu.c
@@ -121,25 +121,34 @@ static int32_t u_strFoldCase_default(UChar *dest, int32_t destCapacity,
const char *locale,
UErrorCode *pErrorCode);
-/*
- * ICU still depends on libc for compatibility with certain historical
- * behavior for single-byte encodings. XXX: consider fixing by decoding the
- * single byte into a code point, and using u_tolower().
- */
static char
char_tolower_icu(unsigned char ch, pg_locale_t locale)
{
- if (isupper(ch))
- return tolower(ch);
- return ch;
+ locale_t loc = locale->icu.lt;
+
+ if (loc)
+ {
+ if (isupper_l(ch, loc))
+ return tolower_l(ch, loc);
+ return ch;
+ }
+ else
+ return pg_ascii_tolower(ch);
}
static char
char_toupper_icu(unsigned char ch, pg_locale_t locale)
{
- if (islower(ch))
- return toupper(ch);
- return ch;
+ locale_t loc = locale->icu.lt;
+
+ if (loc)
+ {
+ if (islower_l(ch, loc))
+ return toupper_l(ch, loc);
+ return ch;
+ }
+ else
+ return pg_ascii_toupper(ch);
}
static bool
@@ -265,6 +274,29 @@ static const struct ctype_methods ctype_methods_icu = {
.wc_toupper = toupper_icu,
.wc_tolower = tolower_icu,
};
+
+/*
+ * ICU still depends on libc for compatibility with certain historical
+ * behavior for single-byte encodings. See char_tolower_libc().
+ *
+ * XXX: consider fixing by decoding the single byte into a code point, and
+ * using u_tolower().
+ */
+static locale_t
+make_libc_ctype_locale(const char *ctype)
+{
+ locale_t loc;
+
+#ifndef WIN32
+ loc = newlocale(LC_CTYPE_MASK, ctype, NULL);
+#else
+ loc = _create_locale(LC_ALL, ctype);
+#endif
+ if (!loc)
+ report_newlocale_failure(ctype);
+
+ return loc;
+}
#endif
pg_locale_t
@@ -275,11 +307,13 @@ create_pg_locale_icu(Oid collid, MemoryContext context)
const char *iculocstr;
const char *icurules = NULL;
UCollator *collator;
+ locale_t loc = (locale_t) 0;
pg_locale_t result;
if (collid == DEFAULT_COLLATION_OID)
{
HeapTuple tp;
+ const char *ctype;
Datum datum;
bool isnull;
@@ -297,6 +331,15 @@ create_pg_locale_icu(Oid collid, MemoryContext context)
if (!isnull)
icurules = TextDatumGetCString(datum);
+ if (pg_database_encoding_max_length() == 1)
+ {
+ datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+ Anum_pg_database_datctype);
+ ctype = TextDatumGetCString(datum);
+
+ loc = make_libc_ctype_locale(ctype);
+ }
+
ReleaseSysCache(tp);
}
else
@@ -327,6 +370,7 @@ create_pg_locale_icu(Oid collid, MemoryContext context)
result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
result->icu.locale = MemoryContextStrdup(context, iculocstr);
result->icu.ucol = collator;
+ result->icu.lt = loc;
result->deterministic = deterministic;
result->collate_is_c = false;
result->ctype_is_c = false;
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 790db566e91..c5978d903cc 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -165,6 +165,7 @@ struct pg_locale_struct
{
const char *locale;
UCollator *ucol;
+ locale_t lt;
} icu;
#endif
};
--
2.43.0