v2-0001-ILIKE-use-CASEFOLD-rather-than-LOWER.patch
text/x-patch
Filename: v2-0001-ILIKE-use-CASEFOLD-rather-than-LOWER.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0001
Subject: ILIKE: use CASEFOLD() rather than LOWER().
| File | + | − |
|---|---|---|
| src/backend/utils/adt/formatting.c | 0 | 5 |
| src/backend/utils/adt/like.c | 6 | 11 |
| src/backend/utils/adt/oracle_compat.c | 5 | 0 |
| src/test/regress/expected/collate.utf8.out | 24 | 0 |
| src/test/regress/sql/collate.utf8.sql | 6 | 0 |
From a5a1cd1c3cd5a3d7f00b11672052fd9087ea35b0 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Mon, 12 Jan 2026 08:58:43 -0800
Subject: [PATCH v2 1/3] ILIKE: use CASEFOLD() rather than LOWER().
For non-C locales, we casefold the entire string before performing
pattern matching with ILIKE. Previously, casefolding was done with the
LOWER() function; now that a proper CASEFOLD() function exists, use
that instead.
CASEFOLD() is better than LOWER() for case-insensitive comparisons in
builtin and ICU locales. For instance, CASEFOLD() transforms a GREEK
SMALL LETTER FINAL SIGMA (U+03C2) into GREEK SMALL LETTER SIGMA
(U+03C3) so that the two characters match in a case-insensitive
comparison; whereas LOWER() does not transform it because it's already
lowercase, so they will not match.
---
src/backend/utils/adt/formatting.c | 5 -----
src/backend/utils/adt/like.c | 17 ++++++---------
src/backend/utils/adt/oracle_compat.c | 5 +++++
src/test/regress/expected/collate.utf8.out | 24 ++++++++++++++++++++++
src/test/regress/sql/collate.utf8.sql | 6 ++++++
5 files changed, 41 insertions(+), 16 deletions(-)
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 0716aff22b6..6d0fd26771a 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1829,11 +1829,6 @@ str_casefold(const char *buff, size_t nbytes, Oid collid)
errhint("Use the COLLATE clause to set the collation explicitly.")));
}
- if (GetDatabaseEncoding() != PG_UTF8)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("Unicode case folding can only be performed if server encoding is UTF8")));
-
mylocale = pg_newlocale_from_collation(collid);
/* C/POSIX collations use this path regardless of database encoding */
diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c
index 350bc07f210..8fd67705f1b 100644
--- a/src/backend/utils/adt/like.c
+++ b/src/backend/utils/adt/like.c
@@ -23,6 +23,7 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/fmgrprotos.h"
+#include "utils/formatting.h"
#include "utils/pg_locale.h"
#include "varatt.h"
@@ -190,10 +191,8 @@ Generic_Text_IC_like(text *str, text *pat, Oid collation)
errmsg("nondeterministic collations are not supported for ILIKE")));
/*
- * For efficiency reasons, in the C locale we don't call lower() on the
+ * For efficiency reasons, in the C locale we don't call casefold() on the
* pattern and text, but instead lowercase each character lazily.
- *
- * XXX: use casefolding instead?
*/
if (locale->ctype_is_c)
@@ -206,14 +205,10 @@ Generic_Text_IC_like(text *str, text *pat, Oid collation)
}
else
{
- pat = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
- PointerGetDatum(pat)));
- p = VARDATA_ANY(pat);
- plen = VARSIZE_ANY_EXHDR(pat);
- str = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
- PointerGetDatum(str)));
- s = VARDATA_ANY(str);
- slen = VARSIZE_ANY_EXHDR(str);
+ p = str_casefold(VARDATA_ANY(pat), VARSIZE_ANY_EXHDR(pat), collation);
+ plen = strlen(p);
+ s = str_casefold(VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str), collation);
+ slen = strlen(s);
if (GetDatabaseEncoding() == PG_UTF8)
return UTF8_MatchText(s, slen, p, plen, 0);
diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c
index 5b0d098bd07..855769d5776 100644
--- a/src/backend/utils/adt/oracle_compat.c
+++ b/src/backend/utils/adt/oracle_compat.c
@@ -133,6 +133,11 @@ casefold(PG_FUNCTION_ARGS)
char *out_string;
text *result;
+ if (GetDatabaseEncoding() != PG_UTF8)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("Unicode case folding can only be performed if server encoding is UTF8")));
+
out_string = str_casefold(VARDATA_ANY(in_string),
VARSIZE_ANY_EXHDR(in_string),
PG_GET_COLLATION());
diff --git a/src/test/regress/expected/collate.utf8.out b/src/test/regress/expected/collate.utf8.out
index 0c3ab5c89b2..3d4292611e2 100644
--- a/src/test/regress/expected/collate.utf8.out
+++ b/src/test/regress/expected/collate.utf8.out
@@ -169,6 +169,18 @@ select casefold('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' collate PG_C_UTF8);
abcd 123 #$% ıiiİ ß ß dždždž σσσ
(1 row)
+SELECT U&'ς' ILIKE U&'σ' COLLATE PG_C_UTF8;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT U&'straße' ILIKE U&'STRASSE' COLLATE PG_C_UTF8;
+ ?column?
+----------
+ f
+(1 row)
+
--
-- Test PG_UNICODE_FAST
--
@@ -338,3 +350,15 @@ select casefold('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' collate PG_UNICODE_FA
abcd 123 #$% ıiii̇ ss ss dždždž σσσ
(1 row)
+SELECT U&'ς' ILIKE U&'σ' COLLATE PG_UNICODE_FAST;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT U&'straße' ILIKE U&'STRASSE' COLLATE PG_UNICODE_FAST;
+ ?column?
+----------
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/collate.utf8.sql b/src/test/regress/sql/collate.utf8.sql
index d6d14220ab3..4a5e519cf07 100644
--- a/src/test/regress/sql/collate.utf8.sql
+++ b/src/test/regress/sql/collate.utf8.sql
@@ -85,6 +85,9 @@ SELECT 'δ' ~* '[Γ-Λ]' COLLATE PG_C_UTF8; -- same as above with cases reversed
-- case folding
select casefold('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' collate PG_C_UTF8);
+SELECT U&'ς' ILIKE U&'σ' COLLATE PG_C_UTF8;
+SELECT U&'straße' ILIKE U&'STRASSE' COLLATE PG_C_UTF8;
+
--
-- Test PG_UNICODE_FAST
--
@@ -148,3 +151,6 @@ SELECT 'δ' ~* '[Γ-Λ]' COLLATE PG_UNICODE_FAST; -- same as above with cases re
-- case folding
select casefold('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' collate PG_UNICODE_FAST);
+
+SELECT U&'ς' ILIKE U&'σ' COLLATE PG_UNICODE_FAST;
+SELECT U&'straße' ILIKE U&'STRASSE' COLLATE PG_UNICODE_FAST;
--
2.43.0