From 8673b5559121b861db98d7b8eacc320ff3c15595 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 5 Jun 2024 15:02:26 -0700
Subject: [PATCH v1 2/5] Make database collation pg_locale_t always non-NULL.

Previously, the database collation's pg_locale_t was NULL for the libc
provider.

This commit properly initializes a pg_locale_t object in all cases.
---
 src/backend/utils/adt/pg_locale.c | 58 +++++++++++++++++++++++++++----
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 29f16c49cb..185b860dad 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1582,7 +1582,58 @@ pg_init_database_collation()
 	}
 	else
 	{
+		const char *datcollate;
+		const char *datctype pg_attribute_unused();
+		locale_t	loc;
+
 		Assert(dbform->datlocprovider == COLLPROVIDER_LIBC);
+
+		datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datcollate);
+		datcollate = TextDatumGetCString(datum);
+		datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datctype);
+		datctype = TextDatumGetCString(datum);
+
+		if (strcmp(datcollate, datctype) == 0)
+		{
+			/* Normal case where they're the same */
+			errno = 0;
+#ifndef WIN32
+			loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, datcollate,
+							NULL);
+#else
+			loc = _create_locale(LC_ALL, datcollate);
+#endif
+			if (!loc)
+				report_newlocale_failure(datcollate);
+		}
+		else
+		{
+#ifndef WIN32
+			/* We need two newlocale() steps */
+			locale_t	loc1;
+
+			errno = 0;
+			loc1 = newlocale(LC_COLLATE_MASK, datcollate, NULL);
+			if (!loc1)
+				report_newlocale_failure(datcollate);
+			errno = 0;
+			loc = newlocale(LC_CTYPE_MASK, datctype, loc1);
+			if (!loc)
+				report_newlocale_failure(datctype);
+#else
+
+			/*
+			 * XXX The _create_locale() API doesn't appear to support
+			 * this. Could perhaps be worked around by changing
+			 * pg_locale_t to contain two separate fields.
+			 */
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("collations with different collate and ctype values are not supported on this platform")));
+#endif
+		}
+
+		default_locale.info.lt = loc;
 	}
 
 	default_locale.provider = dbform->datlocprovider;
@@ -1616,12 +1667,7 @@ pg_newlocale_from_collation(Oid collid)
 	Assert(OidIsValid(collid));
 
 	if (collid == DEFAULT_COLLATION_OID)
-	{
-		if (default_locale.provider == COLLPROVIDER_LIBC)
-			return (pg_locale_t) 0;
-		else
-			return &default_locale;
-	}
+		return &default_locale;
 
 	cache_entry = lookup_collation_cache(collid, false);
 
-- 
2.34.1

