From 39f0bc866b092a5def31184bf7f8ce7b7fcd7f89 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Mon, 13 Apr 2026 12:09:40 -0700
Subject: [PATCH v2] catcache.c: always use C_COLLATION_OID.

The problem report was about setting GUCs in the startup packet when
initiating a replication connection. Setting the GUC required an ACL
check, which performed a catalog lookup on pg_parameter_acl.parname
(type TEXT). The catalog cache was hardwired to use
DEFAULT_COLLATION_OID for text attributes, but walsender never calls
CheckMyDatabase(), so the default collation was uninitialized and it
caused a NULL pointer dereference.

As the comments stated, using DEFAULT_COLLATION_OID was arbitrary
anyway: if the collation actually mattered, it should use the column's
actual collation. (In the catalog, some text columns are the default
collation and some are "C".)

Fix by using C_COLLATION_OID, which doesn't require any initialization
and is always available. When any deterministic collation will do,
it's best to consistently use the simplest and fastest one, so this is
a good idea anyway.

There may be other problems in this general area, so this should not
be considered a complete fix. But this is an independently good change
and solves the immediate problem.

Reported-by: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/D18AD72A-5004-4EF8-AF80-10732AF677FA@yandex-team.ru
Backpatch-through: 17
---
 src/backend/utils/cache/catcache.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 87ed5506460..a8e7bf649d2 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -205,6 +205,10 @@ nameeqfast(Datum a, Datum b)
 	char	   *ca = NameStr(*DatumGetName(a));
 	char	   *cb = NameStr(*DatumGetName(b));
 
+	/*
+	 * Catalogs only use deterministic collations, so ignore column collation
+	 * and use fast path.
+	 */
 	return strncmp(ca, cb, NAMEDATALEN) == 0;
 }
 
@@ -213,6 +217,10 @@ namehashfast(Datum datum)
 {
 	char	   *key = NameStr(*DatumGetName(datum));
 
+	/*
+	 * Catalogs only use deterministic collations, so ignore column collation
+	 * and use fast path.
+	 */
 	return hash_bytes((unsigned char *) key, strlen(key));
 }
 
@@ -244,17 +252,20 @@ static bool
 texteqfast(Datum a, Datum b)
 {
 	/*
-	 * The use of DEFAULT_COLLATION_OID is fairly arbitrary here.  We just
-	 * want to take the fast "deterministic" path in texteq().
+	 * Catalogs only use deterministic collations, so ignore column collation
+	 * and use "C" locale for efficiency.
 	 */
-	return DatumGetBool(DirectFunctionCall2Coll(texteq, DEFAULT_COLLATION_OID, a, b));
+	return DatumGetBool(DirectFunctionCall2Coll(texteq, C_COLLATION_OID, a, b));
 }
 
 static uint32
 texthashfast(Datum datum)
 {
-	/* analogously here as in texteqfast() */
-	return DatumGetInt32(DirectFunctionCall1Coll(hashtext, DEFAULT_COLLATION_OID, datum));
+	/*
+	 * Catalogs only use deterministic collations, so ignore column collation
+	 * and use "C" locale for efficiency.
+	 */
+	return DatumGetInt32(DirectFunctionCall1Coll(hashtext, C_COLLATION_OID, datum));
 }
 
 static bool
-- 
2.43.0

