Subject: [PATCH] Hardcode str{lower,upper,title}() for the C locale

From: Álvaro Herrera <[email-masked]>
Date: Tue, 11 Aug 2026 12:11:35 +0200

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

--bynzpkhnelsy7sdt--