1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Fix pg_iswcased().

Commits 630706ced0 and 9c8de15969 lost some encoding-specific nuances:
in libc with EUC encodings, or in ICU with any non-UTF8 encoding, we
cannot easily classify characters, so we must assume they are
case-varying. Restore these behaviors.

Previously, ILIKE using an index scan could cause wrong results.

Reported-by: Noah Misch <noah@leadboat.com>
Discussion: https://postgr.es/m/20260826221526.89.noahmisch@microsoft.com
Backpatch-through: 19
This commit is contained in:
Jeff Davis
2026-09-09 20:39:53 -07:00
parent e418e8eb92
commit b264391a84
3 changed files with 52 additions and 5 deletions

View File

@@ -1743,6 +1743,11 @@ pg_iswxdigit(pg_wchar wc, pg_locale_t locale)
return locale->ctype->wc_isxdigit(wc, locale);
}
/*
* Is the character potentially case-varying? Used by ILIKE to extract a
* prefix suitable for an index search. Safe to return true if the character
* can't be easily classified.
*/
bool
pg_iswcased(pg_wchar wc, pg_locale_t locale)
{

View File

@@ -247,6 +247,20 @@ wc_isxdigit_icu(pg_wchar wc, pg_locale_t locale)
static bool
wc_iscased_icu(pg_wchar wc, pg_locale_t locale)
{
/*
* For non-UTF8 encodings (single or multibyte), pg_wchar may not be a
* codepoint, so we conservatively assume that any non-ASCII character
* could be case-varying.
*/
if (wc > (pg_wchar) 127)
return true;
return u_hasBinaryProperty(wc, UCHAR_CASED);
}
static bool
wc_iscased_icu_utf8(pg_wchar wc, pg_locale_t locale)
{
return u_hasBinaryProperty(wc, UCHAR_CASED);
}
@@ -289,7 +303,7 @@ static const struct ctype_methods ctype_methods_icu_utf8 = {
.wc_ispunct = wc_ispunct_icu,
.wc_isspace = wc_isspace_icu,
.wc_isxdigit = wc_isxdigit_icu,
.wc_iscased = wc_iscased_icu,
.wc_iscased = wc_iscased_icu_utf8,
.wc_toupper = toupper_icu,
.wc_tolower = tolower_icu,
};

View File

@@ -299,10 +299,37 @@ wc_isxdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
}
static bool
wc_iscased_libc_mb(pg_wchar wc, pg_locale_t locale)
wc_iscased_libc_other_mb(pg_wchar wc, pg_locale_t locale)
{
/*
* For non-UTF8 multibyte encodings, we conservatively assume that any
* non-ASCII character could be case-varying.
*/
if (wc > (pg_wchar) 127)
return true;
/* ASCII: pass directly to isupper_l()/islower_l() */
return isupper_l((unsigned char) wc, locale->lt) ||
islower_l((unsigned char) wc, locale->lt);
}
static bool
wc_iscased_libc_utf8(pg_wchar wc, pg_locale_t locale)
{
/*
* If sizeof(wchar_t) < 4 (that is, on Windows), then return false. This
* is consistent with the behavior of strlower_libc_mb(): the UTF8 string
* will be decoded into 16-bit wchar_t, so strlower_libc_mb() will never
* deal with codepoints beyond 0xFFFF. It may deal with surrogate pairs,
* but those characters map to themselves anyway.
*/
if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
return false;
/*
* For UTF8, pg_wchar is a codepoint and we assume we can pass it directly
* to iswupper_l()/iswlower_l().
*/
return iswupper_l((wint_t) wc, locale->lt) ||
iswlower_l((wint_t) wc, locale->lt);
}
@@ -415,7 +442,8 @@ static const struct ctype_methods ctype_methods_libc_sb = {
/*
* Non-UTF8 multibyte encodings use multibyte semantics for case mapping, but
* single-byte semantics for pattern matching.
* single-byte semantics for pattern matching (except wc_iscased which needs
* to be consistent with case mapping).
*/
static const struct ctype_methods ctype_methods_libc_other_mb = {
.strlower = strlower_libc_mb,
@@ -435,7 +463,7 @@ static const struct ctype_methods ctype_methods_libc_other_mb = {
.wc_ispunct = wc_ispunct_libc_sb,
.wc_isspace = wc_isspace_libc_sb,
.wc_isxdigit = wc_isxdigit_libc_sb,
.wc_iscased = wc_iscased_libc_sb,
.wc_iscased = wc_iscased_libc_other_mb,
.wc_toupper = toupper_libc_sb,
.wc_tolower = tolower_libc_sb,
};
@@ -458,7 +486,7 @@ static const struct ctype_methods ctype_methods_libc_utf8 = {
.wc_ispunct = wc_ispunct_libc_mb,
.wc_isspace = wc_isspace_libc_mb,
.wc_isxdigit = wc_isxdigit_libc_mb,
.wc_iscased = wc_iscased_libc_mb,
.wc_iscased = wc_iscased_libc_utf8,
.wc_toupper = toupper_libc_mb,
.wc_tolower = tolower_libc_mb,
};