diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 91dbde63419e59c9af574fad32bc5336113740c1..a319e2c2b37c678624d6b66f167765eec752c282 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.67 2000/01/26 05:56:21 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.68 2000/02/20 21:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1176,7 +1176,7 @@ ExecEvalExpr(Node *expression,
 			 bool *isNull,
 			 bool *isDone)
 {
-	Datum		retDatum = 0;
+	Datum		retDatum;
 
 	*isNull = false;
 
@@ -1200,36 +1200,33 @@ ExecEvalExpr(Node *expression,
 	switch (nodeTag(expression))
 	{
 		case T_Var:
-			retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull);
+			retDatum = ExecEvalVar((Var *) expression, econtext, isNull);
 			break;
 		case T_Const:
 			{
 				Const	   *con = (Const *) expression;
 
-				if (con->constisnull)
-					*isNull = true;
 				retDatum = con->constvalue;
+				*isNull = con->constisnull;
 				break;
 			}
 		case T_Param:
-			retDatum = (Datum) ExecEvalParam((Param *) expression, econtext, isNull);
+			retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
 			break;
 		case T_Iter:
-			retDatum = (Datum) ExecEvalIter((Iter *) expression,
-											econtext,
-											isNull,
-											isDone);
+			retDatum = ExecEvalIter((Iter *) expression,
+									econtext,
+									isNull,
+									isDone);
 			break;
 		case T_Aggref:
-			retDatum = (Datum) ExecEvalAggref((Aggref *) expression,
-											  econtext,
-											  isNull);
+			retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
 			break;
 		case T_ArrayRef:
-			retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression,
-												econtext,
-												isNull,
-												isDone);
+			retDatum = ExecEvalArrayRef((ArrayRef *) expression,
+										econtext,
+										isNull,
+										isDone);
 			break;
 		case T_Expr:
 			{
@@ -1238,37 +1235,48 @@ ExecEvalExpr(Node *expression,
 				switch (expr->opType)
 				{
 					case OP_EXPR:
-						retDatum = (Datum) ExecEvalOper(expr, econtext, isNull);
+						retDatum = ExecEvalOper(expr, econtext, isNull);
 						break;
 					case FUNC_EXPR:
-						retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone);
+						retDatum = ExecEvalFunc(expr, econtext,
+												isNull, isDone);
 						break;
 					case OR_EXPR:
-						retDatum = (Datum) ExecEvalOr(expr, econtext, isNull);
+						retDatum = ExecEvalOr(expr, econtext, isNull);
 						break;
 					case AND_EXPR:
-						retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull);
+						retDatum = ExecEvalAnd(expr, econtext, isNull);
 						break;
 					case NOT_EXPR:
-						retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
+						retDatum = ExecEvalNot(expr, econtext, isNull);
 						break;
 					case SUBPLAN_EXPR:
-						retDatum = (Datum) ExecSubPlan((SubPlan *) expr->oper,
-													   expr->args, econtext,
-													   isNull);
+						retDatum = ExecSubPlan((SubPlan *) expr->oper,
+											   expr->args, econtext,
+											   isNull);
 						break;
 					default:
-						elog(ERROR, "ExecEvalExpr: unknown expression type %d", expr->opType);
+						elog(ERROR, "ExecEvalExpr: unknown expression type %d",
+							 expr->opType);
+						retDatum = 0; /* keep compiler quiet */
 						break;
 				}
 				break;
 			}
+		case T_RelabelType:
+			retDatum = ExecEvalExpr(((RelabelType *) expression)->arg,
+									econtext,
+									isNull,
+									isDone);
+			break;
 		case T_CaseExpr:
-			retDatum = (Datum) ExecEvalCase((CaseExpr *) expression, econtext, isNull);
+			retDatum = ExecEvalCase((CaseExpr *) expression, econtext, isNull);
 			break;
 
 		default:
-			elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression));
+			elog(ERROR, "ExecEvalExpr: unknown expression type %d",
+				 nodeTag(expression));
+			retDatum = 0;		/* keep compiler quiet */
 			break;
 	}
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5bf01e227228c4d55e9f93a0de964054d06b4c58..fbef91b35d8006b8d28739c65811b0ea5f9faa49 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.106 2000/02/15 20:49:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.107 2000/02/20 21:32:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -873,6 +873,26 @@ _copySubLink(SubLink *from)
 	return newnode;
 }
 
+/* ----------------
+ *		_copyRelabelType
+ * ----------------
+ */
+static RelabelType *
+_copyRelabelType(RelabelType *from)
+{
+	RelabelType    *newnode = makeNode(RelabelType);
+
+	/* ----------------
+	 *	copy remainder of node
+	 * ----------------
+	 */
+	Node_Copy(from, newnode, arg);
+	newnode->resulttype = from->resulttype;
+	newnode->resulttypmod = from->resulttypmod;
+
+	return newnode;
+}
+
 /* ----------------
  *		_copyCaseExpr
  * ----------------
@@ -1617,6 +1637,9 @@ copyObject(void *from)
 		case T_SubLink:
 			retval = _copySubLink(from);
 			break;
+		case T_RelabelType:
+			retval = _copyRelabelType(from);
+			break;
 		case T_CaseExpr:
 			retval = _copyCaseExpr(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fadc282d1add86fcb5aa8255e80f65cf443af725..b4f5fc6285c2ba77e296d151014bd358350f1c6c 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.61 2000/02/15 20:49:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.62 2000/02/20 21:32:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -262,6 +262,18 @@ _equalSubLink(SubLink *a, SubLink *b)
 	return true;
 }
 
+static bool
+_equalRelabelType(RelabelType *a, RelabelType *b)
+{
+	if (!equal(a->arg, b->arg))
+		return false;
+	if (a->resulttype != b->resulttype)
+		return false;
+	if (a->resulttypmod != b->resulttypmod)
+		return false;
+	return true;
+}
+
 static bool
 _equalArray(Array *a, Array *b)
 {
@@ -806,6 +818,9 @@ equal(void *a, void *b)
 		case T_SubLink:
 			retval = _equalSubLink(a, b);
 			break;
+		case T_RelabelType:
+			retval = _equalRelabelType(a, b);
+			break;
 		case T_Func:
 			retval = _equalFunc(a, b);
 			break;
diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c
index 8eed80e61ab42d4835136ac4259514d0424b0936..daca4a6d96a18230f27010f93b76434ed531edc2 100644
--- a/src/backend/nodes/freefuncs.c
+++ b/src/backend/nodes/freefuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.36 2000/02/15 20:49:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.37 2000/02/20 21:32:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -650,6 +650,22 @@ _freeSubLink(SubLink *node)
 	pfree(node);
 }
 
+/* ----------------
+ *		_freeRelabelType
+ * ----------------
+ */
+static void
+_freeRelabelType(RelabelType *node)
+{
+	/* ----------------
+	 *	free remainder of node
+	 * ----------------
+	 */
+	freeObject(node->arg);
+
+	pfree(node);
+}
+
 /* ----------------
  *		_freeCaseExpr
  * ----------------
@@ -1241,6 +1257,9 @@ freeObject(void *node)
 		case T_SubLink:
 			_freeSubLink(node);
 			break;
+		case T_RelabelType:
+			_freeRelabelType(node);
+			break;
 		case T_CaseExpr:
 			_freeCaseExpr(node);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c40ca9ff9cbdf1e1a8afc969dbbde086477824cc..db785afab9fa9ea4a91ef279d32eb18f3a29c815 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.108 2000/02/15 20:49:09 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.109 2000/02/20 21:32:05 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -770,6 +770,19 @@ _outSubLink(StringInfo str, SubLink *node)
 	_outNode(str, node->subselect);
 }
 
+/*
+ *	RelabelType
+ */
+static void
+_outRelabelType(StringInfo str, RelabelType *node)
+{
+	appendStringInfo(str, " RELABELTYPE :arg ");
+	_outNode(str, node->arg);
+
+	appendStringInfo(str, " :resulttype %u :resulttypmod %d ",
+					 node->resulttype, node->resulttypmod);
+}
+
 /*
  *	Array is a subclass of Expr
  */
@@ -1496,6 +1509,9 @@ _outNode(StringInfo str, void *obj)
 			case T_SubLink:
 				_outSubLink(str, obj);
 				break;
+			case T_RelabelType:
+				_outRelabelType(str, obj);
+				break;
 			case T_Array:
 				_outArray(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 7d1e0b4cccf22ca551325c917bd9dce570baf099..dfbdbef9884f16d05d06db1e81588772ce2170b5 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.84 2000/02/15 20:49:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.85 2000/02/20 21:32:05 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -1191,6 +1191,35 @@ _readSubLink()
 	return local_node;
 }
 
+/* ----------------
+ *		_readRelabelType
+ *
+ *	RelabelType is a subclass of Node
+ * ----------------
+ */
+static RelabelType *
+_readRelabelType()
+{
+	RelabelType *local_node;
+	char	   *token;
+	int			length;
+
+	local_node = makeNode(RelabelType);
+
+	token = lsptok(NULL, &length);		/* eat :arg */
+	local_node->arg = nodeRead(true);	/* now read it */
+
+	token = lsptok(NULL, &length);		/* eat :resulttype */
+	token = lsptok(NULL, &length);		/* get resulttype */
+	local_node->resulttype = (Oid) atol(token);
+
+	token = lsptok(NULL, &length);		/* eat :resulttypmod */
+	token = lsptok(NULL, &length);		/* get resulttypmod */
+	local_node->resulttypmod = atoi(token);
+
+	return local_node;
+}
+
 /*
  *	Stuff from execnodes.h
  */
@@ -1820,6 +1849,8 @@ parsePlanString(void)
 		return_value = _readAggref();
 	else if (length == 7 && strncmp(token, "SUBLINK", length) == 0)
 		return_value = _readSubLink();
+	else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0)
+		return_value = _readRelabelType();
 	else if (length == 3 && strncmp(token, "AGG", length) == 0)
 		return_value = _readAgg();
 	else if (length == 4 && strncmp(token, "HASH", length) == 0)
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 664a0dfaaa57e2c652e48ec7506de7756239a8f7..4bb84f1521463375055c07da42b45ec52e4fe20b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.59 2000/02/15 03:37:36 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.60 2000/02/20 21:32:06 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1150,6 +1150,37 @@ eval_const_expressions_mutator (Node *node, void *context)
         newexpr->args = args;
         return (Node *) newexpr;
 	}
+	if (IsA(node, RelabelType))
+	{
+		/*
+		 * If we can simplify the input to a constant, then we don't need
+		 * the RelabelType node anymore: just change the type field of
+		 * the Const node.  Otherwise keep the RelabelType node.
+		 *
+		 * XXX if relabel has a nondefault resulttypmod, do we need to
+		 * keep it to show that?  At present I don't think so.
+		 */
+		RelabelType *relabel = (RelabelType *) node;
+		Node	   *arg;
+
+		arg = eval_const_expressions_mutator(relabel->arg, context);
+		if (arg && IsA(arg, Const))
+		{
+			Const  *con = (Const *) arg;
+
+			con->consttype = relabel->resulttype;
+			return (Node *) con;
+		}
+		else
+		{
+			RelabelType *newrelabel = makeNode(RelabelType);
+
+			newrelabel->arg = arg;
+			newrelabel->resulttype = relabel->resulttype;
+			newrelabel->resulttypmod = relabel->resulttypmod;
+			return (Node *) newrelabel;
+		}
+	}
 	if (IsA(node, CaseExpr))
 	{
 		/*
@@ -1392,6 +1423,8 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
 					return true;
 			}
 			break;
+		case T_RelabelType:
+			return walker(((RelabelType *) node)->arg, context);
 		case T_CaseExpr:
 			{
 				CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -1603,6 +1636,16 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
 				return (Node *) newnode;
 			}
 			break;
+		case T_RelabelType:
+			{
+				RelabelType *relabel = (RelabelType *) node;
+				RelabelType *newnode;
+
+				FLATCOPY(newnode, relabel, RelabelType);
+				MUTATE(newnode->arg, relabel->arg, Node *);
+				return (Node *) newnode;
+			}
+			break;
 		case T_CaseExpr:
 			{
 				CaseExpr   *caseexpr = (CaseExpr *) node;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index dbb6cbc581627450a1540c19d7c90aa724f569b6..646f1a046b324a9cb74a2491cf6a0f414ce680d0 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * parse_coerce.c
- *		handle type coersions/conversions for parser
+ *		handle type coercions/conversions for parser
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.31 2000/02/20 06:28:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.32 2000/02/20 21:32:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,7 @@ Node *
 coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 			Oid targetTypeId, int32 atttypmod)
 {
-	Node	   *result = NULL;
+	Node	   *result;
 
 	if (targetTypeId == InvalidOid ||
 		targetTypeId == inputTypeId)
@@ -44,11 +44,6 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 		/* no conversion needed */
 		result = node;
 	}
-	else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
-	{
-		/* no work if one of the known-good transparent conversions */
-		result = node;
-	}
 	else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
 	{
 		/*
@@ -87,6 +82,27 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 
 		result = (Node *) newcon;
 	}
+	else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
+	{
+		/*
+		 * We don't really need to do a conversion, but we do need to attach
+		 * a RelabelType node so that the expression will be seen to have
+		 * the intended type when inspected by higher-level code.
+		 */
+		RelabelType *relabel = makeNode(RelabelType);
+
+		relabel->arg = node;
+		relabel->resulttype = targetTypeId;
+		/*
+		 * XXX could we label result with exprTypmod(node) instead of
+		 * default -1 typmod, to save a possible length-coercion later?
+		 * Would work if both types have same interpretation of typmod,
+		 * which is likely but not certain.
+		 */
+		relabel->resulttypmod = -1;
+
+		result = (Node *) relabel;
+	}
 	else
 	{
 		/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 365378f40720180f62627c1adf36d250246d44d9..3fd3370672ff40db0af42520cb4f3360c516fb92 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.68 2000/02/15 03:37:47 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.69 2000/02/20 21:32:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -516,6 +516,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 		case T_Param:
 		case T_Aggref:
 		case T_ArrayRef:
+		case T_RelabelType:
 			{
 				result = (Node *) expr;
 				break;
@@ -627,6 +628,9 @@ exprType(Node *expr)
 		case T_Param:
 			type = ((Param *) expr)->paramtype;
 			break;
+		case T_RelabelType:
+			type = ((RelabelType *) expr)->resulttype;
+			break;
 		case T_SubLink:
 			{
 				SubLink    *sublink = (SubLink *) expr;
@@ -697,6 +701,9 @@ exprTypmod(Node *expr)
 				}
 			}
 			break;
+		case T_RelabelType:
+			return ((RelabelType *) expr)->resulttypmod;
+			break;
 		default:
 			break;
 	}
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 939d7a91ec3c220d0aa04b00ed02ce50e0285bae..71cda760874d7ac40ac481ddaf93a8b922101d9e 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.70 2000/02/20 06:35:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.71 2000/02/20 21:32:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -163,7 +163,10 @@ agg_get_candidates(char *aggname,
 }	/* agg_get_candidates() */
 
 /* agg_select_candidate()
- * Try to choose only one candidate aggregate function from a list of possibles.
+ *
+ * Try to choose only one candidate aggregate function from a list of
+ * possible matches.  Return value is Oid of input type of aggregate
+ * if successful, else InvalidOid.
  */
 static Oid
 agg_select_candidate(Oid typeid, CandidateList candidates)
@@ -175,10 +178,12 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
 	CATEGORY	category,
 				current_category;
 
-/*
- * First look for exact matches or binary compatible matches.
- * (Of course exact matches shouldn't even get here, but anyway.)
- */
+	/*
+	 * First look for exact matches or binary compatible matches.
+	 * (Of course exact matches shouldn't even get here, but anyway.)
+	 */
+	ncandidates = 0;
+	last_candidate = NULL;
 	for (current_candidate = candidates;
 		 current_candidate != NULL;
 		 current_candidate = current_candidate->next)
@@ -188,15 +193,17 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
 		if (current_typeid == typeid
             || IS_BINARY_COMPATIBLE(current_typeid, typeid))
 		{
-            /* we're home free */
-            return current_typeid;
+			last_candidate = current_candidate;
+			ncandidates++;
         }
     }
+	if (ncandidates == 1)
+		return last_candidate->args[0];
 
-/*
- * If no luck that way, look for candidates which allow coersion
- * and have a preferred type. Keep all candidates if none match.
- */
+	/*
+	 * If no luck that way, look for candidates which allow coercion
+	 * and have a preferred type. Keep all candidates if none match.
+	 */
 	category = TypeCategory(typeid);
 	ncandidates = 0;
 	last_candidate = NULL;
@@ -232,7 +239,10 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
 	if (last_candidate)			/* terminate rebuilt list */
 		last_candidate->next = NULL;
 
-	return ((ncandidates == 1) ? candidates->args[0] : 0);
+	if (ncandidates == 1)
+		return candidates->args[0];
+
+	return InvalidOid;
 }	/* agg_select_candidate() */
 
 
@@ -471,10 +481,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 	/*
 	 * See if this is a single argument function with the function
 	 * name also a type name and the input argument and type name
-	 * binary compatible... This means that you are trying for a
-	 * type conversion which does not need to take place, so we'll
-	 * just pass through the argument itself. (make this clearer
-	 * with some extra brackets - thomas 1998-12-05)
+	 * binary compatible.  If so, we do not need to do any real
+	 * conversion, but we do need to build a RelabelType node
+	 * so that exprType() sees the result as being of the output type.
 	 */
 	if (nargs == 1)
 	{
@@ -486,8 +495,13 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 		if (HeapTupleIsValid(tp) &&
 			IS_BINARY_COMPATIBLE(typeTypeId(tp), exprType(lfirst(fargs))))
 		{
-			/* XXX FIXME: probably need to change expression's marked type? */
-			return (Node *) lfirst(fargs);
+			RelabelType *relabel = makeNode(RelabelType);
+
+			relabel->arg = (Node *) lfirst(fargs);
+			relabel->resulttype = typeTypeId(tp);
+			relabel->resulttypmod = -1;
+
+			return (Node *) relabel;
 		}
 	}
 
@@ -1128,7 +1142,7 @@ func_get_detail(char *funcname,
  *						 inheritance properties of the supplied argv.
  *
  *		This function is used to disambiguate among functions with the
- *		same name but different signatures.  It takes an array of eight
+ *		same name but different signatures.  It takes an array of input
  *		type ids.  For each type id in the array that's a complex type
  *		(a class), it walks up the inheritance tree, finding all
  *		superclasses of that type.	A vector of new Oid type arrays
@@ -1342,7 +1356,7 @@ gen_cross_product(InhPaths *arginh, int nargs)
  *	2) the input type can be typecast into the function type
  * Right now, we only typecast unknowns, and that is all we check for.
  *
- * func_get_detail() now can find coersions for function arguments which
+ * func_get_detail() now can find coercions for function arguments which
  *	will make this function executable. So, we need to recover these
  *	results here too.
  * - thomas 1998-03-25
@@ -1361,7 +1375,7 @@ make_arguments(ParseState *pstate,
 		 i < nargs;
 		 i++, current_fargs = lnext(current_fargs))
 	{
-		/* types don't match? then force coersion using a function call... */
+		/* types don't match? then force coercion using a function call... */
 		if (input_typeids[i] != function_typeids[i])
 		{
 			lfirst(current_fargs) = coerce_type(pstate,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index d18cff2789cf62a1d94554dab2da69d708f15623..0267fc2c576f804a50c1eb09ee24d7ccc18e3ac6 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *			  out of its tuple
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.41 2000/02/15 08:24:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.42 2000/02/20 21:32:12 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -1006,7 +1006,7 @@ get_select_query_def(Query *query, deparse_context *context)
 						appendStringInfo(buf, "%s",
 										 quote_identifier(strVal(lfirst(col))));
 					}
-					appendStringInfo(buf, ")");
+					appendStringInfoChar(buf, ')');
 				}
 			}
 		}
@@ -1127,7 +1127,7 @@ get_insert_query_def(Query *query, deparse_context *context)
 			sep = ", ";
 			get_tle_expr(tle, context);
 		}
-		appendStringInfo(buf, ")");
+		appendStringInfoChar(buf, ')');
 	}
 	else
 		get_select_query_def(query, context);
@@ -1281,7 +1281,7 @@ get_rule_expr(Node *node, deparse_context *context)
 				switch (expr->opType)
 				{
 					case OP_EXPR:
-						appendStringInfo(buf, "(");
+						appendStringInfoChar(buf, '(');
 						if (length(args) == 2)
 						{
 							/* binary operator */
@@ -1320,35 +1320,35 @@ get_rule_expr(Node *node, deparse_context *context)
 									elog(ERROR, "get_rule_expr: bogus oprkind");
 							}
 						}
-						appendStringInfo(buf, ")");
+						appendStringInfoChar(buf, ')');
 						break;
 
 					case OR_EXPR:
-						appendStringInfo(buf, "(");
+						appendStringInfoChar(buf, '(');
 						get_rule_expr((Node *) lfirst(args), context);
 						while ((args = lnext(args)) != NIL)
 						{
 							appendStringInfo(buf, " OR ");
 							get_rule_expr((Node *) lfirst(args), context);
 						}
-						appendStringInfo(buf, ")");
+						appendStringInfoChar(buf, ')');
 						break;
 
 					case AND_EXPR:
-						appendStringInfo(buf, "(");
+						appendStringInfoChar(buf, '(');
 						get_rule_expr((Node *) lfirst(args), context);
 						while ((args = lnext(args)) != NIL)
 						{
 							appendStringInfo(buf, " AND ");
 							get_rule_expr((Node *) lfirst(args), context);
 						}
-						appendStringInfo(buf, ")");
+						appendStringInfoChar(buf, ')');
 						break;
 
 					case NOT_EXPR:
 						appendStringInfo(buf, "(NOT ");
 						get_rule_expr((Node *) lfirst(args), context);
-						appendStringInfo(buf, ")");
+						appendStringInfoChar(buf, ')');
 						break;
 
 					case FUNC_EXPR:
@@ -1373,7 +1373,7 @@ get_rule_expr(Node *node, deparse_context *context)
 					appendStringInfo(buf, "*");
 				else
 					get_rule_expr(aggref->target, context);
-				appendStringInfo(buf, ")");
+				appendStringInfoChar(buf, ')');
 			}
 			break;
 
@@ -1405,6 +1405,28 @@ get_rule_expr(Node *node, deparse_context *context)
 			}
 			break;
 
+		case T_RelabelType:
+			{
+				RelabelType *relabel = (RelabelType *) node;
+				HeapTuple	typetup;
+				Form_pg_type typeStruct;
+				char	   *extval;
+
+				appendStringInfoChar(buf, '(');
+				get_rule_expr(relabel->arg, context);
+				typetup = SearchSysCacheTuple(TYPEOID,
+									ObjectIdGetDatum(relabel->resulttype),
+											  0, 0, 0);
+				if (!HeapTupleIsValid(typetup))
+					elog(ERROR, "cache lookup of type %u failed",
+						 relabel->resulttype);
+				typeStruct = (Form_pg_type) GETSTRUCT(typetup);
+				extval = pstrdup(NameStr(typeStruct->typname));
+				appendStringInfo(buf, ")::%s", quote_identifier(extval));
+				pfree(extval);
+			}
+			break;
+
 		case T_CaseExpr:
 			{
 				CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -1474,14 +1496,14 @@ get_func_expr(Expr *expr, deparse_context *context)
 	{
 		if (!strcmp(proname, "nullvalue"))
 		{
-			appendStringInfo(buf, "(");
+			appendStringInfoChar(buf, '(');
 			get_rule_expr((Node *) lfirst(expr->args), context);
 			appendStringInfo(buf, " ISNULL)");
 			return;
 		}
 		if (!strcmp(proname, "nonnullvalue"))
 		{
-			appendStringInfo(buf, "(");
+			appendStringInfoChar(buf, '(');
 			get_rule_expr((Node *) lfirst(expr->args), context);
 			appendStringInfo(buf, " NOTNULL)");
 			return;
@@ -1500,7 +1522,7 @@ get_func_expr(Expr *expr, deparse_context *context)
 		sep = ", ";
 		get_rule_expr((Node *) lfirst(l), context);
 	}
-	appendStringInfo(buf, ")");
+	appendStringInfoChar(buf, ')');
 }
 
 
@@ -1712,13 +1734,13 @@ get_sublink_expr(Node *node, deparse_context *context)
 	Oper	   *oper;
 	bool		need_paren;
 
-	appendStringInfo(buf, "(");
+	appendStringInfoChar(buf, '(');
 
 	if (sublink->lefthand != NIL)
 	{
 		need_paren = (length(sublink->lefthand) > 1);
 		if (need_paren)
-			appendStringInfo(buf, "(");
+			appendStringInfoChar(buf, '(');
 
 		sep = "";
 		foreach(l, sublink->lefthand)
@@ -1731,7 +1753,7 @@ get_sublink_expr(Node *node, deparse_context *context)
 		if (need_paren)
 			appendStringInfo(buf, ") ");
 		else
-			appendStringInfo(buf, " ");
+			appendStringInfoChar(buf, ' ');
 	}
 
 	need_paren = true;
@@ -1768,14 +1790,14 @@ get_sublink_expr(Node *node, deparse_context *context)
 	}
 
 	if (need_paren)
-		appendStringInfo(buf, "(");
+		appendStringInfoChar(buf, '(');
 
 	get_query_def(query, buf, context->rangetables);
 
 	if (need_paren)
 		appendStringInfo(buf, "))");
 	else
-		appendStringInfo(buf, ")");
+		appendStringInfoChar(buf, ')');
 }
 
 /* ----------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 8dd893e3f13cc2ef79b260f6a132d27ca4dec2d1..f42f615c7a6603e499bd2ba42dc9d5c2c4f0ae66 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.65 2000/02/18 09:29:43 inoue Exp $
+ * $Id: nodes.h,v 1.66 2000/02/20 21:32:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,6 +67,7 @@ typedef enum NodeTag
 	T_Array,
 	T_ArrayRef,
 	T_Iter,
+	T_RelabelType,
 
 	/*---------------------
 	 * TAGS FOR PLANNER NODES (relation.h)
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index bdeff737b63516b091a4409ef010e8d323756e89..7c3e0c6c4b843ba5585a21599f852ce2fb9b8e00 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.39 2000/01/26 05:58:16 momjian Exp $
+ * $Id: primnodes.h,v 1.40 2000/02/20 21:32:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -467,4 +467,28 @@ typedef struct ArrayRef
 	Node	   *refassgnexpr;
 } ArrayRef;
 
+/* ----------------
+ * RelabelType
+ *		arg				- input expression
+ *		resulttype		- output type of coercion expression
+ *		resulttypmod	- output typmod (usually -1)
+ *
+ * RelabelType represents a "dummy" type coercion between two binary-
+ * compatible datatypes, such as reinterpreting the result of an OID
+ * expression as an int4.  It is a no-op at runtime; we only need it
+ * to provide a place to store the correct type to be attributed to
+ * the expression result during type resolution.  (We can't get away
+ * with just overwriting the type field of the input expression node,
+ * so we need a separate node to show the coercion's result type.)
+ * ----------------
+ */
+
+typedef struct RelabelType
+{
+	NodeTag		type;
+	Node	   *arg;
+	Oid			resulttype;
+	int32		resulttypmod;
+} RelabelType;
+
 #endif	 /* PRIMNODES_H */