Allow pg_locale_t APIs to work when ctype_is_c.
authorJeff Davis <jdavis@postgresql.org>
Wed, 26 Nov 2025 20:45:06 +0000 (12:45 -0800)
committerJeff Davis <jdavis@postgresql.org>
Wed, 26 Nov 2025 20:54:37 +0000 (12:54 -0800)
Previously, the caller needed to check ctype_is_c first for some
routines and not others. Now, the APIs consistently work, and the
caller can just check ctype_is_c for optimization purposes.

Discussion: https://postgr.es/m/450ceb6260cad30d7afdf155d991a9caafee7c0d.camel@j-davis.com
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
src/backend/utils/adt/pg_locale.c
src/backend/utils/adt/pg_locale_libc.c

index b14c7837938128a34a6adea0e92c26cfca688189..b02e7fa4f18ab4660dbd604339ea394cd81e145a 100644 (file)
@@ -1257,35 +1257,99 @@ get_collation_actual_version(char collprovider, const char *collcollate)
    return collversion;
 }
 
+/* lowercasing/casefolding in C locale */
+static size_t
+strlower_c(char *dst, size_t dstsize, const char *src, ssize_t srclen)
+{
+   int         i;
+
+   srclen = (srclen >= 0) ? srclen : strlen(src);
+   for (i = 0; i < srclen && i < dstsize; i++)
+       dst[i] = pg_ascii_tolower(src[i]);
+   if (i < dstsize)
+       dst[i] = '\0';
+   return srclen;
+}
+
+/* titlecasing in C locale */
+static size_t
+strtitle_c(char *dst, size_t dstsize, const char *src, ssize_t srclen)
+{
+   bool        wasalnum = false;
+   int         i;
+
+   srclen = (srclen >= 0) ? srclen : strlen(src);
+   for (i = 0; i < srclen && i < dstsize; i++)
+   {
+       char        c = src[i];
+
+       if (wasalnum)
+           dst[i] = pg_ascii_tolower(c);
+       else
+           dst[i] = pg_ascii_toupper(c);
+
+       wasalnum = ((c >= '0' && c <= '9') ||
+                   (c >= 'A' && c <= 'Z') ||
+                   (c >= 'a' && c <= 'z'));
+   }
+   if (i < dstsize)
+       dst[i] = '\0';
+   return srclen;
+}
+
+/* uppercasing in C locale */
+static size_t
+strupper_c(char *dst, size_t dstsize, const char *src, ssize_t srclen)
+{
+   int         i;
+
+   srclen = (srclen >= 0) ? srclen : strlen(src);
+   for (i = 0; i < srclen && i < dstsize; i++)
+       dst[i] = pg_ascii_toupper(src[i]);
+   if (i < dstsize)
+       dst[i] = '\0';
+   return srclen;
+}
+
 size_t
 pg_strlower(char *dst, size_t dstsize, const char *src, ssize_t srclen,
            pg_locale_t locale)
 {
-   return locale->ctype->strlower(dst, dstsize, src, srclen, locale);
+   if (locale->ctype == NULL)
+       return strlower_c(dst, dstsize, src, srclen);
+   else
+       return locale->ctype->strlower(dst, dstsize, src, srclen, locale);
 }
 
 size_t
 pg_strtitle(char *dst, size_t dstsize, const char *src, ssize_t srclen,
            pg_locale_t locale)
 {
-   return locale->ctype->strtitle(dst, dstsize, src, srclen, locale);
+   if (locale->ctype == NULL)
+       return strtitle_c(dst, dstsize, src, srclen);
+   else
+       return locale->ctype->strtitle(dst, dstsize, src, srclen, locale);
 }
 
 size_t
 pg_strupper(char *dst, size_t dstsize, const char *src, ssize_t srclen,
            pg_locale_t locale)
 {
-   return locale->ctype->strupper(dst, dstsize, src, srclen, locale);
+   if (locale->ctype == NULL)
+       return strupper_c(dst, dstsize, src, srclen);
+   else
+       return locale->ctype->strupper(dst, dstsize, src, srclen, locale);
 }
 
 size_t
 pg_strfold(char *dst, size_t dstsize, const char *src, ssize_t srclen,
           pg_locale_t locale)
 {
-   if (locale->ctype->strfold)
-       return locale->ctype->strfold(dst, dstsize, src, srclen, locale);
+   /* in the C locale, casefolding is the same as lowercasing */
+   if (locale->ctype == NULL)
+       return strlower_c(dst, dstsize, src, srclen);
    else
-       return locale->ctype->strlower(dst, dstsize, src, srclen, locale);
+       return locale->ctype->strfold(dst, dstsize, src, srclen, locale);
 }
 
 /*
@@ -1560,6 +1624,8 @@ pg_towlower(pg_wchar wc, pg_locale_t locale)
 bool
 char_is_cased(char ch, pg_locale_t locale)
 {
+   if (locale->ctype == NULL)
+       return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
    return locale->ctype->char_is_cased(ch, locale);
 }
 
@@ -1571,6 +1637,8 @@ char_is_cased(char ch, pg_locale_t locale)
 bool
 char_tolower_enabled(pg_locale_t locale)
 {
+   if (locale->ctype == NULL)
+       return true;
    return (locale->ctype->char_tolower != NULL);
 }
 
@@ -1582,6 +1650,8 @@ char_tolower_enabled(pg_locale_t locale)
 char
 char_tolower(unsigned char ch, pg_locale_t locale)
 {
+   if (locale->ctype == NULL)
+       return pg_ascii_tolower(ch);
    return locale->ctype->char_tolower(ch, locale);
 }
 
index abf27283a3325c9de316df703ded11f56ce27c27..e2beee44335a712175dac600a602ba38b5c0f180 100644 (file)
@@ -326,6 +326,8 @@ static const struct ctype_methods ctype_methods_libc_sb = {
    .strlower = strlower_libc_sb,
    .strtitle = strtitle_libc_sb,
    .strupper = strupper_libc_sb,
+   /* in libc, casefolding is the same as lowercasing */
+   .strfold = strlower_libc_sb,
    .wc_isdigit = wc_isdigit_libc_sb,
    .wc_isalpha = wc_isalpha_libc_sb,
    .wc_isalnum = wc_isalnum_libc_sb,
@@ -351,6 +353,8 @@ static const struct ctype_methods ctype_methods_libc_other_mb = {
    .strlower = strlower_libc_mb,
    .strtitle = strtitle_libc_mb,
    .strupper = strupper_libc_mb,
+   /* in libc, casefolding is the same as lowercasing */
+   .strfold = strlower_libc_mb,
    .wc_isdigit = wc_isdigit_libc_sb,
    .wc_isalpha = wc_isalpha_libc_sb,
    .wc_isalnum = wc_isalnum_libc_sb,
@@ -372,6 +376,8 @@ static const struct ctype_methods ctype_methods_libc_utf8 = {
    .strlower = strlower_libc_mb,
    .strtitle = strtitle_libc_mb,
    .strupper = strupper_libc_mb,
+   /* in libc, casefolding is the same as lowercasing */
+   .strfold = strlower_libc_mb,
    .wc_isdigit = wc_isdigit_libc_mb,
    .wc_isalpha = wc_isalpha_libc_mb,
    .wc_isalnum = wc_isalnum_libc_mb,