mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix pg_iswcased().
Commits630706ced0and9c8de15969lost 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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user