From 7208fae18f1fdb242b4fcced77a3b836e15ac3ec Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 26 Mar 2011 18:28:40 -0400
Subject: [PATCH] Clean up cruft around collation initialization for tupdescs
 and scankeys.

I found actual bugs in GiST and plpgsql; the rest of this is cosmetic
but meant to decrease the odds of future bugs of omission.
---
 src/backend/access/common/scankey.c    | 25 +++++++++---------
 src/backend/access/common/tupdesc.c    | 10 ++++---
 src/backend/access/gin/ginutil.c       |  2 ++
 src/backend/access/gist/gistscan.c     | 10 +++++--
 src/backend/access/nbtree/nbtsearch.c  |  6 ++---
 src/backend/access/nbtree/nbtutils.c   |  8 ++++--
 src/backend/commands/seclabel.c        |  3 ---
 src/backend/commands/sequence.c        | 15 +++++++----
 src/backend/executor/nodeIndexscan.c   | 10 +++----
 src/backend/executor/nodeMergeAppend.c | 30 +++++++++++----------
 src/backend/utils/adt/pgstatfuncs.c    | 36 +++++++++++++++++---------
 src/backend/utils/adt/selfuncs.c       |  1 +
 src/backend/utils/cache/catcache.c     |  2 ++
 src/backend/utils/sort/tuplesort.c     | 31 +++++++++++-----------
 src/include/access/skey.h              |  7 +++--
 src/pl/plpgsql/src/pl_comp.c           |  4 +++
 16 files changed, 119 insertions(+), 81 deletions(-)

diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c
index 41cd36fce92..b632408da47 100644
--- a/src/backend/access/common/scankey.c
+++ b/src/backend/access/common/scankey.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/skey.h"
+#include "catalog/pg_collation.h"
 
 
 /*
@@ -33,6 +34,7 @@ ScanKeyEntryInitialize(ScanKey entry,
 					   AttrNumber attributeNumber,
 					   StrategyNumber strategy,
 					   Oid subtype,
+					   Oid collation,
 					   RegProcedure procedure,
 					   Datum argument)
 {
@@ -42,7 +44,10 @@ ScanKeyEntryInitialize(ScanKey entry,
 	entry->sk_subtype = subtype;
 	entry->sk_argument = argument;
 	if (RegProcedureIsValid(procedure))
+	{
 		fmgr_info(procedure, &entry->sk_func);
+		entry->sk_func.fn_collation = collation;
+	}
 	else
 	{
 		Assert(flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL));
@@ -53,12 +58,16 @@ ScanKeyEntryInitialize(ScanKey entry,
 /*
  * ScanKeyInit
  *		Shorthand version of ScanKeyEntryInitialize: flags and subtype
- *		are assumed to be zero (the usual value).
+ *		are assumed to be zero (the usual value), and collation is defaulted.
  *
  * This is the recommended version for hardwired lookups in system catalogs.
  * It cannot handle NULL arguments, unary operators, or nondefault operators,
  * but we need none of those features for most hardwired lookups.
  *
+ * We set collation to DEFAULT_COLLATION_OID always.  This is appropriate
+ * for textual columns in system catalogs, and it will be ignored for
+ * non-textual columns, so it's not worth trying to be more finicky.
+ *
  * Note: CurrentMemoryContext at call should be as long-lived as the ScanKey
  * itself, because that's what will be used for any subsidiary info attached
  * to the ScanKey's FmgrInfo record.
@@ -76,6 +85,7 @@ ScanKeyInit(ScanKey entry,
 	entry->sk_subtype = InvalidOid;
 	entry->sk_argument = argument;
 	fmgr_info(procedure, &entry->sk_func);
+	entry->sk_func.fn_collation = DEFAULT_COLLATION_OID;
 }
 
 /*
@@ -93,6 +103,7 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry,
 							   AttrNumber attributeNumber,
 							   StrategyNumber strategy,
 							   Oid subtype,
+							   Oid collation,
 							   FmgrInfo *finfo,
 							   Datum argument)
 {
@@ -102,17 +113,5 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry,
 	entry->sk_subtype = subtype;
 	entry->sk_argument = argument;
 	fmgr_info_copy(&entry->sk_func, finfo, CurrentMemoryContext);
-}
-
-/*
- * ScanKeyEntryInitializeCollation
- *
- * Initialize the collation of a scan key.  This is just a notational
- * convenience and small abstraction.
- */
-void
-ScanKeyEntryInitializeCollation(ScanKey entry,
-								Oid collation)
-{
 	entry->sk_func.fn_collation = collation;
 }
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index d78b08381e0..c06a0271ca5 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -427,6 +427,10 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
  * TupleDescInitEntry
  *		This function initializes a single attribute structure in
  *		a previously allocated tuple descriptor.
+ *
+ * Note that attcollation is set to the default for the specified datatype.
+ * If a nondefault collation is needed, insert it afterwards using
+ * TupleDescInitEntryCollation.
  */
 void
 TupleDescInitEntry(TupleDesc desc,
@@ -496,8 +500,8 @@ TupleDescInitEntry(TupleDesc desc,
 /*
  * TupleDescInitEntryCollation
  *
- * Fill in the collation for an attribute in a previously initialized
- * tuple descriptor.
+ * Assign a nondefault collation to a previously initialized tuple descriptor
+ * entry.
  */
 void
 TupleDescInitEntryCollation(TupleDesc desc,
@@ -571,9 +575,9 @@ BuildDescForRelation(List *schema)
 
 		TupleDescInitEntry(desc, attnum, attname,
 						   atttypid, atttypmod, attdim);
-		TupleDescInitEntryCollation(desc, attnum, attcollation);
 
 		/* Override TupleDescInitEntry's settings as requested */
+		TupleDescInitEntryCollation(desc, attnum, attcollation);
 		if (entry->storage)
 			desc->attrs[attnum - 1]->attstorage = entry->storage;
 
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 23965449df2..9c4473c449b 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -56,6 +56,8 @@ initGinState(GinState *state, Relation index)
 							   origTupdesc->attrs[i]->atttypid,
 							   origTupdesc->attrs[i]->atttypmod,
 							   origTupdesc->attrs[i]->attndims);
+			TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2,
+										origTupdesc->attrs[i]->attcollation);
 		}
 
 		fmgr_info_copy(&(state->compareFn[i]),
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
index c5d32ef7481..0a125e772d0 100644
--- a/src/backend/access/gist/gistscan.c
+++ b/src/backend/access/gist/gistscan.c
@@ -168,7 +168,8 @@ gistrescan(PG_FUNCTION_ARGS)
 		 * all comparisons. The original operator is passed to the Consistent
 		 * function in the form of its strategy number, which is available
 		 * from the sk_strategy field, and its subtype from the sk_subtype
-		 * field.
+		 * field.  Also, preserve sk_func.fn_collation which is the input
+		 * collation for the operator.
 		 *
 		 * Next, if any of keys is a NULL and that key is not marked with
 		 * SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we
@@ -179,8 +180,10 @@ gistrescan(PG_FUNCTION_ARGS)
 		for (i = 0; i < scan->numberOfKeys; i++)
 		{
 			ScanKey		skey = scan->keyData + i;
+			Oid			collation = skey->sk_func.fn_collation;
 
 			skey->sk_func = so->giststate->consistentFn[skey->sk_attno - 1];
+			skey->sk_func.fn_collation = collation;
 
 			if (skey->sk_flags & SK_ISNULL)
 			{
@@ -201,13 +204,16 @@ gistrescan(PG_FUNCTION_ARGS)
 		 * all comparisons. The original operator is passed to the Distance
 		 * function in the form of its strategy number, which is available
 		 * from the sk_strategy field, and its subtype from the sk_subtype
-		 * field.
+		 * field.  Also, preserve sk_func.fn_collation which is the input
+		 * collation for the operator.
 		 */
 		for (i = 0; i < scan->numberOfOrderBys; i++)
 		{
 			ScanKey		skey = scan->orderByData + i;
+			Oid			collation = skey->sk_func.fn_collation;
 
 			skey->sk_func = so->giststate->distanceFn[skey->sk_attno - 1];
+			skey->sk_func.fn_collation = collation;
 
 			/* Check we actually have a distance function ... */
 			if (!OidIsValid(skey->sk_func.fn_oid))
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index be8d958352b..cb78a1bae16 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -721,10 +721,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
 											   cur->sk_attno,
 											   InvalidStrategy,
 											   cur->sk_subtype,
+											   cur->sk_func.fn_collation,
 											   procinfo,
 											   cur->sk_argument);
-				ScanKeyEntryInitializeCollation(scankeys + i,
-												cur->sk_func.fn_collation);
 			}
 			else
 			{
@@ -743,10 +742,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
 									   cur->sk_attno,
 									   InvalidStrategy,
 									   cur->sk_subtype,
+									   cur->sk_func.fn_collation,
 									   cmp_proc,
 									   cur->sk_argument);
-				ScanKeyEntryInitializeCollation(scankeys + i,
-												cur->sk_func.fn_collation);
 			}
 		}
 	}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 7ee7ebeb3cb..add932d9428 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -70,7 +70,8 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 
 		/*
 		 * We can use the cached (default) support procs since no cross-type
-		 * comparison can be needed.
+		 * comparison can be needed.  The cached support proc entries have
+		 * the right collation for the index, too.
 		 */
 		procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
 		arg = index_getattr(itup, i + 1, itupdesc, &null);
@@ -80,6 +81,7 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 									   (AttrNumber) (i + 1),
 									   InvalidStrategy,
 									   InvalidOid,
+									   procinfo->fn_collation,
 									   procinfo,
 									   arg);
 	}
@@ -118,7 +120,8 @@ _bt_mkscankey_nodata(Relation rel)
 
 		/*
 		 * We can use the cached (default) support procs since no cross-type
-		 * comparison can be needed.
+		 * comparison can be needed.  The cached support proc entries have
+		 * the right collation for the index, too.
 		 */
 		procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
 		flags = SK_ISNULL | (indoption[i] << SK_BT_INDOPTION_SHIFT);
@@ -127,6 +130,7 @@ _bt_mkscankey_nodata(Relation rel)
 									   (AttrNumber) (i + 1),
 									   InvalidStrategy,
 									   InvalidOid,
+									   procinfo->fn_collation,
 									   procinfo,
 									   (Datum) 0);
 	}
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 050792f4a06..1c96b005d7f 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -15,7 +15,6 @@
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
-#include "catalog/pg_collation.h"
 #include "catalog/pg_seclabel.h"
 #include "commands/seclabel.h"
 #include "miscadmin.h"
@@ -166,7 +165,6 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider)
 				Anum_pg_seclabel_provider,
 				BTEqualStrategyNumber, F_TEXTEQ,
 				CStringGetTextDatum(provider));
-	ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
 
 	pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
 
@@ -236,7 +234,6 @@ SetSecurityLabel(const ObjectAddress *object,
 				Anum_pg_seclabel_provider,
 				BTEqualStrategyNumber, F_TEXTEQ,
 				CStringGetTextDatum(provider));
-	ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
 
 	pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
 
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 5c6212c64cd..bfa94a0c114 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1455,11 +1455,16 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 						RelationGetRelationName(seqrel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", INT8OID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "maximum_value", INT8OID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "increment", INT8OID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
+					   INT8OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value",
+					   INT8OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "maximum_value",
+					   INT8OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "increment",
+					   INT8OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option",
+					   BOOLOID, -1, 0);
 
 	BlessTupleDesc(tupdesc);
 
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index e60db7813a4..3b8741fc21b 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -829,10 +829,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
 								   varattno,	/* attribute number to scan */
 								   op_strategy, /* op's strategy */
 								   op_righttype,		/* strategy subtype */
+								   ((OpExpr *) clause)->inputcollid,	/* collation */
 								   opfuncid,	/* reg proc to use */
 								   scanvalue);	/* constant */
-			ScanKeyEntryInitializeCollation(this_scan_key,
-											((OpExpr *) clause)->inputcollid);
 		}
 		else if (IsA(clause, RowCompareExpr))
 		{
@@ -957,10 +956,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
 									   varattno,		/* attribute number */
 									   op_strategy,		/* op's strategy */
 									   op_righttype,	/* strategy subtype */
+									   inputcollation,	/* collation */
 									   opfuncid,		/* reg proc to use */
 									   scanvalue);		/* constant */
-				ScanKeyEntryInitializeCollation(this_sub_key,
-												inputcollation);
 				n_sub_key++;
 			}
 
@@ -1042,10 +1040,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
 								   varattno,	/* attribute number to scan */
 								   op_strategy, /* op's strategy */
 								   op_righttype,		/* strategy subtype */
+								   saop->inputcollid,	/* collation */
 								   opfuncid,	/* reg proc to use */
 								   (Datum) 0);	/* constant */
-			ScanKeyEntryInitializeCollation(this_scan_key,
-											saop->inputcollid);
 		}
 		else if (IsA(clause, NullTest))
 		{
@@ -1094,6 +1091,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
 								   varattno,	/* attribute number to scan */
 								   InvalidStrategy,		/* no strategy */
 								   InvalidOid,	/* no strategy subtype */
+								   InvalidOid,	/* no collation */
 								   InvalidOid,	/* no reg proc for this */
 								   (Datum) 0);	/* constant */
 		}
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index e46af8cff93..73920f21c8c 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -134,30 +134,32 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
 	{
 		Oid		sortFunction;
 		bool	reverse;
+		int		flags;
 
 		if (!get_compare_function_for_ordering_op(node->sortOperators[i],
 												  &sortFunction, &reverse))
 			elog(ERROR, "operator %u is not a valid ordering operator",
 				 node->sortOperators[i]);
 
+		/* We use btree's conventions for encoding directionality */
+		flags = 0;
+		if (reverse)
+			flags |= SK_BT_DESC;
+		if (node->nullsFirst[i])
+			flags |= SK_BT_NULLS_FIRST;
+
 		/*
 		 * We needn't fill in sk_strategy or sk_subtype since these scankeys
 		 * will never be passed to an index.
 		 */
-		ScanKeyInit(&mergestate->ms_scankeys[i],
-					node->sortColIdx[i],
-					InvalidStrategy,
-					sortFunction,
-					(Datum) 0);
-
-		ScanKeyEntryInitializeCollation(&mergestate->ms_scankeys[i],
-										node->collations[i]);
-
-		/* However, we use btree's conventions for encoding directionality */
-		if (reverse)
-			mergestate->ms_scankeys[i].sk_flags |= SK_BT_DESC;
-		if (node->nullsFirst[i])
-			mergestate->ms_scankeys[i].sk_flags |= SK_BT_NULLS_FIRST;
+		ScanKeyEntryInitialize(&mergestate->ms_scankeys[i],
+							   flags,
+							   node->sortColIdx[i],
+							   InvalidStrategy,
+							   InvalidOid,
+							   node->collations[i],
+							   sortFunction,
+							   (Datum) 0);
 	}
 
 	/*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index eb9bca19877..137c811bc3a 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -507,18 +507,30 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
 		tupdesc = CreateTemplateTupleDesc(12, false);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name", TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "current_query", TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "waiting", BOOLOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "act_start", TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 8, "query_start", TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 9, "backend_start", TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_addr", INETOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 11, "client_hostname", TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_port", INT4OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
+						   OIDOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid",
+						   INT4OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid",
+						   OIDOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name",
+						   TEXTOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "current_query",
+						   TEXTOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "waiting",
+						   BOOLOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "act_start",
+						   TIMESTAMPTZOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 8, "query_start",
+						   TIMESTAMPTZOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 9, "backend_start",
+						   TIMESTAMPTZOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_addr",
+						   INETOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 11, "client_hostname",
+						   TEXTOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_port",
+						   INT4OID, -1, 0);
 
 		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
 
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c757fcb424f..f7358d198c5 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -4649,6 +4649,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 								   1,	/* index col to scan */
 								   InvalidStrategy,		/* no strategy */
 								   InvalidOid,	/* no strategy subtype */
+								   InvalidOid,	/* no collation */
 								   InvalidOid,	/* no reg proc for this */
 								   (Datum) 0);	/* constant */
 
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 2241cb91f29..5caa53d4d25 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -934,6 +934,8 @@ CatalogCacheInitializeCache(CatCache *cache)
 		/* Fill in sk_strategy as well --- always standard equality */
 		cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
 		cache->cc_skey[i].sk_subtype = InvalidOid;
+		/* Currently, there are no catcaches on collation-aware data types */
+		cache->cc_skey[i].sk_func.fn_collation = InvalidOid;
 
 		CACHE4_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
 					cache->cc_relname,
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 56185fcabc7..a1850b83c5e 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -621,6 +621,7 @@ tuplesort_begin_heap(TupleDesc tupDesc,
 	{
 		Oid			sortFunction;
 		bool		reverse;
+		int			flags;
 
 		AssertArg(attNums[i] != 0);
 		AssertArg(sortOperators[i] != 0);
@@ -630,25 +631,25 @@ tuplesort_begin_heap(TupleDesc tupDesc,
 			elog(ERROR, "operator %u is not a valid ordering operator",
 				 sortOperators[i]);
 
+		/* We use btree's conventions for encoding directionality */
+		flags = 0;
+		if (reverse)
+			flags |= SK_BT_DESC;
+		if (nullsFirstFlags[i])
+			flags |= SK_BT_NULLS_FIRST;
+
 		/*
 		 * We needn't fill in sk_strategy or sk_subtype since these scankeys
 		 * will never be passed to an index.
 		 */
-		ScanKeyInit(&state->scanKeys[i],
-					attNums[i],
-					InvalidStrategy,
-					sortFunction,
-					(Datum) 0);
-
-		if (collations)
-			ScanKeyEntryInitializeCollation(&state->scanKeys[i],
-											collations[i]);
-
-		/* However, we use btree's conventions for encoding directionality */
-		if (reverse)
-			state->scanKeys[i].sk_flags |= SK_BT_DESC;
-		if (nullsFirstFlags[i])
-			state->scanKeys[i].sk_flags |= SK_BT_NULLS_FIRST;
+		ScanKeyEntryInitialize(&state->scanKeys[i],
+							   flags,
+							   attNums[i],
+							   InvalidStrategy,
+							   InvalidOid,
+							   collations ? collations[i] : InvalidOid,
+							   sortFunction,
+							   (Datum) 0);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
diff --git a/src/include/access/skey.h b/src/include/access/skey.h
index 3d2956c9353..1d0071ac2d3 100644
--- a/src/include/access/skey.h
+++ b/src/include/access/skey.h
@@ -52,6 +52,9 @@ typedef uint16 StrategyNumber;
  * the operator.  When using a ScanKey in a heap scan, these fields are not
  * used and may be set to InvalidStrategy/InvalidOid.
  *
+ * If the operator is collation-sensitive, sk_func.fn_collation must be set
+ * correctly as well.
+ *
  * A ScanKey can also represent a condition "column IS NULL" or "column
  * IS NOT NULL"; these cases are signaled by the SK_SEARCHNULL and
  * SK_SEARCHNOTNULL flag bits respectively.  The argument is always NULL,
@@ -143,6 +146,7 @@ extern void ScanKeyEntryInitialize(ScanKey entry,
 					   AttrNumber attributeNumber,
 					   StrategyNumber strategy,
 					   Oid subtype,
+					   Oid collation,
 					   RegProcedure procedure,
 					   Datum argument);
 extern void ScanKeyEntryInitializeWithInfo(ScanKey entry,
@@ -150,9 +154,8 @@ extern void ScanKeyEntryInitializeWithInfo(ScanKey entry,
 							   AttrNumber attributeNumber,
 							   StrategyNumber strategy,
 							   Oid subtype,
+							   Oid collation,
 							   FmgrInfo *finfo,
 							   Datum argument);
-extern void ScanKeyEntryInitializeCollation(ScanKey entry,
-											Oid collation);
 
 #endif   /* SKEY_H */
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 675b91d5306..c7ba4248c72 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -2011,12 +2011,14 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars)
 		PLpgSQL_variable *var = vars[i];
 		Oid			typoid = RECORDOID;
 		int32		typmod = -1;
+		Oid			typcoll = InvalidOid;
 
 		switch (var->dtype)
 		{
 			case PLPGSQL_DTYPE_VAR:
 				typoid = ((PLpgSQL_var *) var)->datatype->typoid;
 				typmod = ((PLpgSQL_var *) var)->datatype->atttypmod;
+				typcoll = ((PLpgSQL_var *) var)->datatype->collation;
 				break;
 
 			case PLPGSQL_DTYPE_REC:
@@ -2027,6 +2029,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars)
 				{
 					typoid = ((PLpgSQL_row *) var)->rowtupdesc->tdtypeid;
 					typmod = ((PLpgSQL_row *) var)->rowtupdesc->tdtypmod;
+					/* composite types have no collation */
 				}
 				break;
 
@@ -2041,6 +2044,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars)
 						   var->refname,
 						   typoid, typmod,
 						   0);
+		TupleDescInitEntryCollation(row->rowtupdesc, i + 1, typcoll);
 	}
 
 	return row;
-- 
GitLab