From 51ee12ca009678a5eac5a4b4e94d0eddb3531c9f Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 21 Jan 2026 10:26:07 -0800
Subject: [PATCH v1] Fix crash introduced by incorrect backport 806555e300.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Commit 7f007e4a04 in master depends on 1476028225, but the latter was
not backported. Therefore 806555e300 (the backport of commit
7f007e4a04) incorrectly used pg_strfold() in a locale where
ctype_is_c.

The fix is to simply have the callers check for ctype_is_c.

Because 7f007e4a04 was only backported to version 18, and because the
commit in master is fine, this fix only exists in version 18.

Reported-by: Александр Кожемякин <a.kozhemyakin@postgrespro.ru>
Discussion: https://postgr.es/m/456f7143-51ea-4342-b4a1-85f0d9b6c79f@postgrespro.ru
---
 contrib/ltree/crc32.c     | 29 ++++++++++++++++++++---------
 contrib/ltree/lquery_op.c | 21 +++++++++++++++++++--
 2 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/contrib/ltree/crc32.c b/contrib/ltree/crc32.c
index ce1b0f28e21..0aa6e08c61e 100644
--- a/contrib/ltree/crc32.c
+++ b/contrib/ltree/crc32.c
@@ -32,18 +32,29 @@ ltree_crc32_sz(const char *buf, int size)
 	INIT_TRADITIONAL_CRC32(crc);
 	while (size > 0)
 	{
-		char		foldstr[UNICODE_CASEMAP_BUFSZ];
-		int			srclen = pg_mblen(p);
-		size_t		foldlen;
+		if (locale->ctype_is_c)
+		{
+			char		c = pg_ascii_tolower(*p);
 
-		/* fold one codepoint at a time */
-		foldlen = pg_strfold(foldstr, UNICODE_CASEMAP_BUFSZ, p, srclen,
-							 locale);
+			COMP_TRADITIONAL_CRC32(crc, &c, 1);
+			size--;
+			p++;
+		}
+		else
+		{
+			char		foldstr[UNICODE_CASEMAP_BUFSZ];
+			int			srclen = pg_mblen(p);
+			size_t		foldlen;
 
-		COMP_TRADITIONAL_CRC32(crc, foldstr, foldlen);
+			/* fold one codepoint at a time */
+			foldlen = pg_strfold(foldstr, UNICODE_CASEMAP_BUFSZ, p, srclen,
+								 locale);
 
-		size -= srclen;
-		p += srclen;
+			COMP_TRADITIONAL_CRC32(crc, foldstr, foldlen);
+
+			size -= srclen;
+			p += srclen;
+		}
 	}
 	FIN_TRADITIONAL_CRC32(crc);
 	return (unsigned int) crc;
diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c
index 9b1de101213..f4a507a27c7 100644
--- a/contrib/ltree/lquery_op.c
+++ b/contrib/ltree/lquery_op.c
@@ -96,15 +96,32 @@ ltree_prefix_eq_ci(const char *a, size_t a_sz, const char *b, size_t b_sz)
 	static pg_locale_t locale = NULL;
 	size_t		al_sz = a_sz + 1;
 	size_t		al_len;
-	char	   *al = palloc(al_sz);
+	char	   *al;
 	size_t		bl_sz = b_sz + 1;
 	size_t		bl_len;
-	char	   *bl = palloc(bl_sz);
+	char	   *bl;
 	bool		res;
 
 	if (!locale)
 		locale = pg_newlocale_from_collation(DEFAULT_COLLATION_OID);
 
+	if (locale->ctype_is_c)
+	{
+		if (a_sz > b_sz)
+			return false;
+
+		for (int i = 0; i < a_sz; i++)
+		{
+			if (pg_ascii_tolower(a[i]) != pg_ascii_tolower(b[i]))
+				return false;
+		}
+
+		return true;
+	}
+
+	al = palloc(al_sz);
+	bl = palloc(bl_sz);
+
 	/* casefold both a and b */
 
 	al_len = pg_strfold(al, al_sz, a, a_sz, locale);
-- 
2.43.0

