From 3b208a189ecc0d6942c357f8b616ad0aa9ee5848 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 5 Jun 2024 15:22:04 -0700
Subject: [PATCH v2 5/7] Avoid setlocale() in lc_collate_is_c() and
 lc_ctype_is_c().

Store the collate and ctype strings in the pg_locale_t structure,
which requires some minor refactoring.
---
 src/backend/regex/regc_pg_locale.c   | 44 ++++++++++++++--------------
 src/backend/utils/adt/formatting.c   | 20 ++++++-------
 src/backend/utils/adt/like.c         |  2 +-
 src/backend/utils/adt/like_support.c |  2 +-
 src/backend/utils/adt/pg_locale.c    | 30 +++++++++++--------
 src/include/utils/pg_locale.h        |  7 ++++-
 6 files changed, 57 insertions(+), 48 deletions(-)

diff --git a/src/backend/regex/regc_pg_locale.c b/src/backend/regex/regc_pg_locale.c
index 85f3238eb0..1e180b844f 100644
--- a/src/backend/regex/regc_pg_locale.c
+++ b/src/backend/regex/regc_pg_locale.c
@@ -309,11 +309,11 @@ pg_wc_isdigit(pg_wchar c)
 					isdigit((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswdigit_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswdigit_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					isdigit_l((unsigned char) c, pg_regex_locale->info.lt));
+					isdigit_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -343,11 +343,11 @@ pg_wc_isalpha(pg_wchar c)
 					isalpha((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswalpha_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswalpha_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					isalpha_l((unsigned char) c, pg_regex_locale->info.lt));
+					isalpha_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -377,11 +377,11 @@ pg_wc_isalnum(pg_wchar c)
 					isalnum((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswalnum_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswalnum_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					isalnum_l((unsigned char) c, pg_regex_locale->info.lt));
+					isalnum_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -420,11 +420,11 @@ pg_wc_isupper(pg_wchar c)
 					isupper((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswupper_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswupper_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					isupper_l((unsigned char) c, pg_regex_locale->info.lt));
+					isupper_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -454,11 +454,11 @@ pg_wc_islower(pg_wchar c)
 					islower((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswlower_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswlower_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					islower_l((unsigned char) c, pg_regex_locale->info.lt));
+					islower_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -488,11 +488,11 @@ pg_wc_isgraph(pg_wchar c)
 					isgraph((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswgraph_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswgraph_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					isgraph_l((unsigned char) c, pg_regex_locale->info.lt));
+					isgraph_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -522,11 +522,11 @@ pg_wc_isprint(pg_wchar c)
 					isprint((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswprint_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswprint_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					isprint_l((unsigned char) c, pg_regex_locale->info.lt));
+					isprint_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -556,11 +556,11 @@ pg_wc_ispunct(pg_wchar c)
 					ispunct((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswpunct_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswpunct_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					ispunct_l((unsigned char) c, pg_regex_locale->info.lt));
+					ispunct_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -590,11 +590,11 @@ pg_wc_isspace(pg_wchar c)
 					isspace((unsigned char) c));
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return iswspace_l((wint_t) c, pg_regex_locale->info.lt);
+				return iswspace_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			return (c <= (pg_wchar) UCHAR_MAX &&
-					isspace_l((unsigned char) c, pg_regex_locale->info.lt));
+					isspace_l((unsigned char) c, pg_regex_locale->info.libc.lt));
 			break;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -632,11 +632,11 @@ pg_wc_toupper(pg_wchar c)
 			return c;
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return towupper_l((wint_t) c, pg_regex_locale->info.lt);
+				return towupper_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			if (c <= (pg_wchar) UCHAR_MAX)
-				return toupper_l((unsigned char) c, pg_regex_locale->info.lt);
+				return toupper_l((unsigned char) c, pg_regex_locale->info.libc.lt);
 			return c;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
@@ -674,11 +674,11 @@ pg_wc_tolower(pg_wchar c)
 			return c;
 		case PG_REGEX_LOCALE_WIDE_L:
 			if (sizeof(wchar_t) >= 4 || c <= (pg_wchar) 0xFFFF)
-				return towlower_l((wint_t) c, pg_regex_locale->info.lt);
+				return towlower_l((wint_t) c, pg_regex_locale->info.libc.lt);
 			/* FALL THRU */
 		case PG_REGEX_LOCALE_1BYTE_L:
 			if (c <= (pg_wchar) UCHAR_MAX)
-				return tolower_l((unsigned char) c, pg_regex_locale->info.lt);
+				return tolower_l((unsigned char) c, pg_regex_locale->info.libc.lt);
 			return c;
 		case PG_REGEX_LOCALE_ICU:
 #ifdef USE_ICU
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 8736ada4be..2c3a28ca25 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1732,7 +1732,7 @@ str_tolower(const char *buff, size_t nbytes, Oid collid)
 				for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
 				{
 					if (mylocale)
-						workspace[curr_char] = towlower_l(workspace[curr_char], mylocale->info.lt);
+						workspace[curr_char] = towlower_l(workspace[curr_char], mylocale->info.libc.lt);
 					else
 						workspace[curr_char] = towlower(workspace[curr_char]);
 				}
@@ -1763,7 +1763,7 @@ str_tolower(const char *buff, size_t nbytes, Oid collid)
 				for (p = result; *p; p++)
 				{
 					if (mylocale)
-						*p = tolower_l((unsigned char) *p, mylocale->info.lt);
+						*p = tolower_l((unsigned char) *p, mylocale->info.libc.lt);
 					else
 						*p = pg_tolower((unsigned char) *p);
 				}
@@ -1880,7 +1880,7 @@ str_toupper(const char *buff, size_t nbytes, Oid collid)
 				for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
 				{
 					if (mylocale)
-						workspace[curr_char] = towupper_l(workspace[curr_char], mylocale->info.lt);
+						workspace[curr_char] = towupper_l(workspace[curr_char], mylocale->info.libc.lt);
 					else
 						workspace[curr_char] = towupper(workspace[curr_char]);
 				}
@@ -1911,7 +1911,7 @@ str_toupper(const char *buff, size_t nbytes, Oid collid)
 				for (p = result; *p; p++)
 				{
 					if (mylocale)
-						*p = toupper_l((unsigned char) *p, mylocale->info.lt);
+						*p = toupper_l((unsigned char) *p, mylocale->info.libc.lt);
 					else
 						*p = pg_toupper((unsigned char) *p);
 				}
@@ -2084,10 +2084,10 @@ str_initcap(const char *buff, size_t nbytes, Oid collid)
 					if (mylocale)
 					{
 						if (wasalnum)
-							workspace[curr_char] = towlower_l(workspace[curr_char], mylocale->info.lt);
+							workspace[curr_char] = towlower_l(workspace[curr_char], mylocale->info.libc.lt);
 						else
-							workspace[curr_char] = towupper_l(workspace[curr_char], mylocale->info.lt);
-						wasalnum = iswalnum_l(workspace[curr_char], mylocale->info.lt);
+							workspace[curr_char] = towupper_l(workspace[curr_char], mylocale->info.libc.lt);
+						wasalnum = iswalnum_l(workspace[curr_char], mylocale->info.libc.lt);
 					}
 					else
 					{
@@ -2127,10 +2127,10 @@ str_initcap(const char *buff, size_t nbytes, Oid collid)
 					if (mylocale)
 					{
 						if (wasalnum)
-							*p = tolower_l((unsigned char) *p, mylocale->info.lt);
+							*p = tolower_l((unsigned char) *p, mylocale->info.libc.lt);
 						else
-							*p = toupper_l((unsigned char) *p, mylocale->info.lt);
-						wasalnum = isalnum_l((unsigned char) *p, mylocale->info.lt);
+							*p = toupper_l((unsigned char) *p, mylocale->info.libc.lt);
+						wasalnum = isalnum_l((unsigned char) *p, mylocale->info.libc.lt);
 					}
 					else
 					{
diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c
index 0807b89b17..4d9a207816 100644
--- a/src/backend/utils/adt/like.c
+++ b/src/backend/utils/adt/like.c
@@ -96,7 +96,7 @@ SB_lower_char(unsigned char c, pg_locale_t locale, bool locale_is_c)
 	if (locale_is_c)
 		return pg_ascii_tolower(c);
 	else if (locale)
-		return tolower_l(c, locale->info.lt);
+		return tolower_l(c, locale->info.libc.lt);
 	else
 		return pg_tolower(c);
 }
diff --git a/src/backend/utils/adt/like_support.c b/src/backend/utils/adt/like_support.c
index 2635050861..6a1c64176d 100644
--- a/src/backend/utils/adt/like_support.c
+++ b/src/backend/utils/adt/like_support.c
@@ -1509,7 +1509,7 @@ pattern_char_isalpha(char c, bool is_multibyte,
 		return IS_HIGHBIT_SET(c) ||
 			(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
 	else if (locale && locale->provider == COLLPROVIDER_LIBC)
-		return isalpha_l((unsigned char) c, locale->info.lt);
+		return isalpha_l((unsigned char) c, locale->info.libc.lt);
 	else
 		return isalpha((unsigned char) c);
 }
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 2ffc551913..060dcbcb14 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1350,9 +1350,7 @@ lc_collate_is_c(Oid collation)
 		}
 		else if (default_locale.provider == COLLPROVIDER_LIBC)
 		{
-			localeptr = setlocale(LC_CTYPE, NULL);
-			if (!localeptr)
-				elog(ERROR, "invalid LC_CTYPE setting");
+			localeptr = default_locale.info.libc.collate;
 		}
 		else
 			elog(ERROR, "unexpected collation provider '%c'",
@@ -1416,9 +1414,7 @@ lc_ctype_is_c(Oid collation)
 		}
 		else if (default_locale.provider == COLLPROVIDER_LIBC)
 		{
-			localeptr = setlocale(LC_CTYPE, NULL);
-			if (!localeptr)
-				elog(ERROR, "invalid LC_CTYPE setting");
+			localeptr = default_locale.info.libc.ctype;
 		}
 		else
 			elog(ERROR, "unexpected collation provider '%c'",
@@ -1629,7 +1625,11 @@ pg_init_database_collation()
 #endif
 		}
 
-		default_locale.info.lt = loc;
+		default_locale.info.libc.collate = MemoryContextStrdup(
+			TopMemoryContext, datcollate);
+		default_locale.info.libc.ctype = MemoryContextStrdup(
+			TopMemoryContext, datctype);
+		default_locale.info.libc.lt = loc;
 	}
 
 	default_locale.provider = dbform->datlocprovider;
@@ -1750,7 +1750,11 @@ pg_newlocale_from_collation(Oid collid)
 #endif
 			}
 
-			result.info.lt = loc;
+			result.info.libc.collate = MemoryContextStrdup(
+				TopMemoryContext, collcollate);
+			result.info.libc.ctype = MemoryContextStrdup(
+				TopMemoryContext, collctype);
+			result.info.libc.lt = loc;
 		}
 		else if (collform->collprovider == COLLPROVIDER_ICU)
 		{
@@ -1990,7 +1994,7 @@ pg_strncoll_libc_win32_utf8(const char *arg1, size_t len1, const char *arg2,
 	((LPWSTR) a2p)[r] = 0;
 
 	errno = 0;
-	result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->info.lt);
+	result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->info.libc.lt);
 	if (result == 2147483647)	/* _NLSCMPERROR; missing from mingw headers */
 		ereport(ERROR,
 				(errmsg("could not compare Unicode strings: %m")));
@@ -2027,7 +2031,7 @@ pg_strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale)
 	}
 	else
 #endif							/* WIN32 */
-		result = strcoll_l(arg1, arg2, locale->info.lt);
+		result = strcoll_l(arg1, arg2, locale->info.libc.lt);
 
 	return result;
 }
@@ -2252,7 +2256,7 @@ pg_strxfrm_libc(char *dest, const char *src, size_t destsize,
 	Assert(locale->provider == COLLPROVIDER_LIBC);
 
 #ifdef TRUST_STRXFRM
-	return strxfrm_l(dest, src, destsize, locale->info.lt);
+	return strxfrm_l(dest, src, destsize, locale->info.libc.lt);
 #else
 	/* shouldn't happen */
 	PGLOCALE_SUPPORT_ERROR(locale->provider);
@@ -3185,7 +3189,7 @@ wchar2char(char *to, const wchar_t *from, size_t tolen, pg_locale_t locale)
 	else
 	{
 		/* Use wcstombs_l for nondefault locales */
-		result = wcstombs_l(to, from, tolen, locale->info.lt);
+		result = wcstombs_l(to, from, tolen, locale->info.libc.lt);
 	}
 
 	return result;
@@ -3247,7 +3251,7 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
 		else
 		{
 			/* Use mbstowcs_l for nondefault locales */
-			result = mbstowcs_l(to, str, tolen, locale->info.lt);
+			result = mbstowcs_l(to, str, tolen, locale->info.libc.lt);
 		}
 
 		pfree(str);
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 3a9026e7b7..63b724dc1d 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -80,7 +80,12 @@ struct pg_locale_struct
 		{
 			const char *locale;
 		}			builtin;
-		locale_t	lt;
+		struct
+		{
+			const char *collate;
+			const char *ctype;
+			locale_t	lt;
+		}			libc;
 #ifdef USE_ICU
 		struct
 		{
-- 
2.34.1

