diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 397bd4b8b079e84482b2f5570f9a2c000009baba..18224a7e3fef45e63f0b305cb515318da4c391f4 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.81 2002/08/31 22:10:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.82 2002/09/01 02:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,24 +27,27 @@
 #include "utils/syscache.h"
 
 
-Oid			DemoteType(Oid inType);
-Oid			PromoteTypeToNext(Oid inType);
-
 static Oid	PreferredType(CATEGORY category, Oid type);
-static Node *build_func_call(Oid funcid, Oid rettype, List *args);
-static Oid	find_coercion_function(Oid targetTypeId, Oid sourceTypeId,
-								   bool isExplicit);
+static bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
+								  bool isExplicit,
+								  Oid *funcid);
 static Oid	find_typmod_coercion_function(Oid typeId);
+static Node *build_func_call(Oid funcid, Oid rettype, List *args);
 
 
-/* coerce_type()
- * Convert a function argument to a different type.
+/*
+ * coerce_type()
+ *		Convert a function argument to a different type.
+ *
+ * The caller should already have determined that the coercion is possible;
+ * see can_coerce_type.
  */
 Node *
 coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 			Oid targetTypeId, int32 atttypmod, bool isExplicit)
 {
 	Node	   *result;
+	Oid			funcId;
 
 	if (targetTypeId == inputTypeId ||
 		node == NULL)
@@ -118,25 +121,71 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 		/* assume can_coerce_type verified that implicit coercion is okay */
 		result = node;
 	}
-	else if (IsBinaryCompatible(inputTypeId, targetTypeId))
+	else if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
+								   &funcId))
 	{
-		/*
-		 * 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.
-		 *
-		 * Also, domains may have value restrictions beyond the base type
-		 * that must be accounted for.
-		 */
-		result = coerce_type_constraints(pstate, node, targetTypeId, true);
-		/*
-		 * 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 (wrong if target is a domain,
-		 * in any case).
-		 */
-		result = (Node *) makeRelabelType(result, targetTypeId, -1);
+		if (OidIsValid(funcId))
+		{
+			/*
+			 * Generate an expression tree representing run-time application
+			 * of the conversion function.  If we are dealing with a domain
+			 * target type, the conversion function will yield the base type.
+			 */
+			Oid		baseTypeId = getBaseType(targetTypeId);
+
+			result = build_func_call(funcId, baseTypeId, makeList1(node));
+
+			/*
+			 * If domain, test against domain constraints and relabel with
+			 * domain type ID
+			 */
+			if (targetTypeId != baseTypeId)
+			{
+				result = coerce_type_constraints(pstate, result,
+												 targetTypeId, true);
+				result = (Node *) makeRelabelType(result, targetTypeId, -1);
+			}
+
+			/*
+			 * If the input is a constant, apply the type conversion function
+			 * now instead of delaying to runtime.	(We could, of course, just
+			 * leave this to be done during planning/optimization; but it's a
+			 * very frequent special case, and we save cycles in the rewriter
+			 * if we fold the expression now.)
+			 *
+			 * Note that no folding will occur if the conversion function is
+			 * not marked 'immutable'.
+			 *
+			 * HACK: if constant is NULL, don't fold it here.  This is needed
+			 * by make_subplan(), which calls this routine on placeholder
+			 * Const nodes that mustn't be collapsed.  (It'd be a lot cleaner
+			 * to make a separate node type for that purpose...)
+			 */
+			if (IsA(node, Const) &&
+				!((Const *) node)->constisnull)
+				result = eval_const_expressions(result);
+		}
+		else
+		{
+			/*
+			 * We don't need to do a physical 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.
+			 *
+			 * Also, domains may have value restrictions beyond the base type
+			 * that must be accounted for.
+			 */
+			result = coerce_type_constraints(pstate, node,
+											 targetTypeId, true);
+			/*
+			 * 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 (wrong if target is a domain,
+			 * in any case).
+			 */
+			result = (Node *) makeRelabelType(result, targetTypeId, -1);
+		}
 	}
 	else if (typeInheritsFrom(inputTypeId, targetTypeId))
 	{
@@ -149,74 +198,23 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 	}
 	else
 	{
-		/*
-		 * Otherwise, find the appropriate type conversion function
-		 * (caller should have determined that there is one), and generate
-		 * an expression tree representing run-time application of the
-		 * conversion function.
-		 *
-		 * For domains, we use the coercion function for the base type.
-		 */
-		Oid			baseTypeId = getBaseType(targetTypeId);
-		Oid			funcId;
-
-		funcId = find_coercion_function(baseTypeId,
-										getBaseType(inputTypeId),
-										isExplicit);
-		if (!OidIsValid(funcId))
-			elog(ERROR, "coerce_type: no conversion function from '%s' to '%s'",
-				 format_type_be(inputTypeId), format_type_be(targetTypeId));
-
-		result = build_func_call(funcId, baseTypeId, makeList1(node));
-
-		/*
-		 * If domain, test against domain constraints and relabel with
-		 * domain type ID
-		 */
-		if (targetTypeId != baseTypeId)
-		{
-			result = coerce_type_constraints(pstate, result, targetTypeId,
-											 true);
-			result = (Node *) makeRelabelType(result, targetTypeId, -1);
-		}
-
-		/*
-		 * If the input is a constant, apply the type conversion function
-		 * now instead of delaying to runtime.	(We could, of course, just
-		 * leave this to be done during planning/optimization; but it's a
-		 * very frequent special case, and we save cycles in the rewriter
-		 * if we fold the expression now.)
-		 *
-		 * Note that no folding will occur if the conversion function is not
-		 * marked 'iscachable'.
-		 *
-		 * HACK: if constant is NULL, don't fold it here.  This is needed by
-		 * make_subplan(), which calls this routine on placeholder Const
-		 * nodes that mustn't be collapsed.  (It'd be a lot cleaner to
-		 * make a separate node type for that purpose...)
-		 */
-		if (IsA(node, Const) &&
-			!((Const *) node)->constisnull)
-			result = eval_const_expressions(result);
+		/* If we get here, caller blew it */
+		elog(ERROR, "coerce_type: no conversion function from %s to %s",
+			 format_type_be(inputTypeId), format_type_be(targetTypeId));
+		result = NULL;			/* keep compiler quiet */
 	}
 
 	return result;
 }
 
 
-/* can_coerce_type()
- * Can input_typeids be coerced to func_typeids?
- *
- * There are a few types which are known apriori to be convertible.
- * We will check for those cases first, and then look for possible
- * conversion functions.
+/*
+ * can_coerce_type()
+ *		Can input_typeids be coerced to func_typeids?
  *
  * We must be told whether this is an implicit or explicit coercion
  * (explicit being a CAST construct, explicit function call, etc).
  * We will accept a wider set of coercion cases for an explicit coercion.
- *
- * Notes:
- * This uses the same mechanism as the CAST() SQL construct in gram.y.
  */
 bool
 can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
@@ -278,35 +276,101 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
 		}
 
 		/*
-		 * one of the known-good transparent conversions? then drop
-		 * through...
+		 * If pg_cast shows that we can coerce, accept.  This test now
+		 * covers both binary-compatible and coercion-function cases.
 		 */
-		if (IsBinaryCompatible(inputTypeId, targetTypeId))
+		if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
+								  &funcId))
 			continue;
 
 		/*
-		 * If input is a class type that inherits from target, no problem
+		 * If input is a class type that inherits from target, accept
 		 */
 		if (typeInheritsFrom(inputTypeId, targetTypeId))
 			continue;
 
 		/*
-		 * Else, try for run-time conversion using functions: look for a
-		 * single-argument function named with the target type name and
-		 * accepting the source type.
-		 *
-		 * If either type is a domain, use its base type instead.
+		 * Else, cannot coerce at this argument position
 		 */
-		funcId = find_coercion_function(getBaseType(targetTypeId),
-										getBaseType(inputTypeId),
-										isExplicit);
-		if (!OidIsValid(funcId))
-			return false;
+		return false;
 	}
 
 	return true;
 }
 
+
+/*
+ * Create an expression tree to enforce the constraints (if any)
+ * that should be applied by the type.  Currently this is only
+ * interesting for domain types.
+ */
+Node *
+coerce_type_constraints(ParseState *pstate, Node *arg,
+						Oid typeId, bool applyTypmod)
+{
+	char   *notNull = NULL;
+	int32	typmod = -1;
+
+	for (;;)
+	{
+		HeapTuple	tup;
+		Form_pg_type typTup;
+
+		tup = SearchSysCache(TYPEOID,
+							 ObjectIdGetDatum(typeId),
+							 0, 0, 0);
+		if (!HeapTupleIsValid(tup))
+			elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
+				 typeId);
+		typTup = (Form_pg_type) GETSTRUCT(tup);
+
+		/* Test for NOT NULL Constraint */
+		if (typTup->typnotnull && notNull == NULL)
+			notNull = pstrdup(NameStr(typTup->typname));
+
+		/* TODO: Add CHECK Constraints to domains */
+
+		if (typTup->typtype != 'd')
+		{
+			/* Not a domain, so done */
+			ReleaseSysCache(tup);
+			break;
+		}
+
+		Assert(typmod < 0);
+
+		typeId = typTup->typbasetype;
+		typmod = typTup->typtypmod;
+		ReleaseSysCache(tup);
+	}
+
+	/*
+	 * If domain applies a typmod to its base type, do length coercion.
+	 */
+	if (applyTypmod && typmod >= 0)
+		arg = coerce_type_typmod(pstate, arg, typeId, typmod);
+
+	/*
+	 * Only need to add one NOT NULL check regardless of how many 
+	 * domains in the stack request it.  The topmost domain that
+	 * requested it is used as the constraint name.
+	 */
+	if (notNull)
+	{
+		ConstraintTest *r = makeNode(ConstraintTest);
+
+		r->arg = arg;
+		r->testtype = CONSTR_TEST_NOTNULL;
+		r->name	= notNull;
+		r->check_expr = NULL;
+
+		arg = (Node *) r;
+	}	
+
+	return arg;
+}
+
+
 /* coerce_type_typmod()
  * Force a value to a particular typmod, if meaningful and possible.
  *
@@ -317,21 +381,9 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
  * The caller must have already ensured that the value is of the correct
  * type, typically by applying coerce_type.
  *
- * If the target column type possesses a function named for the type
- * and having parameter signature (columntype, int4), we assume that
- * the type requires coercion to its own length and that the said
- * function should be invoked to do that.
- *
- * "bpchar" (ie, char(N)) and "numeric" are examples of such types.
- *
- * This mechanism may seem pretty grotty and in need of replacement by
- * something in pg_cast, but since typmod is only interesting for datatypes
- * that have special handling in the grammar, there's not really much
- * percentage in making it any easier to apply such coercions ...
- *
  * NOTE: this does not need to work on domain types, because any typmod
  * coercion for a domain is considered to be part of the type coercion
- * needed to produce the domain value in the first place.
+ * needed to produce the domain value in the first place.  So, no getBaseType.
  */
 Node *
 coerce_type_typmod(ParseState *pstate, Node *node,
@@ -600,100 +652,6 @@ TypeCategory(Oid inType)
 }	/* TypeCategory() */
 
 
-/* IsBinaryCompatible()
- *		Check if two types are binary-compatible.
- *
- * This notion allows us to cheat and directly exchange values without
- * going through the trouble of calling a conversion function.
- *
- * XXX This should be moved to system catalog lookups
- * to allow for better type extensibility.
- */
-
-#define TypeIsTextGroup(t) \
-		((t) == TEXTOID || \
-		 (t) == BPCHAROID || \
-		 (t) == VARCHAROID)
-
-/* Notice OidGroup is a subset of Int4GroupA */
-#define TypeIsOidGroup(t) \
-		((t) == OIDOID || \
-		 (t) == REGPROCOID || \
-		 (t) == REGPROCEDUREOID || \
-		 (t) == REGOPEROID || \
-		 (t) == REGOPERATOROID || \
-		 (t) == REGCLASSOID || \
-		 (t) == REGTYPEOID)
-
-/*
- * INT4 is binary-compatible with many types, but we don't want to allow
- * implicit coercion directly between, say, OID and AbsTime.  So we subdivide
- * the categories.
- */
-#define TypeIsInt4GroupA(t) \
-		((t) == INT4OID || \
-		 TypeIsOidGroup(t))
-
-#define TypeIsInt4GroupB(t) \
-		((t) == INT4OID || \
-		 (t) == ABSTIMEOID)
-
-#define TypeIsInt4GroupC(t) \
-		((t) == INT4OID || \
-		 (t) == RELTIMEOID)
-
-#define TypeIsInetGroup(t) \
-		((t) == INETOID || \
-		 (t) == CIDROID)
-
-#define TypeIsBitGroup(t) \
-		((t) == BITOID || \
-		 (t) == VARBITOID)
-
-
-static bool
-DirectlyBinaryCompatible(Oid type1, Oid type2)
-{
-	HeapTuple	tuple;
-	bool		result;
-
-	if (type1 == type2)
-		return true;
-
-	tuple = SearchSysCache(CASTSOURCETARGET, type1, type2, 0, 0);
-	if (HeapTupleIsValid(tuple))
-	{
-		Form_pg_cast caststruct;
-
-		caststruct = (Form_pg_cast) GETSTRUCT(tuple);
-		result = caststruct->castfunc == InvalidOid && caststruct->castimplicit;
-		ReleaseSysCache(tuple);
-	}
-	else
-		result = false;
-
-	return result;
-}
-
-
-bool
-IsBinaryCompatible(Oid type1, Oid type2)
-{
-	if (DirectlyBinaryCompatible(type1, type2))
-		return true;
-	/*
-	 * Perhaps the types are domains; if so, look at their base types
-	 */
-	if (OidIsValid(type1))
-		type1 = getBaseType(type1);
-	if (OidIsValid(type2))
-		type2 = getBaseType(type2);
-	if (DirectlyBinaryCompatible(type1, type2))
-		return true;
-	return false;
-}
-
-
 /* IsPreferredType()
  * Check if this type is a preferred type.
  * XXX This should be moved to system catalog lookups
@@ -733,7 +691,13 @@ PreferredType(CATEGORY category, Oid type)
 			break;
 
 		case (NUMERIC_TYPE):
-			if (TypeIsOidGroup(type))
+			if (type == OIDOID ||
+				type == REGPROCOID ||
+				type == REGPROCEDUREOID ||
+				type == REGOPEROID ||
+				type == REGOPERATOROID ||
+				type == REGCLASSOID ||
+				type == REGTYPEOID)
 				result = OIDOID;
 			else if (type == NUMERICOID)
 				result = NUMERICOID;
@@ -768,30 +732,85 @@ PreferredType(CATEGORY category, Oid type)
 	return result;
 }	/* PreferredType() */
 
-/*
- * find_coercion_function
- *		Look for a coercion function between two types.
+
+/* IsBinaryCompatible()
+ *		Check if two types are binary-compatible.
  *
- * A coercion function must be named after (the internal name of) its
- * result type, and must accept exactly the specified input type.  We
- * also require it to be defined in the same namespace as its result type.
- * Furthermore, unless we are doing explicit coercion the function must
- * be marked as usable for implicit coercion --- this allows coercion
- * functions to be provided that aren't implicitly invokable.
+ * This notion allows us to cheat and directly exchange values without
+ * going through the trouble of calling a conversion function.
  *
- * This routine is also used to look for length-coercion functions, which
- * are similar but accept a second argument.  secondArgType is the type
- * of the second argument (normally INT4OID), or InvalidOid if we are
- * looking for a regular coercion function.
+ * As of 7.3, binary compatibility isn't hardwired into the code anymore.
+ * We consider two types binary-compatible if there is an implicit,
+ * no-function-needed pg_cast entry.  NOTE that we assume that such
+ * entries are symmetric, ie, it doesn't matter which type we consider
+ * source and which target.  (cf. checks in opr_sanity regression test)
+ */
+bool
+IsBinaryCompatible(Oid type1, Oid type2)
+{
+	HeapTuple	tuple;
+	Form_pg_cast castForm;
+	bool		result;
+
+	/* Fast path if same type */
+	if (type1 == type2)
+		return true;
+
+	/* Perhaps the types are domains; if so, look at their base types */
+	if (OidIsValid(type1))
+		type1 = getBaseType(type1);
+	if (OidIsValid(type2))
+		type2 = getBaseType(type2);
+
+	/* Somewhat-fast path if same base type */
+	if (type1 == type2)
+		return true;
+
+	/* Else look in pg_cast */
+	tuple = SearchSysCache(CASTSOURCETARGET,
+						   ObjectIdGetDatum(type1),
+						   ObjectIdGetDatum(type2),
+						   0, 0);
+	if (!HeapTupleIsValid(tuple))
+		return false;			/* no cast */
+	castForm = (Form_pg_cast) GETSTRUCT(tuple);
+
+	result = (castForm->castfunc == InvalidOid) && castForm->castimplicit;
+
+	ReleaseSysCache(tuple);
+
+	return result;
+}
+
+
+/*
+ * find_coercion_pathway
+ *		Look for a coercion pathway between two types.
  *
- * If a function is found, return its pg_proc OID; else return InvalidOid.
+ * If we find a matching entry in pg_cast, return TRUE, and set *funcid
+ * to the castfunc value (which may be InvalidOid for a binary-compatible
+ * coercion).
  */
-static Oid
-find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit)
+static bool
+find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit,
+					  Oid *funcid)
 {
-	Oid			funcid = InvalidOid;
+	bool		result = false;
 	HeapTuple	tuple;
 
+	*funcid = InvalidOid;
+
+	/* Perhaps the types are domains; if so, look at their base types */
+	if (OidIsValid(sourceTypeId))
+		sourceTypeId = getBaseType(sourceTypeId);
+	if (OidIsValid(targetTypeId))
+		targetTypeId = getBaseType(targetTypeId);
+
+	/* Domains are automatically binary-compatible with their base type */
+	if (sourceTypeId == targetTypeId)
+		return true;
+
+	/* Else look in pg_cast */
 	tuple = SearchSysCache(CASTSOURCETARGET,
 						   ObjectIdGetDatum(sourceTypeId),
 						   ObjectIdGetDatum(targetTypeId),
@@ -799,18 +818,36 @@ find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit)
 
 	if (HeapTupleIsValid(tuple))
 	{
-		Form_pg_cast cform = (Form_pg_cast) GETSTRUCT(tuple);
+		Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
 
-		if (isExplicit || cform->castimplicit)
-			funcid = cform->castfunc;
+		if (isExplicit || castForm->castimplicit)
+		{
+			*funcid = castForm->castfunc;
+			result = true;
+		}
 
 		ReleaseSysCache(tuple);
 	}
 
-	return funcid;
+	return result;
 }
 
 
+/*
+ * find_typmod_coercion_function -- does the given type need length coercion?
+ *
+ * If the target type possesses a function named for the type
+ * and having parameter signature (targettype, int4), we assume that
+ * the type requires coercion to its own length and that the said
+ * function should be invoked to do that.
+ *
+ * "bpchar" (ie, char(N)) and "numeric" are examples of such types.
+ *
+ * This mechanism may seem pretty grotty and in need of replacement by
+ * something in pg_cast, but since typmod is only interesting for datatypes
+ * that have special handling in the grammar, there's not really much
+ * percentage in making it any easier to apply such coercions ...
+ */
 static Oid
 find_typmod_coercion_function(Oid typeId)
 {
@@ -849,6 +886,7 @@ find_typmod_coercion_function(Oid typeId)
 	}
 
 	ReleaseSysCache(targetType);
+
 	return funcid;
 }
 
@@ -877,74 +915,3 @@ build_func_call(Oid funcid, Oid rettype, List *args)
 
 	return (Node *) expr;
 }
-
-/*
- * Create an expression tree to enforce the constraints (if any)
- * that should be applied by the type.  Currently this is only
- * interesting for domain types.
- */
-Node *
-coerce_type_constraints(ParseState *pstate, Node *arg,
-						Oid typeId, bool applyTypmod)
-{
-	char   *notNull = NULL;
-	int32	typmod = -1;
-
-	for (;;)
-	{
-		HeapTuple	tup;
-		Form_pg_type typTup;
-
-		tup = SearchSysCache(TYPEOID,
-							 ObjectIdGetDatum(typeId),
-							 0, 0, 0);
-		if (!HeapTupleIsValid(tup))
-			elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
-				 typeId);
-		typTup = (Form_pg_type) GETSTRUCT(tup);
-
-		/* Test for NOT NULL Constraint */
-		if (typTup->typnotnull && notNull == NULL)
-			notNull = pstrdup(NameStr(typTup->typname));
-
-		/* TODO: Add CHECK Constraints to domains */
-
-		if (typTup->typtype != 'd')
-		{
-			/* Not a domain, so done */
-			ReleaseSysCache(tup);
-			break;
-		}
-
-		Assert(typmod < 0);
-
-		typeId = typTup->typbasetype;
-		typmod = typTup->typtypmod;
-		ReleaseSysCache(tup);
-	}
-
-	/*
-	 * If domain applies a typmod to its base type, do length coercion.
-	 */
-	if (applyTypmod && typmod >= 0)
-		arg = coerce_type_typmod(pstate, arg, typeId, typmod);
-
-	/*
-	 * Only need to add one NOT NULL check regardless of how many 
-	 * domains in the stack request it.  The topmost domain that
-	 * requested it is used as the constraint name.
-	 */
-	if (notNull)
-	{
-		ConstraintTest *r = makeNode(ConstraintTest);
-
-		r->arg = arg;
-		r->testtype = CONSTR_TEST_NOTNULL;
-		r->name	= notNull;
-		r->check_expr = NULL;
-
-		arg = (Node *) r;
-	}	
-
-	return arg;
-}