On 2026-Aug-11, Heikki Linnakangas wrote: > On 11/08/2026 12:19, Heikki Linnakangas wrote: > > I think the best fix is to make pg_strupper() in REL_18_STABLE also work > > with the C locale. It's an accident waiting to happen if it doesn't. > > (And same for all the other pg_str*() functions, of course) > > Like the attached.
Hm, this looks really similar to what I wrote (attached here for the curious), except you chose a different layer (directly in pg_locale.c, whereas I put mine under the libc implementation). Probably yours is the better choice since it also covers the builtin provider. > There's some code duplication: the strupper_c() function is essentially the > same as asc_toupper(), and str_toupper() wouldn't really need to have the > special case for C locale anymore, it could just rely on pg_strupper() now. > But that's so in 'master' too, so I think cleaning that up should be left > for a separate patch. Agreed. It's unclear to me how to get the strtitle() thing called, since the only caller seems to be str_initcap() which will use asc_initcap anyway. Maybe such a cleanup should remove some part of this code. -- Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
>From e6df99c6ba63216297630a3b7057fb5ac1735db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <[email protected]> Date: Tue, 11 Aug 2026 12:11:35 +0200 Subject: [PATCH] Hardcode str{lower,upper,title}() for the C locale This avoids a crash when those functions are called directly rather than via str_to{lower,upper,title} directly. --- src/backend/utils/adt/pg_locale_libc.c | 64 +++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c index 31274f069d2..afacb727efe 100644 --- a/src/backend/utils/adt/pg_locale_libc.c +++ b/src/backend/utils/adt/pg_locale_libc.c @@ -66,18 +66,24 @@ static int strncoll_libc_win32_utf8(const char *arg1, ssize_t len1, pg_locale_t locale); #endif +static size_t strlower_libc_c(char *dest, size_t dstsize, + const char *src, ssize_t srclen); static size_t strlower_libc_sb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale); static size_t strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale); +static size_t strtitle_libc_c(char *dest, size_t dstsize, + const char *src, ssize_t srclen); static size_t strtitle_libc_sb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale); static size_t strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale); +static size_t strupper_libc_c(char *dest, size_t dstsize, + const char *src, ssize_t srclen); static size_t strupper_libc_sb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale); @@ -123,7 +129,9 @@ size_t strlower_libc(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale) { - if (pg_database_encoding_max_length() > 1) + if (locale->ctype_is_c) + return strlower_libc_c(dst, dstsize, src, srclen); + else if (pg_database_encoding_max_length() > 1) return strlower_libc_mb(dst, dstsize, src, srclen, locale); else return strlower_libc_sb(dst, dstsize, src, srclen, locale); @@ -133,6 +141,8 @@ size_t strtitle_libc(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale) { + if (locale->ctype_is_c) + return strtitle_libc_c(dst, dstsize, src, srclen); if (pg_database_encoding_max_length() > 1) return strtitle_libc_mb(dst, dstsize, src, srclen, locale); else @@ -143,12 +153,26 @@ size_t strupper_libc(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale) { - if (pg_database_encoding_max_length() > 1) + if (locale->ctype_is_c) + return strupper_libc_c(dst, dstsize, src, srclen); + else if (pg_database_encoding_max_length() > 1) return strupper_libc_mb(dst, dstsize, src, srclen, locale); else return strupper_libc_sb(dst, dstsize, src, srclen, locale); } +static size_t +strlower_libc_c(char *dest, size_t dstsize, const char *src, ssize_t srclen) +{ + int i; + + for (i = 0; i < srclen && i < dstsize; i++) + dest[i] = pg_ascii_tolower(src[i]); + if (i < dstsize) + dest[i] = '\0'; + return srclen; +} + static size_t strlower_libc_sb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale) @@ -280,6 +304,30 @@ strtitle_libc_sb(char *dest, size_t destsize, const char *src, ssize_t srclen, return srclen; } +static size_t +strtitle_libc_c(char *dest, size_t dstsize, const char *src, ssize_t srclen) +{ + bool wasalnum = false; + int i; + + for (i = 0; i < srclen && i < dstsize; i++) + { + char c = src[i]; + + if (wasalnum) + dest[i] = pg_ascii_tolower(c); + else + dest[i] = pg_ascii_toupper(c); + + wasalnum = ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z')); + } + if (i < dstsize) + dest[i] = '\0'; + return srclen; +} + static size_t strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale) @@ -335,6 +383,18 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen, return result_size; } +static size_t +strupper_libc_c(char *dest, size_t dstsize, const char *src, ssize_t srclen) +{ + int i; + + for (i = 0; i < srclen && i < dstsize; i++) + dest[i] = pg_ascii_toupper(src[i]); + if (i < dstsize) + dest[i] = '\0'; + return srclen; +} + static size_t strupper_libc_sb(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale) -- 2.47.3
