From e30ab34f6a9df6f1d0e64438b3632f3ed9ce3452 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Mon, 6 Oct 2025 13:01:25 -0700
Subject: [PATCH v1 3/6] Add pg_wc_isxdigit(), useful for tsearch.

---
 src/backend/utils/adt/pg_locale.c         | 12 ++++++++++++
 src/backend/utils/adt/pg_locale_builtin.c |  7 +++++++
 src/backend/utils/adt/pg_locale_icu.c     |  7 +++++++
 src/backend/utils/adt/pg_locale_libc.c    | 23 +++++++++++++++++++++++
 src/include/utils/pg_locale.h             |  2 ++
 5 files changed, 51 insertions(+)

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 07d26bb5e02..c06004400fc 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1493,6 +1493,18 @@ pg_wc_isspace(pg_wchar wc, pg_locale_t locale)
 		return locale->ctype->wc_isspace(wc, locale);
 }
 
+bool
+pg_wc_isxdigit(pg_wchar wc, pg_locale_t locale)
+{
+	if (locale->ctype_is_c)
+		return (wc <= (pg_wchar) 127 &&
+				((pg_char_properties[wc] & PG_ISDIGIT) ||
+				 ((wc >= 'A' && wc <= 'F') ||
+				  (wc >= 'a' && wc <= 'f'))));
+	else
+		return locale->ctype->wc_isxdigit(wc, locale);
+}
+
 pg_wchar
 pg_wc_toupper(pg_wchar wc, pg_locale_t locale)
 {
diff --git a/src/backend/utils/adt/pg_locale_builtin.c b/src/backend/utils/adt/pg_locale_builtin.c
index 526ab3c6711..3dc611b50e1 100644
--- a/src/backend/utils/adt/pg_locale_builtin.c
+++ b/src/backend/utils/adt/pg_locale_builtin.c
@@ -163,6 +163,12 @@ wc_isspace_builtin(pg_wchar wc, pg_locale_t locale)
 	return pg_u_isspace(wc);
 }
 
+static bool
+wc_isxdigit_builtin(pg_wchar wc, pg_locale_t locale)
+{
+	return pg_u_isxdigit(wc, !locale->builtin.casemap_full);
+}
+
 static bool
 char_is_cased_builtin(char ch, pg_locale_t locale)
 {
@@ -196,6 +202,7 @@ static const struct ctype_methods ctype_methods_builtin = {
 	.wc_isprint = wc_isprint_builtin,
 	.wc_ispunct = wc_ispunct_builtin,
 	.wc_isspace = wc_isspace_builtin,
+	.wc_isxdigit = wc_isxdigit_builtin,
 	.char_is_cased = char_is_cased_builtin,
 	.wc_tolower = wc_tolower_builtin,
 	.wc_toupper = wc_toupper_builtin,
diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c
index 9f0b4eead73..05bad202669 100644
--- a/src/backend/utils/adt/pg_locale_icu.c
+++ b/src/backend/utils/adt/pg_locale_icu.c
@@ -212,6 +212,12 @@ wc_isspace_icu(pg_wchar wc, pg_locale_t locale)
 	return u_isspace(wc);
 }
 
+static bool
+wc_isxdigit_icu(pg_wchar wc, pg_locale_t locale)
+{
+	return u_isxdigit(wc);
+}
+
 static const struct ctype_methods ctype_methods_icu = {
 	.strlower = strlower_icu,
 	.strtitle = strtitle_icu,
@@ -226,6 +232,7 @@ static const struct ctype_methods ctype_methods_icu = {
 	.wc_isprint = wc_isprint_icu,
 	.wc_ispunct = wc_ispunct_icu,
 	.wc_isspace = wc_isspace_icu,
+	.wc_isxdigit = wc_isxdigit_icu,
 	.char_is_cased = char_is_cased_icu,
 	.wc_toupper = toupper_icu,
 	.wc_tolower = tolower_icu,
diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c
index f56b5dbdd37..34865ccf00e 100644
--- a/src/backend/utils/adt/pg_locale_libc.c
+++ b/src/backend/utils/adt/pg_locale_libc.c
@@ -172,6 +172,16 @@ wc_isspace_libc_sb(pg_wchar wc, pg_locale_t locale)
 	return isspace_l((unsigned char) wc, locale->lt);
 }
 
+static bool
+wc_isxdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
+{
+#ifndef WIN32
+	return isxdigit_l((unsigned char) wc, locale->lt);
+#else
+	return _isxdigit_l((unsigned char) wc, locale->lt);
+#endif
+}
+
 static bool
 wc_isdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
 {
@@ -226,6 +236,16 @@ wc_isspace_libc_mb(pg_wchar wc, pg_locale_t locale)
 	return iswspace_l((wint_t) wc, locale->lt);
 }
 
+static bool
+wc_isxdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
+{
+#ifndef WIN32
+	return iswxdigit_l((wint_t) wc, locale->lt);
+#else
+	return _iswxdigit_l((wint_t) wc, locale->lt);
+#endif
+}
+
 static char
 char_tolower_libc(unsigned char ch, pg_locale_t locale)
 {
@@ -313,6 +333,7 @@ static const struct ctype_methods ctype_methods_libc_sb = {
 	.wc_isprint = wc_isprint_libc_sb,
 	.wc_ispunct = wc_ispunct_libc_sb,
 	.wc_isspace = wc_isspace_libc_sb,
+	.wc_isxdigit = wc_isxdigit_libc_sb,
 	.char_is_cased = char_is_cased_libc,
 	.char_tolower = char_tolower_libc,
 	.wc_toupper = toupper_libc_sb,
@@ -337,6 +358,7 @@ static const struct ctype_methods ctype_methods_libc_other_mb = {
 	.wc_isprint = wc_isprint_libc_sb,
 	.wc_ispunct = wc_ispunct_libc_sb,
 	.wc_isspace = wc_isspace_libc_sb,
+	.wc_isxdigit = wc_isxdigit_libc_sb,
 	.char_is_cased = char_is_cased_libc,
 	.char_tolower = char_tolower_libc,
 	.wc_toupper = toupper_libc_sb,
@@ -357,6 +379,7 @@ static const struct ctype_methods ctype_methods_libc_utf8 = {
 	.wc_isprint = wc_isprint_libc_mb,
 	.wc_ispunct = wc_ispunct_libc_mb,
 	.wc_isspace = wc_isspace_libc_mb,
+	.wc_isxdigit = wc_isxdigit_libc_mb,
 	.char_is_cased = char_is_cased_libc,
 	.char_tolower = char_tolower_libc,
 	.wc_toupper = toupper_libc_mb,
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 93a23bfe24c..2f6b04062f2 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -110,6 +110,7 @@ struct ctype_methods
 	bool		(*wc_isprint) (pg_wchar wc, pg_locale_t locale);
 	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);
 	pg_wchar	(*wc_toupper) (pg_wchar wc, pg_locale_t locale);
 	pg_wchar	(*wc_tolower) (pg_wchar wc, pg_locale_t locale);
 
@@ -217,6 +218,7 @@ extern bool pg_wc_isgraph(pg_wchar wc, pg_locale_t locale);
 extern bool pg_wc_isprint(pg_wchar wc, pg_locale_t locale);
 extern bool pg_wc_ispunct(pg_wchar wc, pg_locale_t locale);
 extern bool pg_wc_isspace(pg_wchar wc, pg_locale_t locale);
+extern bool pg_wc_isxdigit(pg_wchar wc, pg_locale_t locale);
 extern pg_wchar pg_wc_toupper(pg_wchar wc, pg_locale_t locale);
 extern pg_wchar pg_wc_tolower(pg_wchar wc, pg_locale_t locale);
 
-- 
2.43.0

