diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fd7793d7a..4db0068da8 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.13 2001/01/24 19:42:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.14 2001/02/16 03:16:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -152,7 +152,6 @@ analyze_rel(Oid relid, List *anal_cols2, int MESSAGE_LEVEL)
 	for (i = 0; i < attr_cnt; i++)
 	{
 		Operator	func_operator;
-		Form_pg_operator pgopform;
 		VacAttrStats *stats;
 
 		stats = &vacattrstats[i];
@@ -167,21 +166,25 @@ analyze_rel(Oid relid, List *anal_cols2, int MESSAGE_LEVEL)
 		stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0;
 		stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0;
 
-		func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true);
+		func_operator = compatible_oper("=",
+										stats->attr->atttypid,
+										stats->attr->atttypid,
+										true);
 		if (func_operator != NULL)
 		{
-			pgopform = (Form_pg_operator) GETSTRUCT(func_operator);
-			fmgr_info(pgopform->oprcode, &(stats->f_cmpeq));
+			fmgr_info(oprfuncid(func_operator), &(stats->f_cmpeq));
 			ReleaseSysCache(func_operator);
 		}
 		else
 			stats->f_cmpeq.fn_addr = NULL;
 
-		func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true);
+		func_operator = compatible_oper("<",
+										stats->attr->atttypid,
+										stats->attr->atttypid,
+										true);
 		if (func_operator != NULL)
 		{
-			pgopform = (Form_pg_operator) GETSTRUCT(func_operator);
-			fmgr_info(pgopform->oprcode, &(stats->f_cmplt));
+			fmgr_info(oprfuncid(func_operator), &(stats->f_cmplt));
 			stats->op_cmplt = oprid(func_operator);
 			ReleaseSysCache(func_operator);
 		}
@@ -191,11 +194,13 @@ analyze_rel(Oid relid, List *anal_cols2, int MESSAGE_LEVEL)
 			stats->op_cmplt = InvalidOid;
 		}
 
-		func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true);
+		func_operator = compatible_oper(">",
+										stats->attr->atttypid,
+										stats->attr->atttypid,
+										true);
 		if (func_operator != NULL)
 		{
-			pgopform = (Form_pg_operator) GETSTRUCT(func_operator);
-			fmgr_info(pgopform->oprcode, &(stats->f_cmpgt));
+			fmgr_info(oprfuncid(func_operator), &(stats->f_cmpgt));
 			ReleaseSysCache(func_operator);
 		}
 		else
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index bfb2646b6a..f60f499e6b 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -46,7 +46,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.74 2001/02/15 21:47:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.75 2001/02/16 03:16:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -909,21 +909,19 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 			 * (Consider COUNT(*).)
 			 */
 			Oid			inputType = exprType(aggref->target);
-			Operator	eq_operator;
-			Form_pg_operator pgopform;
+			Oid			eq_function;
 
 			peraggstate->inputType = inputType;
 			get_typlenbyval(inputType,
 							&peraggstate->inputtypeLen,
 							&peraggstate->inputtypeByVal);
 
-			eq_operator = oper("=", inputType, inputType, true);
-			if (!HeapTupleIsValid(eq_operator))
+			eq_function = compatible_oper_funcid("=", inputType, inputType,
+												 true);
+			if (!OidIsValid(eq_function))
 				elog(ERROR, "Unable to identify an equality operator for type '%s'",
 					 typeidTypeName(inputType));
-			pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
-			fmgr_info(pgopform->oprcode, &(peraggstate->equalfn));
-			ReleaseSysCache(eq_operator);
+			fmgr_info(eq_function, &(peraggstate->equalfn));
 			peraggstate->sortOperator = any_ordering_op(inputType);
 			peraggstate->sortstate = NULL;
 		}
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index e98819a5d8..e4ede51852 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -15,7 +15,7 @@
  *	  locate group boundaries.
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.40 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.41 2001/02/16 03:16:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -495,16 +495,13 @@ execTuplesMatchPrepare(TupleDesc tupdesc,
 	{
 		AttrNumber	att = matchColIdx[i];
 		Oid			typid = tupdesc->attrs[att - 1]->atttypid;
-		Operator	eq_operator;
-		Form_pg_operator pgopform;
+		Oid			eq_function;
 
-		eq_operator = oper("=", typid, typid, true);
-		if (!HeapTupleIsValid(eq_operator))
+		eq_function = compatible_oper_funcid("=", typid, typid, true);
+		if (!OidIsValid(eq_function))
 			elog(ERROR, "Unable to identify an equality operator for type '%s'",
 				 typeidTypeName(typid));
-		pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
-		fmgr_info(pgopform->oprcode, &eqfunctions[i]);
-		ReleaseSysCache(eq_operator);
+		fmgr_info(eq_function, &eqfunctions[i]);
 	}
 
 	return eqfunctions;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index cc408ce282..ed5a53db0b 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.101 2001/01/24 19:42:57 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.102 2001/02/16 03:16:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -878,7 +878,7 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
 	 * (In theory this might find a non-semantically-comparable operator,
 	 * but in practice that seems pretty unlikely for binary-compatible types.)
 	 */
-	new_op = oper_oid(opname, indexkeytype, indexkeytype, true);
+	new_op = compatible_oper_opid(opname, indexkeytype, indexkeytype, true);
 
 	if (OidIsValid(new_op))
 	{
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index b8639b4410..1025a9b7f6 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.56 2001/01/24 19:42:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.57 2001/02/16 03:16:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -617,7 +617,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
 	 */
 	ltype = exprType(item1);
 	rtype = exprType(item2);
-	eq_operator = oper("=", ltype, rtype, true);
+	eq_operator = compatible_oper("=", ltype, rtype, true);
 	if (!HeapTupleIsValid(eq_operator))
 	{
 		/*
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 266b6da75b..5d44bbefbd 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.76 2001/02/14 21:35:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.77 2001/02/16 03:16:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1155,10 +1155,10 @@ addTargetToSortList(TargetEntry *tle, List *sortlist, List *targetlist,
 		sortcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
 
 		if (opname)
-			sortcl->sortop = oper_oid(opname,
-									  tle->resdom->restype,
-									  tle->resdom->restype,
-									  false);
+			sortcl->sortop = compatible_oper_opid(opname,
+												  tle->resdom->restype,
+												  tle->resdom->restype,
+												  false);
 		else
 			sortcl->sortop = any_ordering_op(tle->resdom->restype);
 
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 93a986d835..69f34c890e 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.90 2001/02/14 21:35:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.91 2001/02/16 03:16:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -383,6 +383,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 						lexpr = lfirst(left_list);
 						left_list = lnext(left_list);
 
+						/*
+						 * It's OK to use oper() not compatible_oper() here,
+						 * because make_subplan() will insert type coercion
+						 * calls if needed.
+						 */
 						optup = oper(op,
 									 exprType(lexpr),
 									 exprType(tent->expr),
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index e55f638765..cd557994f1 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.46 2001/01/24 19:43:02 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.47 2001/02/16 03:16:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,15 +40,15 @@ static void unary_op_error(char *op, Oid arg, bool is_left_op);
 
 /* Select an ordering operator for the given datatype */
 Oid
-any_ordering_op(Oid restype)
+any_ordering_op(Oid argtype)
 {
 	Oid			order_opid;
 
-	order_opid = oper_oid("<", restype, restype, true);
+	order_opid = compatible_oper_opid("<", argtype, argtype, true);
 	if (!OidIsValid(order_opid))
 		elog(ERROR, "Unable to identify an ordering operator '%s' for type '%s'"
 			 "\n\tUse an explicit ordering operator or modify the query",
-			 "<", typeidTypeName(restype));
+			 "<", typeidTypeName(argtype));
 	return order_opid;
 }
 
@@ -59,6 +59,15 @@ oprid(Operator op)
 	return op->t_data->t_oid;
 }
 
+/* given operator tuple, return the underlying function's OID */
+Oid
+oprfuncid(Operator op)
+{
+	Form_pg_operator	pgopform = (Form_pg_operator) GETSTRUCT(op);
+
+	return pgopform->oprcode;
+}
+
 
 /* binary_oper_get_candidates()
  *	given opname, find all possible input type pairs for which an operator
@@ -119,7 +128,7 @@ binary_oper_get_candidates(char *opname,
 
 
 /* oper_select_candidate()
- * Given the input argtype array and more than one candidate
+ * Given the input argtype array and one or more candidates
  * for the function argtype array, attempt to resolve the conflict.
  * Returns the selected argtype array if the conflict can be resolved,
  * otherwise returns NULL.
@@ -593,34 +602,22 @@ oper_inexact(char *op, Oid arg1, Oid arg2)
 	if (ncandidates == 0)
 		return NULL;
 
-	/* Or found exactly one? Then proceed... */
-	else if (ncandidates == 1)
+	/* Otherwise, check for compatible datatypes, and then try to resolve
+	 * the conflict if more than one candidate remains.
+	 */
+	inputOids[0] = arg1;
+	inputOids[1] = arg2;
+	targetOids = oper_select_candidate(2, inputOids, candidates);
+	if (targetOids != NULL)
 	{
 		tup = SearchSysCache(OPERNAME,
 							 PointerGetDatum(op),
-							 ObjectIdGetDatum(candidates->args[0]),
-							 ObjectIdGetDatum(candidates->args[1]),
+							 ObjectIdGetDatum(targetOids[0]),
+							 ObjectIdGetDatum(targetOids[1]),
 							 CharGetDatum('b'));
-		Assert(HeapTupleIsValid(tup));
 	}
-
-	/* Otherwise, multiple operators of the desired types found... */
 	else
-	{
-		inputOids[0] = arg1;
-		inputOids[1] = arg2;
-		targetOids = oper_select_candidate(2, inputOids, candidates);
-		if (targetOids != NULL)
-		{
-			tup = SearchSysCache(OPERNAME,
-								 PointerGetDatum(op),
-								 ObjectIdGetDatum(targetOids[0]),
-								 ObjectIdGetDatum(targetOids[1]),
-								 CharGetDatum('b'));
-		}
-		else
-			tup = NULL;
-	}
+		tup = NULL;
 	return (Operator) tup;
 }
 
@@ -628,6 +625,10 @@ oper_inexact(char *op, Oid arg1, Oid arg2)
 /* oper() -- search for a binary operator
  * Given operator name, types of arg1 and arg2, return oper struct.
  *
+ * IMPORTANT: the returned operator (if any) is only promised to be
+ * coercion-compatible with the input datatypes.  Do not use this if
+ * you need an exact- or binary-compatible match; see compatible_oper.
+ *
  * If no matching operator found, return NULL if noError is true,
  * raise an error if it is false.
  *
@@ -653,19 +654,51 @@ oper(char *opname, Oid ltypeId, Oid rtypeId, bool noError)
 	return (Operator) NULL;
 }
 
-/* oper_oid() -- get OID of a binary operator
+/* compatible_oper()
+ *	given an opname and input datatypes, find a compatible binary operator
+ *
+ *	This is tighter than oper() because it will not return an operator that
+ *	requires coercion of the input datatypes (but binary-compatible operators
+ *	are accepted).  Otherwise, the semantics are the same.
+ */
+Operator
+compatible_oper(char *op, Oid arg1, Oid arg2, bool noError)
+{
+	Operator	optup;
+	Form_pg_operator	opform;
+
+	/* oper() will find the best available match */
+	optup = oper(op, arg1, arg2, noError);
+	if (optup == (Operator) NULL)
+		return (Operator) NULL;	/* must be noError case */
+
+	/* but is it good enough? */
+	opform = (Form_pg_operator) GETSTRUCT(optup);
+	if ((opform->oprleft == arg1 ||
+		 IS_BINARY_COMPATIBLE(opform->oprleft, arg1)) &&
+		(opform->oprright == arg2 ||
+		 IS_BINARY_COMPATIBLE(opform->oprright, arg2)))
+		return optup;
+
+	if (!noError)
+		op_error(op, arg1, arg2);
+
+	return (Operator) NULL;
+}
+
+/* compatible_oper_opid() -- get OID of a binary operator
  *
  * This is a convenience routine that extracts only the operator OID
- * from the result of oper().  InvalidOid is returned if the lookup
- * fails and noError is true.
+ * from the result of compatible_oper().  InvalidOid is returned if the
+ * lookup fails and noError is true.
  */
 Oid
-oper_oid(char *op, Oid arg1, Oid arg2, bool noError)
+compatible_oper_opid(char *op, Oid arg1, Oid arg2, bool noError)
 {
 	Operator	optup;
 	Oid			result;
 
-	optup = oper(op, arg1, arg2, noError);
+	optup = compatible_oper(op, arg1, arg2, noError);
 	if (optup != NULL)
 	{
 		result = oprid(optup);
@@ -675,6 +708,28 @@ oper_oid(char *op, Oid arg1, Oid arg2, bool noError)
 	return InvalidOid;
 }
 
+/* compatible_oper_funcid() -- get OID of a binary operator's function
+ *
+ * This is a convenience routine that extracts only the function OID
+ * from the result of compatible_oper().  InvalidOid is returned if the
+ * lookup fails and noError is true.
+ */
+Oid
+compatible_oper_funcid(char *op, Oid arg1, Oid arg2, bool noError)
+{
+	Operator	optup;
+	Oid			result;
+
+	optup = compatible_oper(op, arg1, arg2, noError);
+	if (optup != NULL)
+	{
+		result = oprfuncid(optup);
+		ReleaseSysCache(optup);
+		return result;
+	}
+	return InvalidOid;
+}
+
 /* unary_oper_get_candidates()
  *	given opname, find all possible types for which
  *	a right/left unary operator named opname exists.
@@ -737,6 +792,10 @@ unary_oper_get_candidates(char *opname,
 
 
 /* Given unary right operator (operator on right), return oper struct
+ *
+ * IMPORTANT: the returned operator (if any) is only promised to be
+ * coercion-compatible with the input datatype.  Do not use this if
+ * you need an exact- or binary-compatible match.
  *
  * Always raises error on failure.
  *
@@ -764,16 +823,11 @@ right_oper(char *op, Oid arg)
 		ncandidates = unary_oper_get_candidates(op, &candidates, 'r');
 		if (ncandidates == 0)
 			unary_op_error(op, arg, FALSE);
-		else if (ncandidates == 1)
-		{
-			tup = SearchSysCache(OPERNAME,
-								 PointerGetDatum(op),
-								 ObjectIdGetDatum(candidates->args[0]),
-								 ObjectIdGetDatum(InvalidOid),
-								 CharGetDatum('r'));
-		}
 		else
 		{
+			/* We must run oper_select_candidate even if only one candidate,
+			 * otherwise we may falsely return a non-type-compatible operator.
+			 */
 			targetOid = oper_select_candidate(1, &arg, candidates);
 			if (targetOid != NULL)
 				tup = SearchSysCache(OPERNAME,
@@ -792,6 +846,10 @@ right_oper(char *op, Oid arg)
 
 
 /* Given unary left operator (operator on left), return oper struct
+ *
+ * IMPORTANT: the returned operator (if any) is only promised to be
+ * coercion-compatible with the input datatype.  Do not use this if
+ * you need an exact- or binary-compatible match.
  *
  * Always raises error on failure.
  *
@@ -819,16 +877,11 @@ left_oper(char *op, Oid arg)
 		ncandidates = unary_oper_get_candidates(op, &candidates, 'l');
 		if (ncandidates == 0)
 			unary_op_error(op, arg, TRUE);
-		else if (ncandidates == 1)
-		{
-			tup = SearchSysCache(OPERNAME,
-								 PointerGetDatum(op),
-								 ObjectIdGetDatum(InvalidOid),
-								 ObjectIdGetDatum(candidates->args[0]),
-								 CharGetDatum('l'));
-		}
 		else
 		{
+			/* We must run oper_select_candidate even if only one candidate,
+			 * otherwise we may falsely return a non-type-compatible operator.
+			 */
 			targetOid = oper_select_candidate(1, &arg, candidates);
 			if (targetOid != NULL)
 				tup = SearchSysCache(OPERNAME,
diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h
index 6867addea5..069a9d5be2 100644
--- a/src/include/parser/parse_oper.h
+++ b/src/include/parser/parse_oper.h
@@ -1,13 +1,13 @@
 /*-------------------------------------------------------------------------
  *
- * catalog_utils.h
+ * parse_oper.h
  *
  *
  *
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_oper.h,v 1.13 2001/01/24 19:43:27 momjian Exp $
+ * $Id: parse_oper.h,v 1.14 2001/02/16 03:16:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,13 +18,27 @@
 
 typedef HeapTuple Operator;
 
+/* Routines to find operators matching a name and given input types */
+/* NB: the selected operator may require coercion of the input types! */
 extern Operator oper(char *op, Oid arg1, Oid arg2, bool noError);
 extern Operator right_oper(char *op, Oid arg);
 extern Operator left_oper(char *op, Oid arg);
 
-extern Oid	oper_oid(char *op, Oid arg1, Oid arg2, bool noError);
-extern Oid	oprid(Operator op);
+/* Routines to find operators that DO NOT require coercion --- ie, their */
+/* input types are either exactly as given, or binary-compatible */
+extern Operator compatible_oper(char *op, Oid arg1, Oid arg2, bool noError);
+/* currently no need for compatible_left_oper/compatible_right_oper */
 
-extern Oid	any_ordering_op(Oid restype);
+/* Convenience routines that call compatible_oper() and return either */
+/* the operator OID or the underlying function OID, or InvalidOid if fail */
+extern Oid compatible_oper_opid(char *op, Oid arg1, Oid arg2, bool noError);
+extern Oid compatible_oper_funcid(char *op, Oid arg1, Oid arg2, bool noError);
+
+/* Convenience routine that packages a specific call on compatible_oper */
+extern Oid	any_ordering_op(Oid argtype);
+
+/* Extract operator OID or underlying-function OID from an Operator tuple */
+extern Oid	oprid(Operator op);
+extern Oid	oprfuncid(Operator op);
 
 #endif	 /* PARSE_OPER_H */