From b23c9fa9293c54a3829093d207be37a7b42cb630 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 26 Mar 2011 14:25:48 -0400
Subject: [PATCH] Clean up a few failures to set collation fields in expression
 nodes.

I'm not sure these have any non-cosmetic implications, but I'm not sure
they don't, either.  In particular, ensure the CaseTestExpr generated
by transformAssignmentIndirection to represent the base target column
carries the correct collation, because parse_collate.c won't fix that.
Tweak lsyscache.c API so that we can get the appropriate collation
without an extra syscache lookup.
---
 src/backend/optimizer/path/pathkeys.c   |  3 +-
 src/backend/optimizer/plan/createplan.c |  2 ++
 src/backend/optimizer/util/clauses.c    |  2 +-
 src/backend/optimizer/util/predtest.c   |  4 +++
 src/backend/parser/parse_coerce.c       |  2 ++
 src/backend/parser/parse_target.c       | 31 +++++++++++++++++---
 src/backend/utils/adt/ruleutils.c       |  6 ++--
 src/backend/utils/cache/lsyscache.c     | 38 ++++---------------------
 src/include/utils/lsyscache.h           |  5 ++--
 9 files changed, 49 insertions(+), 44 deletions(-)

diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 34c772356b..47597a5d35 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -603,8 +603,7 @@ find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
 
 	relid = rel->relid;
 	reloid = getrelid(relid, root->parse->rtable);
-	get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod);
-	varcollid = get_attcollation(reloid, varattno);
+	get_atttypetypmodcoll(reloid, varattno, &vartypeid, &type_mod, &varcollid);
 
 	return makeVar(relid, varattno, vartypeid, type_mod, varcollid, 0);
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index bdd14f524d..f130881251 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2652,6 +2652,8 @@ get_switched_clauses(List *clauses, Relids outerrelids)
 			temp->opfuncid = InvalidOid;
 			temp->opresulttype = clause->opresulttype;
 			temp->opretset = clause->opretset;
+			temp->opcollid = clause->opcollid;
+			temp->inputcollid = clause->inputcollid;
 			temp->args = list_copy(clause->args);
 			temp->location = clause->location;
 			/* Commute it --- note this modifies the temp node in-place. */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 1a63146e6f..b1069259f9 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1816,7 +1816,7 @@ CommuteOpExpr(OpExpr *clause)
 	 */
 	clause->opno = opoid;
 	clause->opfuncid = InvalidOid;
-	/* opresulttype and opretset are assumed not to change */
+	/* opresulttype, opretset, opcollid, inputcollid need not change */
 
 	temp = linitial(clause->args);
 	linitial(clause->args) = lsecond(clause->args);
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 6a1f7291ba..72fd3e4ca7 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -906,6 +906,8 @@ arrayconst_startup_fn(Node *clause, PredIterInfo info)
 	state->opexpr.opfuncid = saop->opfuncid;
 	state->opexpr.opresulttype = BOOLOID;
 	state->opexpr.opretset = false;
+	state->opexpr.opcollid = InvalidOid;
+	state->opexpr.inputcollid = saop->inputcollid;
 	state->opexpr.args = list_copy(saop->args);
 
 	/* Set up a dummy Const node to hold the per-element values */
@@ -972,6 +974,8 @@ arrayexpr_startup_fn(Node *clause, PredIterInfo info)
 	state->opexpr.opfuncid = saop->opfuncid;
 	state->opexpr.opresulttype = BOOLOID;
 	state->opexpr.opretset = false;
+	state->opexpr.opcollid = InvalidOid;
+	state->opexpr.inputcollid = saop->inputcollid;
 	state->opexpr.args = list_copy(saop->args);
 
 	/* Initialize iteration variable to first member of ArrayExpr */
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index cc03f9f48e..dd3e748c9f 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -796,6 +796,7 @@ build_coercion_expression(Node *node,
 		 * one argument.
 		 */
 		acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
+		/* resultcollid will be set by parse_collate.c */
 		acoerce->isExplicit = isExplicit;
 		acoerce->coerceformat = cformat;
 		acoerce->location = location;
@@ -811,6 +812,7 @@ build_coercion_expression(Node *node,
 
 		iocoerce->arg = (Expr *) node;
 		iocoerce->resulttype = targetTypeId;
+		/* resultcollid will be set by parse_collate.c */
 		iocoerce->coerceformat = cformat;
 		iocoerce->location = location;
 
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 8e92b99b5b..d53d1d9f00 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -40,6 +40,7 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
 							   bool targetIsArray,
 							   Oid targetTypeId,
 							   int32 targetTypMod,
+							   Oid targetCollation,
 							   ListCell *indirection,
 							   Node *rhs,
 							   int location);
@@ -48,6 +49,7 @@ static Node *transformAssignmentSubscripts(ParseState *pstate,
 							  const char *targetName,
 							  Oid targetTypeId,
 							  int32 targetTypMod,
+							  Oid targetCollation,
 							  List *subscripts,
 							  bool isSlice,
 							  ListCell *next_indirection,
@@ -455,6 +457,7 @@ transformAssignedExpr(ParseState *pstate,
 										   false,
 										   attrtype,
 										   attrtypmod,
+										   attrcollation,
 										   list_head(indirection),
 										   (Node *) expr,
 										   location);
@@ -548,8 +551,9 @@ updateTargetListEntry(ParseState *pstate,
  * targetIsArray is true if we're subscripting it.  These are just for
  * error reporting.
  *
- * targetTypeId and targetTypMod indicate the datatype of the object to
- * be assigned to (initially the target column, later some subobject).
+ * targetTypeId, targetTypMod, targetCollation indicate the datatype and
+ * collation of the object to be assigned to (initially the target column,
+ * later some subobject).
  *
  * indirection is the sublist remaining to process.  When it's NULL, we're
  * done recursing and can just coerce and return the RHS.
@@ -569,6 +573,7 @@ transformAssignmentIndirection(ParseState *pstate,
 							   bool targetIsArray,
 							   Oid targetTypeId,
 							   int32 targetTypMod,
+							   Oid targetCollation,
 							   ListCell *indirection,
 							   Node *rhs,
 							   int location)
@@ -585,6 +590,7 @@ transformAssignmentIndirection(ParseState *pstate,
 
 		ctest->typeId = targetTypeId;
 		ctest->typeMod = targetTypMod;
+		ctest->collation = targetCollation;
 		basenode = (Node *) ctest;
 	}
 
@@ -617,6 +623,7 @@ transformAssignmentIndirection(ParseState *pstate,
 			AttrNumber	attnum;
 			Oid			fieldTypeId;
 			int32		fieldTypMod;
+			Oid			fieldCollation;
 
 			Assert(IsA(n, String));
 
@@ -629,6 +636,7 @@ transformAssignmentIndirection(ParseState *pstate,
 													 targetName,
 													 targetTypeId,
 													 targetTypMod,
+													 targetCollation,
 													 subscripts,
 													 isSlice,
 													 i,
@@ -662,8 +670,8 @@ transformAssignmentIndirection(ParseState *pstate,
 								strVal(n)),
 						 parser_errposition(pstate, location)));
 
-			get_atttypetypmod(typrelid, attnum,
-							  &fieldTypeId, &fieldTypMod);
+			get_atttypetypmodcoll(typrelid, attnum,
+								  &fieldTypeId, &fieldTypMod, &fieldCollation);
 
 			/* recurse to create appropriate RHS for field assign */
 			rhs = transformAssignmentIndirection(pstate,
@@ -672,6 +680,7 @@ transformAssignmentIndirection(ParseState *pstate,
 												 false,
 												 fieldTypeId,
 												 fieldTypMod,
+												 fieldCollation,
 												 lnext(i),
 												 rhs,
 												 location);
@@ -696,6 +705,7 @@ transformAssignmentIndirection(ParseState *pstate,
 											 targetName,
 											 targetTypeId,
 											 targetTypMod,
+											 targetCollation,
 											 subscripts,
 											 isSlice,
 											 NULL,
@@ -747,6 +757,7 @@ transformAssignmentSubscripts(ParseState *pstate,
 							  const char *targetName,
 							  Oid targetTypeId,
 							  int32 targetTypMod,
+							  Oid targetCollation,
 							  List *subscripts,
 							  bool isSlice,
 							  ListCell *next_indirection,
@@ -758,6 +769,7 @@ transformAssignmentSubscripts(ParseState *pstate,
 	int32		arrayTypMod;
 	Oid			elementTypeId;
 	Oid			typeNeeded;
+	Oid			collationNeeded;
 
 	Assert(subscripts != NIL);
 
@@ -769,6 +781,16 @@ transformAssignmentSubscripts(ParseState *pstate,
 	/* Identify type that RHS must provide */
 	typeNeeded = isSlice ? arrayType : elementTypeId;
 
+	/*
+	 * Array normally has same collation as elements, but there's an
+	 * exception: we might be subscripting a domain over an array type.
+	 * In that case use collation of the base type.
+	 */
+	if (arrayType == targetTypeId)
+		collationNeeded = targetCollation;
+	else
+		collationNeeded = get_typcollation(arrayType);
+
 	/* recurse to create appropriate RHS for array assign */
 	rhs = transformAssignmentIndirection(pstate,
 										 NULL,
@@ -776,6 +798,7 @@ transformAssignmentSubscripts(ParseState *pstate,
 										 true,
 										 typeNeeded,
 										 arrayTypMod,
+										 collationNeeded,
 										 next_indirection,
 										 rhs,
 										 location);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 621f1eb24a..326079a75b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -912,12 +912,14 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 		{
 			/* Simple index column */
 			char	   *attname;
+			int32		keycoltypmod;
 
 			attname = get_relid_attribute_name(indrelid, attnum);
 			if (!colno || colno == keyno + 1)
 				appendStringInfoString(&buf, quote_identifier(attname));
-			keycoltype = get_atttype(indrelid, attnum);
-			keycolcollation = get_attcollation(indrelid, attnum);
+			get_atttypetypmodcoll(indrelid, attnum,
+								  &keycoltype, &keycoltypmod,
+								  &keycolcollation);
 		}
 		else
 		{
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 6bcaf30ffe..877e50d873 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -904,44 +904,17 @@ get_atttypmod(Oid relid, AttrNumber attnum)
 }
 
 /*
- * get_attcollation
+ * get_atttypetypmodcoll
  *
- *		Given the relation id and the attribute number,
- *		return the "attcollation" field from the attribute relation.
- */
-Oid
-get_attcollation(Oid relid, AttrNumber attnum)
-{
-	HeapTuple	tp;
-
-	tp = SearchSysCache2(ATTNUM,
-						 ObjectIdGetDatum(relid),
-						 Int16GetDatum(attnum));
-	if (HeapTupleIsValid(tp))
-	{
-		Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
-		Oid		result;
-
-		result = att_tup->attcollation;
-		ReleaseSysCache(tp);
-		return result;
-	}
-	else
-		return InvalidOid;
-}
-
-/*
- * get_atttypetypmod
- *
- *		A two-fer: given the relation id and the attribute number,
- *		fetch both type OID and atttypmod in a single cache lookup.
+ *		A three-fer: given the relation id and the attribute number,
+ *		fetch atttypid, atttypmod, and attcollation in a single cache lookup.
  *
  * Unlike the otherwise-similar get_atttype/get_atttypmod, this routine
  * raises an error if it can't obtain the information.
  */
 void
-get_atttypetypmod(Oid relid, AttrNumber attnum,
-				  Oid *typid, int32 *typmod)
+get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
+					  Oid *typid, int32 *typmod, Oid *collid)
 {
 	HeapTuple	tp;
 	Form_pg_attribute att_tup;
@@ -956,6 +929,7 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
 
 	*typid = att_tup->atttypid;
 	*typmod = att_tup->atttypmod;
+	*collid = att_tup->attcollation;
 	ReleaseSysCache(tp);
 }
 
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 22dfd58f0a..b6ceb26c8b 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -60,9 +60,8 @@ extern char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
 extern AttrNumber get_attnum(Oid relid, const char *attname);
 extern Oid	get_atttype(Oid relid, AttrNumber attnum);
 extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
-extern Oid	get_attcollation(Oid relid, AttrNumber attnum);
-extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
-				  Oid *typid, int32 *typmod);
+extern void get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
+				  Oid *typid, int32 *typmod, Oid *collid);
 extern char *get_collation_name(Oid colloid);
 extern char *get_constraint_name(Oid conoid);
 extern Oid	get_opclass_family(Oid opclass);
-- 
2.50.0