From 18b5b0055c6e59ed4739a626d59a88891f0ca382 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 25 Sep 2024 15:49:32 -0700
Subject: [PATCH v6 04/11] Perform provider-specific initialization code in new
 functions.

---
 src/backend/utils/adt/pg_locale.c      | 199 ++++++++-----------------
 src/backend/utils/adt/pg_locale_icu.c  |  97 +++++++++++-
 src/backend/utils/adt/pg_locale_libc.c |  74 ++++++++-
 3 files changed, 227 insertions(+), 143 deletions(-)

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 9bbb3420be..0534a232a5 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -89,10 +89,11 @@
 
 #define		MAX_L10N_DATA		80
 
+extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
+extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
+
 #ifdef USE_ICU
 extern UCollator *pg_ucol_open(const char *loc_str);
-extern UCollator *make_icu_collator(const char *iculocstr,
-									const char *icurules);
 extern int	strncoll_icu(const char *arg1, ssize_t len1,
 						 const char *arg2, ssize_t len2,
 						 pg_locale_t locale);
@@ -104,8 +105,6 @@ extern size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
 								  pg_locale_t locale);
 #endif
 
-extern locale_t make_libc_collator(const char *collate,
-								   const char *ctype);
 extern int	strncoll_libc(const char *arg1, ssize_t len1,
 						  const char *arg2, ssize_t len2,
 						  pg_locale_t locale);
@@ -136,7 +135,7 @@ char	   *localized_full_months[12 + 1];
 /* is the databases's LC_CTYPE the C locale? */
 bool		database_ctype_is_c = false;
 
-static struct pg_locale_struct default_locale;
+static pg_locale_t default_locale = NULL;
 
 /* indicates whether locale information cache is valid */
 static bool CurrentLocaleConvValid = false;
@@ -1211,6 +1210,51 @@ IsoLocaleName(const char *winlocname)
 
 #endif							/* WIN32 && LC_MESSAGES */
 
+static pg_locale_t
+create_pg_locale_builtin(Oid collid, MemoryContext context)
+{
+	const char *locstr;
+	pg_locale_t result;
+
+	if (collid == DEFAULT_COLLATION_OID)
+	{
+		HeapTuple	tp;
+		Datum		datum;
+
+		tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+		datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+									   Anum_pg_database_datlocale);
+		locstr = TextDatumGetCString(datum);
+		ReleaseSysCache(tp);
+	}
+	else
+	{
+		HeapTuple	tp;
+		Datum		datum;
+
+		tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup failed for collation %u", collid);
+		datum = SysCacheGetAttrNotNull(COLLOID, tp,
+									   Anum_pg_collation_colllocale);
+		locstr = TextDatumGetCString(datum);
+		ReleaseSysCache(tp);
+	}
+
+	builtin_validate_locale(GetDatabaseEncoding(), locstr);
+
+	result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
+
+	result->info.builtin.locale = MemoryContextStrdup(context, locstr);
+	result->provider = COLLPROVIDER_BUILTIN;
+	result->deterministic = true;
+	result->collate_is_c = true;
+	result->ctype_is_c = (strcmp(locstr, "C") == 0);
+
+	return result;
+}
 
 /*
  * Create a new pg_locale_t struct for the given collation oid.
@@ -1225,75 +1269,17 @@ create_pg_locale(Oid collid, MemoryContext context)
 	Datum		datum;
 	bool		isnull;
 
-	result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
-
 	tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
 	if (!HeapTupleIsValid(tp))
 		elog(ERROR, "cache lookup failed for collation %u", collid);
 	collform = (Form_pg_collation) GETSTRUCT(tp);
 
-	result->provider = collform->collprovider;
-	result->deterministic = collform->collisdeterministic;
-
 	if (collform->collprovider == COLLPROVIDER_BUILTIN)
-	{
-		const char *locstr;
-
-		datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_colllocale);
-		locstr = TextDatumGetCString(datum);
-
-		result->collate_is_c = true;
-		result->ctype_is_c = (strcmp(locstr, "C") == 0);
-
-		builtin_validate_locale(GetDatabaseEncoding(), locstr);
-
-		result->info.builtin.locale = MemoryContextStrdup(context,
-														  locstr);
-	}
+		result = create_pg_locale_builtin(collid, context);
 	else if (collform->collprovider == COLLPROVIDER_ICU)
-	{
-#ifdef USE_ICU
-		const char *iculocstr;
-		const char *icurules;
-
-		datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_colllocale);
-		iculocstr = TextDatumGetCString(datum);
-
-		result->collate_is_c = false;
-		result->ctype_is_c = false;
-
-		datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collicurules, &isnull);
-		if (!isnull)
-			icurules = TextDatumGetCString(datum);
-		else
-			icurules = NULL;
-
-		result->info.icu.locale = MemoryContextStrdup(context, iculocstr);
-		result->info.icu.ucol = make_icu_collator(iculocstr, icurules);
-#else
-		/* could get here if a collation was created by a build with ICU */
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("ICU is not supported in this build")));
-#endif
-	}
+		result = create_pg_locale_icu(collid, context);
 	else if (collform->collprovider == COLLPROVIDER_LIBC)
-	{
-		const char *collcollate;
-		const char *collctype;
-
-		datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
-		collcollate = TextDatumGetCString(datum);
-		datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collctype);
-		collctype = TextDatumGetCString(datum);
-
-		result->collate_is_c = (strcmp(collcollate, "C") == 0) ||
-			(strcmp(collcollate, "POSIX") == 0);
-		result->ctype_is_c = (strcmp(collctype, "C") == 0) ||
-			(strcmp(collctype, "POSIX") == 0);
-
-		result->info.lt = make_libc_collator(collcollate, collctype);
-	}
+		result = create_pg_locale_libc(collid, context);
 	else
 		/* shouldn't happen */
 		PGLOCALE_SUPPORT_ERROR(collform->collprovider);
@@ -1353,7 +1339,9 @@ init_database_collation(void)
 {
 	HeapTuple	tup;
 	Form_pg_database dbform;
-	Datum		datum;
+	pg_locale_t result;
+
+	Assert(default_locale == NULL);
 
 	/* Fetch our pg_database row normally, via syscache */
 	tup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
@@ -1362,80 +1350,21 @@ init_database_collation(void)
 	dbform = (Form_pg_database) GETSTRUCT(tup);
 
 	if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
-	{
-		char	   *datlocale;
-
-		datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datlocale);
-		datlocale = TextDatumGetCString(datum);
-
-		builtin_validate_locale(dbform->encoding, datlocale);
-
-		default_locale.collate_is_c = true;
-		default_locale.ctype_is_c = (strcmp(datlocale, "C") == 0);
-
-		default_locale.info.builtin.locale = MemoryContextStrdup(
-																 TopMemoryContext, datlocale);
-	}
+		result = create_pg_locale_builtin(DEFAULT_COLLATION_OID,
+										  TopMemoryContext);
 	else if (dbform->datlocprovider == COLLPROVIDER_ICU)
-	{
-#ifdef USE_ICU
-		char	   *datlocale;
-		char	   *icurules;
-		bool		isnull;
-
-		datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datlocale);
-		datlocale = TextDatumGetCString(datum);
-
-		default_locale.collate_is_c = false;
-		default_locale.ctype_is_c = false;
-
-		datum = SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_daticurules, &isnull);
-		if (!isnull)
-			icurules = TextDatumGetCString(datum);
-		else
-			icurules = NULL;
-
-		default_locale.info.icu.locale = MemoryContextStrdup(TopMemoryContext, datlocale);
-		default_locale.info.icu.ucol = make_icu_collator(datlocale, icurules);
-#else
-		/* could get here if a collation was created by a build with ICU */
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("ICU is not supported in this build")));
-#endif
-	}
+		result = create_pg_locale_icu(DEFAULT_COLLATION_OID,
+									  TopMemoryContext);
 	else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
-	{
-		const char *datcollate;
-		const char *datctype;
-
-		datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datcollate);
-		datcollate = TextDatumGetCString(datum);
-		datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datctype);
-		datctype = TextDatumGetCString(datum);
-
-		default_locale.collate_is_c = (strcmp(datcollate, "C") == 0) ||
-			(strcmp(datcollate, "POSIX") == 0);
-		default_locale.ctype_is_c = (strcmp(datctype, "C") == 0) ||
-			(strcmp(datctype, "POSIX") == 0);
-
-		default_locale.info.lt = make_libc_collator(datcollate, datctype);
-	}
+		result = create_pg_locale_libc(DEFAULT_COLLATION_OID,
+									   TopMemoryContext);
 	else
 		/* shouldn't happen */
 		PGLOCALE_SUPPORT_ERROR(dbform->datlocprovider);
 
-
-	default_locale.provider = dbform->datlocprovider;
-
-	/*
-	 * Default locale is currently always deterministic.  Nondeterministic
-	 * locales currently don't support pattern matching, which would break a
-	 * lot of things if applied globally.
-	 */
-	default_locale.deterministic = true;
-
 	ReleaseSysCache(tup);
+
+	default_locale = result;
 }
 
 /*
@@ -1453,7 +1382,7 @@ pg_newlocale_from_collation(Oid collid)
 	bool		found;
 
 	if (collid == DEFAULT_COLLATION_OID)
-		return &default_locale;
+		return default_locale;
 
 	if (!OidIsValid(collid))
 		elog(ERROR, "cache lookup failed for collation %u", collid);
diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c
index c91954787d..e3268f9f69 100644
--- a/src/backend/utils/adt/pg_locale_icu.c
+++ b/src/backend/utils/adt/pg_locale_icu.c
@@ -12,14 +12,20 @@
 #include "postgres.h"
 
 #ifdef USE_ICU
-
 #include <unicode/ucnv.h>
 #include <unicode/ustring.h>
+#endif
 
+#include "access/htup_details.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_collation.h"
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 #include "utils/formatting.h"
+#include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/syscache.h"
 
 /*
  * This should be large enough that most strings will fit, but small enough
@@ -27,9 +33,11 @@
  */
 #define		TEXTBUFLEN			1024
 
+extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
+
+#ifdef USE_ICU
+
 extern UCollator *pg_ucol_open(const char *loc_str);
-extern UCollator *make_icu_collator(const char *iculocstr,
-									const char *icurules);
 extern int	strncoll_icu(const char *arg1, ssize_t len1,
 						 const char *arg2, ssize_t len2,
 						 pg_locale_t locale);
@@ -47,6 +55,8 @@ extern size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
  */
 static UConverter *icu_converter = NULL;
 
+static UCollator *make_icu_collator(const char *iculocstr,
+									const char *icurules);
 static int	strncoll_icu_no_utf8(const char *arg1, ssize_t len1,
 								 const char *arg2, ssize_t len2,
 								 pg_locale_t locale);
@@ -61,6 +71,85 @@ static int32_t uchar_convert(UConverter *converter,
 							 const char *src, int32_t srclen);
 static void icu_set_collation_attributes(UCollator *collator, const char *loc,
 										 UErrorCode *status);
+#endif
+
+pg_locale_t
+create_pg_locale_icu(Oid collid, MemoryContext context)
+{
+#ifdef USE_ICU
+	bool		deterministic;
+	const char *iculocstr;
+	const char *icurules = NULL;
+	UCollator  *collator;
+	pg_locale_t result;
+
+	if (collid == DEFAULT_COLLATION_OID)
+	{
+		HeapTuple	tp;
+		Datum		datum;
+		bool		isnull;
+
+		tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+
+		/* default database collation is always deterministic */
+		deterministic = true;
+		datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+									   Anum_pg_database_datlocale);
+		iculocstr = TextDatumGetCString(datum);
+		datum = SysCacheGetAttr(DATABASEOID, tp,
+								Anum_pg_database_daticurules, &isnull);
+		if (!isnull)
+			icurules = TextDatumGetCString(datum);
+
+		ReleaseSysCache(tp);
+	}
+	else
+	{
+		Form_pg_collation collform;
+		HeapTuple	tp;
+		Datum		datum;
+		bool		isnull;
+
+		tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup failed for collation %u", collid);
+		collform = (Form_pg_collation) GETSTRUCT(tp);
+		deterministic = collform->collisdeterministic;
+		datum = SysCacheGetAttrNotNull(COLLOID, tp,
+									   Anum_pg_collation_colllocale);
+		iculocstr = TextDatumGetCString(datum);
+		datum = SysCacheGetAttr(COLLOID, tp,
+								Anum_pg_collation_collicurules, &isnull);
+		if (!isnull)
+			icurules = TextDatumGetCString(datum);
+
+		ReleaseSysCache(tp);
+	}
+
+	collator = make_icu_collator(iculocstr, icurules);
+
+	result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
+	result->info.icu.locale = MemoryContextStrdup(context, iculocstr);
+	result->info.icu.ucol = collator;
+	result->provider = COLLPROVIDER_ICU;
+	result->deterministic = deterministic;
+	result->collate_is_c = false;
+	result->ctype_is_c = false;
+
+	return result;
+#else
+	/* could get here if a collation was created by a build with ICU */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("ICU is not supported in this build")));
+
+	return NULL;
+#endif
+}
+
+#ifdef USE_ICU
 
 /*
  * Wrapper around ucol_open() to handle API differences for older ICU
@@ -158,7 +247,7 @@ pg_ucol_open(const char *loc_str)
  *
  * Ensure that no path leaks a UCollator.
  */
-UCollator *
+static UCollator *
 make_icu_collator(const char *iculocstr, const char *icurules)
 {
 	if (!icurules)
diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c
index 61066ee21a..8736661111 100644
--- a/src/backend/utils/adt/pg_locale_libc.c
+++ b/src/backend/utils/adt/pg_locale_libc.c
@@ -11,10 +11,16 @@
 
 #include "postgres.h"
 
+#include "access/htup_details.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_collation.h"
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 #include "utils/formatting.h"
+#include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/syscache.h"
 
 /*
  * This should be large enough that most strings will fit, but small enough
@@ -22,15 +28,16 @@
  */
 #define		TEXTBUFLEN			1024
 
-extern locale_t make_libc_collator(const char *collate,
-								   const char *ctype);
+extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
+
 extern int	strncoll_libc(const char *arg1, ssize_t len1,
 						  const char *arg2, ssize_t len2,
 						  pg_locale_t locale);
 extern size_t strnxfrm_libc(char *dest, size_t destsize,
 							const char *src, ssize_t srclen,
 							pg_locale_t locale);
-
+static locale_t make_libc_collator(const char *collate,
+								   const char *ctype);
 static void report_newlocale_failure(const char *localename);
 
 #ifdef WIN32
@@ -39,6 +46,65 @@ static int	strncoll_libc_win32_utf8(const char *arg1, ssize_t len1,
 									 pg_locale_t locale);
 #endif
 
+pg_locale_t
+create_pg_locale_libc(Oid collid, MemoryContext context)
+{
+	const char *collate;
+	const char *ctype;
+	locale_t	loc;
+	pg_locale_t result;
+
+	if (collid == DEFAULT_COLLATION_OID)
+	{
+		HeapTuple	tp;
+		Datum		datum;
+
+		tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+		datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+									   Anum_pg_database_datcollate);
+		collate = TextDatumGetCString(datum);
+		datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+									   Anum_pg_database_datctype);
+		ctype = TextDatumGetCString(datum);
+
+		ReleaseSysCache(tp);
+	}
+	else
+	{
+		HeapTuple	tp;
+		Datum		datum;
+
+		tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup failed for collation %u", collid);
+
+		datum = SysCacheGetAttrNotNull(COLLOID, tp,
+									   Anum_pg_collation_collcollate);
+		collate = TextDatumGetCString(datum);
+		datum = SysCacheGetAttrNotNull(COLLOID, tp,
+									   Anum_pg_collation_collctype);
+		ctype = TextDatumGetCString(datum);
+
+		ReleaseSysCache(tp);
+	}
+
+
+	loc = make_libc_collator(collate, ctype);
+
+	result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
+	result->provider = COLLPROVIDER_LIBC;
+	result->deterministic = true;
+	result->collate_is_c = (strcmp(collate, "C") == 0) ||
+		(strcmp(collate, "POSIX") == 0);
+	result->ctype_is_c = (strcmp(ctype, "C") == 0) ||
+		(strcmp(ctype, "POSIX") == 0);
+	result->info.lt = loc;
+
+	return result;
+}
+
 /*
  * Create a locale_t with the given collation and ctype.
  *
@@ -47,7 +113,7 @@ static int	strncoll_libc_win32_utf8(const char *arg1, ssize_t len1,
  *
  * Ensure that no path leaks a locale_t.
  */
-locale_t
+static locale_t
 make_libc_collator(const char *collate, const char *ctype)
 {
 	locale_t	loc = 0;
-- 
2.34.1

