Add pg_iswcased().
authorJeff Davis <jdavis@postgresql.org>
Wed, 10 Dec 2025 19:56:11 +0000 (11:56 -0800)
committerJeff Davis <jdavis@postgresql.org>
Wed, 10 Dec 2025 19:56:11 +0000 (11:56 -0800)
True if character has multiple case forms. Will be a useful
multibyte-aware replacement for char_is_cased().

Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/450ceb6260cad30d7afdf155d991a9caafee7c0d.camel@j-davis.com

src/backend/utils/adt/pg_locale.c
src/backend/utils/adt/pg_locale_builtin.c
src/backend/utils/adt/pg_locale_icu.c
src/backend/utils/adt/pg_locale_libc.c
src/include/utils/pg_locale.h

index b00663b958521331efcd1a7027d2a35405f473ab..70933ee384360754e70373f488b716ca2e190539 100644 (file)
@@ -1588,6 +1588,17 @@ pg_iswxdigit(pg_wchar wc, pg_locale_t locale)
        return locale->ctype->wc_isxdigit(wc, locale);
 }
 
+bool
+pg_iswcased(pg_wchar wc, pg_locale_t locale)
+{
+   /* for the C locale, Cased and Alpha are equivalent */
+   if (locale->ctype == NULL)
+       return (wc <= (pg_wchar) 127 &&
+               (pg_char_properties[wc] & PG_ISALPHA));
+   else
+       return locale->ctype->wc_iscased(wc, locale);
+}
+
 pg_wchar
 pg_towupper(pg_wchar wc, pg_locale_t locale)
 {
index 1021e0d129b351d63ff28a2e19e65d260ad9fbd6..0d4c754a26732c93630ae001d364762598c05582 100644 (file)
@@ -185,6 +185,12 @@ wc_isxdigit_builtin(pg_wchar wc, pg_locale_t locale)
    return pg_u_isxdigit(to_char32(wc), !locale->builtin.casemap_full);
 }
 
+static bool
+wc_iscased_builtin(pg_wchar wc, pg_locale_t locale)
+{
+   return pg_u_prop_cased(to_char32(wc));
+}
+
 static bool
 char_is_cased_builtin(char ch, pg_locale_t locale)
 {
@@ -220,6 +226,7 @@ static const struct ctype_methods ctype_methods_builtin = {
    .wc_isspace = wc_isspace_builtin,
    .wc_isxdigit = wc_isxdigit_builtin,
    .char_is_cased = char_is_cased_builtin,
+   .wc_iscased = wc_iscased_builtin,
    .wc_tolower = wc_tolower_builtin,
    .wc_toupper = wc_toupper_builtin,
 };
index f5a0cc8fe41572e600f243675d78ce9d498248c0..e8820666b2d9f73d3cae226b3e03088c810d2910 100644 (file)
@@ -223,6 +223,12 @@ wc_isxdigit_icu(pg_wchar wc, pg_locale_t locale)
    return u_isxdigit(wc);
 }
 
+static bool
+wc_iscased_icu(pg_wchar wc, pg_locale_t locale)
+{
+   return u_hasBinaryProperty(wc, UCHAR_CASED);
+}
+
 static const struct ctype_methods ctype_methods_icu = {
    .strlower = strlower_icu,
    .strtitle = strtitle_icu,
@@ -239,6 +245,7 @@ static const struct ctype_methods ctype_methods_icu = {
    .wc_isspace = wc_isspace_icu,
    .wc_isxdigit = wc_isxdigit_icu,
    .char_is_cased = char_is_cased_icu,
+   .wc_iscased = wc_iscased_icu,
    .wc_toupper = toupper_icu,
    .wc_tolower = tolower_icu,
 };
index fa871690e0c31e7a3cb5ce52e7a510d31bf7d720..3d841f818a51aff983ce278093bf206d9e560158 100644 (file)
@@ -184,6 +184,13 @@ wc_isxdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
 #endif
 }
 
+static bool
+wc_iscased_libc_sb(pg_wchar wc, pg_locale_t locale)
+{
+   return isupper_l((unsigned char) wc, locale->lt) ||
+       islower_l((unsigned char) wc, locale->lt);
+}
+
 static bool
 wc_isdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
 {
@@ -248,6 +255,13 @@ wc_isxdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
 #endif
 }
 
+static bool
+wc_iscased_libc_mb(pg_wchar wc, pg_locale_t locale)
+{
+   return iswupper_l((wint_t) wc, locale->lt) ||
+       iswlower_l((wint_t) wc, locale->lt);
+}
+
 static bool
 char_is_cased_libc(char ch, pg_locale_t locale)
 {
@@ -332,6 +346,7 @@ static const struct ctype_methods ctype_methods_libc_sb = {
    .wc_isspace = wc_isspace_libc_sb,
    .wc_isxdigit = wc_isxdigit_libc_sb,
    .char_is_cased = char_is_cased_libc,
+   .wc_iscased = wc_iscased_libc_sb,
    .wc_toupper = toupper_libc_sb,
    .wc_tolower = tolower_libc_sb,
 };
@@ -357,6 +372,7 @@ static const struct ctype_methods ctype_methods_libc_other_mb = {
    .wc_isspace = wc_isspace_libc_sb,
    .wc_isxdigit = wc_isxdigit_libc_sb,
    .char_is_cased = char_is_cased_libc,
+   .wc_iscased = wc_iscased_libc_sb,
    .wc_toupper = toupper_libc_sb,
    .wc_tolower = tolower_libc_sb,
 };
@@ -378,6 +394,7 @@ static const struct ctype_methods ctype_methods_libc_utf8 = {
    .wc_isspace = wc_isspace_libc_mb,
    .wc_isxdigit = wc_isxdigit_libc_mb,
    .char_is_cased = char_is_cased_libc,
+   .wc_iscased = wc_iscased_libc_mb,
    .wc_toupper = toupper_libc_mb,
    .wc_tolower = tolower_libc_mb,
 };
index 50520e50127bb9c226023b77e5ba6522e315e593..832007385d87b080dd60ef70cea9ec19538b8681 100644 (file)
@@ -122,6 +122,7 @@ struct ctype_methods
    bool        (*wc_ispunct) (pg_wchar wc, pg_locale_t locale);
    bool        (*wc_isspace) (pg_wchar wc, pg_locale_t locale);
    bool        (*wc_isxdigit) (pg_wchar wc, pg_locale_t locale);
+   bool        (*wc_iscased) (pg_wchar wc, pg_locale_t locale);
    pg_wchar    (*wc_toupper) (pg_wchar wc, pg_locale_t locale);
    pg_wchar    (*wc_tolower) (pg_wchar wc, pg_locale_t locale);
 
@@ -214,6 +215,7 @@ extern bool pg_iswprint(pg_wchar wc, pg_locale_t locale);
 extern bool pg_iswpunct(pg_wchar wc, pg_locale_t locale);
 extern bool pg_iswspace(pg_wchar wc, pg_locale_t locale);
 extern bool pg_iswxdigit(pg_wchar wc, pg_locale_t locale);
+extern bool pg_iswcased(pg_wchar wc, pg_locale_t locale);
 extern pg_wchar pg_towupper(pg_wchar wc, pg_locale_t locale);
 extern pg_wchar pg_towlower(pg_wchar wc, pg_locale_t locale);