From 9bd779fb711c902b33cfd3a5350e0736d7ceb138 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Mon, 29 Jul 2024 23:58:29 -0700
Subject: [PATCH v6 3/3] selfuncs.c: use pg_strxfrm() instead of strxfrm().

pg_strxfrm() takes a pg_locale_t, so it works properly with other
providers and does not rely on setlocale().

Discussion: https://postgr.es/m/cfd9eb85-c52a-4ec9-a90e-a5e4de56e57d@eisentraut.org
Reviewed-by: Peter Eisentraut, Andreas Karlsson
---
 src/backend/utils/adt/pg_locale.c | 23 ++++++++++++++++-------
 src/backend/utils/adt/selfuncs.c  |  9 +++++++--
 2 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index ccd6180a743..17c55c5ab17 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -2164,14 +2164,7 @@ pg_strxfrm_libc(char *dest, const char *src, size_t destsize,
 				pg_locale_t locale)
 {
 	Assert(locale->provider == COLLPROVIDER_LIBC);
-
-#ifdef TRUST_STRXFRM
 	return strxfrm_l(dest, src, destsize, locale->info.lt);
-#else
-	/* shouldn't happen */
-	PGLOCALE_SUPPORT_ERROR(locale->provider);
-	return 0;					/* keep compiler quiet */
-#endif
 }
 
 static size_t
@@ -2380,6 +2373,10 @@ pg_strxfrm_enabled(pg_locale_t locale)
  * The provided 'src' must be nul-terminated. If 'destsize' is zero, 'dest'
  * may be NULL.
  *
+ * Not all providers support pg_strxfrm() safely. The caller should check
+ * pg_strxfrm_enabled() first, otherwise this function may return wrong
+ * results or an error.
+ *
  * Returns the number of bytes needed to store the transformed string,
  * excluding the terminating nul byte. If the value returned is 'destsize' or
  * greater, the resulting contents of 'dest' are undefined.
@@ -2412,6 +2409,10 @@ pg_strxfrm(char *dest, const char *src, size_t destsize, pg_locale_t locale)
  * 'src' does not need to be nul-terminated. If 'destsize' is zero, 'dest' may
  * be NULL.
  *
+ * Not all providers support pg_strnxfrm() safely. The caller should check
+ * pg_strxfrm_enabled() first, otherwise this function may return wrong
+ * results or an error.
+ *
  * Returns the number of bytes needed to store the transformed string,
  * excluding the terminating nul byte. If the value returned is 'destsize' or
  * greater, the resulting contents of 'dest' are undefined.
@@ -2466,6 +2467,10 @@ pg_strxfrm_prefix_enabled(pg_locale_t locale)
  *
  * The provided 'src' must be nul-terminated.
  *
+ * Not all providers support pg_strxfrm_prefix() safely. The caller should
+ * check pg_strxfrm_prefix_enabled() first, otherwise this function may return
+ * wrong results or an error.
+ *
  * If destsize is not large enough to hold the resulting byte sequence, stores
  * only the first destsize bytes in 'dest'. Returns the number of bytes
  * actually copied to 'dest'.
@@ -2495,6 +2500,10 @@ pg_strxfrm_prefix(char *dest, const char *src, size_t destsize,
  *
  * The provided 'src' must be nul-terminated.
  *
+ * Not all providers support pg_strnxfrm_prefix() safely. The caller should
+ * check pg_strxfrm_prefix_enabled() first, otherwise this function may return
+ * wrong results or an error.
+ *
  * If destsize is not large enough to hold the resulting byte sequence, stores
  * only the first destsize bytes in 'dest'. Returns the number of bytes
  * actually copied to 'dest'.
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 877a62a62ec..673cfd9e703 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -4673,6 +4673,7 @@ convert_string_datum(Datum value, Oid typid, Oid collid, bool *failure)
 
 	if (!lc_collate_is_c(collid))
 	{
+		pg_locale_t mylocale = pg_newlocale_from_collation(collid);
 		char	   *xfrmstr;
 		size_t		xfrmlen;
 		size_t		xfrmlen2 PG_USED_FOR_ASSERTS_ONLY;
@@ -4685,8 +4686,12 @@ convert_string_datum(Datum value, Oid typid, Oid collid, bool *failure)
 		 * bogus data or set an error. This is not really a problem unless it
 		 * crashes since it will only give an estimation error and nothing
 		 * fatal.
+		 *
+		 * XXX: we do not check pg_strxfrm_enabled(). On some platforms and in
+		 * some cases, libc strxfrm() may return the wrong results, but that
+		 * will only lead to an estimation error.
 		 */
-		xfrmlen = strxfrm(NULL, val, 0);
+		xfrmlen = pg_strxfrm(NULL, val, 0, mylocale);
 #ifdef WIN32
 
 		/*
@@ -4698,7 +4703,7 @@ convert_string_datum(Datum value, Oid typid, Oid collid, bool *failure)
 			return val;
 #endif
 		xfrmstr = (char *) palloc(xfrmlen + 1);
-		xfrmlen2 = strxfrm(xfrmstr, val, xfrmlen + 1);
+		xfrmlen2 = pg_strxfrm(xfrmstr, val, xfrmlen + 1, mylocale);
 
 		/*
 		 * Some systems (e.g., glibc) can return a smaller value from the
-- 
2.34.1

