From 8526ae1884e7d36ab9d0da06a1fd16b9ea0f7206 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Mon, 12 Jan 2026 09:04:03 -0800
Subject: [PATCH v1 2/4] citext: use CASEFOLD() rather than LOWER().

CASEFOLD() is better for case-insensitive matching in edge cases.

Existing citext indexes require REINDEX.
---
 contrib/citext/citext.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/contrib/citext/citext.c b/contrib/citext/citext.c
index a15ce5db829..02cad8c4ac3 100644
--- a/contrib/citext/citext.c
+++ b/contrib/citext/citext.c
@@ -43,15 +43,15 @@ citextcmp(text *left, text *right, Oid collid)
 	int32		result;
 
 	/*
-	 * We must do our str_tolower calls with DEFAULT_COLLATION_OID, not the
+	 * We must do our str_casefold calls with DEFAULT_COLLATION_OID, not the
 	 * input collation as you might expect.  This is so that the behavior of
 	 * citext's equality and hashing functions is not collation-dependent.  We
 	 * should change this once the core infrastructure is able to cope with
 	 * collation-dependent equality and hashing functions.
 	 */
 
-	lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
-	rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
+	lcstr = str_casefold(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
+	rcstr = str_casefold(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
 
 	result = varstr_cmp(lcstr, strlen(lcstr),
 						rcstr, strlen(rcstr),
@@ -77,8 +77,8 @@ internal_citext_pattern_cmp(text *left, text *right, Oid collid)
 				rlen;
 	int32		result;
 
-	lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
-	rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
+	lcstr = str_casefold(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
+	rcstr = str_casefold(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
 
 	llen = strlen(lcstr);
 	rlen = strlen(rcstr);
@@ -147,7 +147,7 @@ citext_hash(PG_FUNCTION_ARGS)
 	char	   *str;
 	Datum		result;
 
-	str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
+	str = str_casefold(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
 	result = hash_any((unsigned char *) str, strlen(str));
 	pfree(str);
 
@@ -167,7 +167,7 @@ citext_hash_extended(PG_FUNCTION_ARGS)
 	char	   *str;
 	Datum		result;
 
-	str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
+	str = str_casefold(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
 	result = hash_any_extended((unsigned char *) str, strlen(str), seed);
 	pfree(str);
 
@@ -196,8 +196,8 @@ citext_eq(PG_FUNCTION_ARGS)
 
 	/* We can't compare lengths in advance of downcasing ... */
 
-	lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
-	rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
+	lcstr = str_casefold(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
+	rcstr = str_casefold(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
 
 	/*
 	 * Since we only care about equality or not-equality, we can avoid all the
@@ -226,8 +226,8 @@ citext_ne(PG_FUNCTION_ARGS)
 
 	/* We can't compare lengths in advance of downcasing ... */
 
-	lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
-	rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
+	lcstr = str_casefold(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
+	rcstr = str_casefold(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
 
 	/*
 	 * Since we only care about equality or not-equality, we can avoid all the
-- 
2.43.0

