diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 688c5bfa306c4ebb2227d25f02500efbb8fdd981..9902b0cf92d3f201027e85da49f1e220a055619c 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.94 2000/11/16 22:30:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.95 2000/12/15 19:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -759,14 +759,15 @@ func_select_candidate(int nargs,
 	CandidateList current_candidate;
 	CandidateList last_candidate;
 	Oid		   *current_typeids;
+	Oid			current_type;
 	int			i;
 	int			ncandidates;
 	int			nbestMatch,
 				nmatch;
-	CATEGORY	slot_category,
+	CATEGORY	slot_category[FUNC_MAX_ARGS],
 				current_category;
-	Oid			slot_type,
-				current_type;
+	bool		slot_has_preferred_type[FUNC_MAX_ARGS];
+	bool		resolved_unknowns;
 
 	/*
 	 * Run through all candidates and keep those with the most matches on
@@ -911,98 +912,133 @@ func_select_candidate(int nargs,
 	 * Still too many candidates? Try assigning types for the unknown
 	 * columns.
 	 *
-	 * We do this by examining each unknown argument position to see if all
-	 * the candidates agree on the type category of that slot.	If so, and
-	 * if some candidates accept the preferred type in that category,
-	 * eliminate the candidates with other input types.  If we are down to
-	 * one candidate at the end, we win.
+	 * We do this by examining each unknown argument position to see if we
+	 * can determine a "type category" for it.  If any candidate has an
+	 * input datatype of STRING category, use STRING category (this bias
+	 * towards STRING is appropriate since unknown-type literals look like
+	 * strings).  Otherwise, if all the candidates agree on the type
+	 * category of this argument position, use that category.  Otherwise,
+	 * fail because we cannot determine a category.
 	 *
-	 * XXX It's kinda bogus to do this left-to-right, isn't it?  If we
-	 * eliminate some candidates because they are non-preferred at the
-	 * first slot, we won't notice that they didn't have the same type
-	 * category for a later slot.
-	 * XXX Hmm. How else would you do this? These candidates are here because
-	 * they all have the same number of matches on arguments with explicit
-	 * types, so from here on left-to-right resolution is as good as any.
-	 * Need a counterexample to see otherwise...
+	 * If we are able to determine a type category, also notice whether
+	 * any of the candidates takes a preferred datatype within the category.
+	 *
+	 * Having completed this examination, remove candidates that accept
+	 * the wrong category at any unknown position.  Also, if at least one
+	 * candidate accepted a preferred type at a position, remove candidates
+	 * that accept non-preferred types.
+	 *
+	 * If we are down to one candidate at the end, we win.
 	 */
+	resolved_unknowns = false;
 	for (i = 0; i < nargs; i++)
 	{
-		if (input_typeids[i] == UNKNOWNOID)
+		bool	have_conflict;
+
+		if (input_typeids[i] != UNKNOWNOID)
+			continue;
+		resolved_unknowns = true; /* assume we can do it */
+		slot_category[i] = INVALID_TYPE;
+		slot_has_preferred_type[i] = false;
+		have_conflict = false;
+		for (current_candidate = candidates;
+			 current_candidate != NULL;
+			 current_candidate = current_candidate->next)
 		{
-			slot_category = INVALID_TYPE;
-			slot_type = InvalidOid;
-			last_candidate = NULL;
-			for (current_candidate = candidates;
-				 current_candidate != NULL;
-				 current_candidate = current_candidate->next)
+			current_typeids = current_candidate->args;
+			current_type = current_typeids[i];
+			current_category = TypeCategory(current_type);
+			if (slot_category[i] == INVALID_TYPE)
 			{
-				current_typeids = current_candidate->args;
-				current_type = current_typeids[i];
-				current_category = TypeCategory(current_type);
-				if (slot_category == INVALID_TYPE)
+				/* first candidate */
+				slot_category[i] = current_category;
+				slot_has_preferred_type[i] =
+					IsPreferredType(current_category, current_type);
+			}
+			else if (current_category == slot_category[i])
+			{
+				/* more candidates in same category */
+				slot_has_preferred_type[i] |=
+					IsPreferredType(current_category, current_type);
+			}
+			else
+			{
+				/* category conflict! */
+				if (current_category == STRING_TYPE)
 				{
-					slot_category = current_category;
-					slot_type = current_type;
-					last_candidate = current_candidate;
+					/* STRING always wins if available */
+					slot_category[i] = current_category;
+					slot_has_preferred_type[i] =
+						IsPreferredType(current_category, current_type);
 				}
-				else if (current_category != slot_category)
+				else
 				{
-					/* started out as unknown type, so give preference to string type, if available */
-					if (current_category == STRING_TYPE)
-					{
-						slot_category = current_category;
-						slot_type = current_type;
-						/* forget all previous candidates */
-						candidates = current_candidate;
-						last_candidate = current_candidate;
-					}
-					else if (slot_category == STRING_TYPE)
-					{
-						/* forget this candidate */
-						if (last_candidate)
-							last_candidate->next = current_candidate->next;
-						else
-							candidates = current_candidate->next;
-					}
+					/* Remember conflict, but keep going (might find STRING) */
+					have_conflict = true;
 				}
-				else if (current_type != slot_type)
+			}
+		}
+		if (have_conflict && slot_category[i] != STRING_TYPE)
+		{
+			/* Failed to resolve category conflict at this position */
+			resolved_unknowns = false;
+			break;
+		}
+	}
+
+	if (resolved_unknowns)
+	{
+		/* Strip non-matching candidates */
+		ncandidates = 0;
+		last_candidate = NULL;
+		for (current_candidate = candidates;
+			 current_candidate != NULL;
+			 current_candidate = current_candidate->next)
+		{
+			bool	keepit = true;
+
+			current_typeids = current_candidate->args;
+			for (i = 0; i < nargs; i++)
+			{
+				if (input_typeids[i] != UNKNOWNOID)
+					continue;
+				current_type = current_typeids[i];
+				current_category = TypeCategory(current_type);
+				if (current_category != slot_category[i])
 				{
-					if (IsPreferredType(slot_category, current_type))
-					{
-						slot_type = current_type;
-						/* forget all previous candidates */
-						candidates = current_candidate;
-						last_candidate = current_candidate;
-					}
-					else if (IsPreferredType(slot_category, slot_type))
-					{
-						/* forget this candidate */
-						if (last_candidate)
-							last_candidate->next = current_candidate->next;
-						else
-							candidates = current_candidate->next;
-					}
-					else
-						last_candidate = current_candidate;
+					keepit = false;
+					break;
 				}
-				else
+				if (slot_has_preferred_type[i] &&
+					!IsPreferredType(current_category, current_type))
 				{
-					/* keep this candidate */
-					last_candidate = current_candidate;
+					keepit = false;
+					break;
 				}
 			}
-			if (last_candidate) /* terminate rebuilt list */
-				last_candidate->next = NULL;
+			if (keepit)
+			{
+				/* keep this candidate */
+				last_candidate = current_candidate;
+				ncandidates++;
+			}
+			else
+			{
+				/* forget this candidate */
+				if (last_candidate)
+					last_candidate->next = current_candidate->next;
+				else
+					candidates = current_candidate->next;
+			}
 		}
+		if (last_candidate)		/* terminate rebuilt list */
+			last_candidate->next = NULL;
 	}
 
-	if (candidates == NULL)
-		return NULL;			/* no remaining candidates */
-	if (candidates->next != NULL)
-		return NULL;			/* more than one remaining candidate */
+	if (ncandidates == 1)
+		return candidates->args;
 
-	return candidates->args;
+	return NULL;				/* failed to determine a unique candidate */
 }	/* func_select_candidate() */
 
 
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index a44a740dc926045b6dba4f7cfaa5835805a164fe..dc1c267366e0a95745b5e33eb639bb9177ec3981 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.44 2000/11/16 22:30:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.45 2000/12/15 19:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -176,15 +176,16 @@ oper_select_candidate(int nargs,
 	CandidateList current_candidate;
 	CandidateList last_candidate;
 	Oid		   *current_typeids;
+	Oid			current_type;
 	int			unknownOids;
 	int			i;
 	int			ncandidates;
 	int			nbestMatch,
 				nmatch;
-	CATEGORY	slot_category,
+	CATEGORY	slot_category[FUNC_MAX_ARGS],
 				current_category;
-	Oid			slot_type,
-				current_type;
+	bool		slot_has_preferred_type[FUNC_MAX_ARGS];
+	bool		resolved_unknowns;
 
 	/*
 	 * First, delete any candidates that cannot actually accept the given
@@ -406,94 +407,135 @@ oper_select_candidate(int nargs,
 	}
 
 	/*
-	 * Second try: examine each unknown argument position to see if all
-	 * the candidates agree on the type category of that slot.	If so, and
-	 * if some candidates accept the preferred type in that category,
-	 * eliminate the candidates with other input types.  If we are down to
-	 * one candidate at the end, we win.
+	 * Second try: same algorithm as for unknown resolution in parse_func.c.
 	 *
-	 * XXX It's kinda bogus to do this left-to-right, isn't it?  If we
-	 * eliminate some candidates because they are non-preferred at the
-	 * first slot, we won't notice that they didn't have the same type
-	 * category for a later slot.
+	 * We do this by examining each unknown argument position to see if we
+	 * can determine a "type category" for it.  If any candidate has an
+	 * input datatype of STRING category, use STRING category (this bias
+	 * towards STRING is appropriate since unknown-type literals look like
+	 * strings).  Otherwise, if all the candidates agree on the type
+	 * category of this argument position, use that category.  Otherwise,
+	 * fail because we cannot determine a category.
+	 *
+	 * If we are able to determine a type category, also notice whether
+	 * any of the candidates takes a preferred datatype within the category.
+	 *
+	 * Having completed this examination, remove candidates that accept
+	 * the wrong category at any unknown position.  Also, if at least one
+	 * candidate accepted a preferred type at a position, remove candidates
+	 * that accept non-preferred types.
+	 *
+	 * If we are down to one candidate at the end, we win.
 	 */
+	resolved_unknowns = false;
 	for (i = 0; i < nargs; i++)
 	{
-		if (input_typeids[i] == UNKNOWNOID)
+		bool	have_conflict;
+
+		if (input_typeids[i] != UNKNOWNOID)
+			continue;
+		resolved_unknowns = true; /* assume we can do it */
+		slot_category[i] = INVALID_TYPE;
+		slot_has_preferred_type[i] = false;
+		have_conflict = false;
+		for (current_candidate = candidates;
+			 current_candidate != NULL;
+			 current_candidate = current_candidate->next)
 		{
-			slot_category = INVALID_TYPE;
-			slot_type = InvalidOid;
-			last_candidate = NULL;
-			for (current_candidate = candidates;
-				 current_candidate != NULL;
-				 current_candidate = current_candidate->next)
+			current_typeids = current_candidate->args;
+			current_type = current_typeids[i];
+			current_category = TypeCategory(current_type);
+			if (slot_category[i] == INVALID_TYPE)
 			{
-				current_typeids = current_candidate->args;
-				current_type = current_typeids[i];
-				current_category = TypeCategory(current_type);
-				/* first time through? Then we'll use this one for now */
-				if (slot_category == INVALID_TYPE)
+				/* first candidate */
+				slot_category[i] = current_category;
+				slot_has_preferred_type[i] =
+					IsPreferredType(current_category, current_type);
+			}
+			else if (current_category == slot_category[i])
+			{
+				/* more candidates in same category */
+				slot_has_preferred_type[i] |=
+					IsPreferredType(current_category, current_type);
+			}
+			else
+			{
+				/* category conflict! */
+				if (current_category == STRING_TYPE)
 				{
-					slot_category = current_category;
-					slot_type = current_type;
-					last_candidate = current_candidate;
+					/* STRING always wins if available */
+					slot_category[i] = current_category;
+					slot_has_preferred_type[i] =
+						IsPreferredType(current_category, current_type);
 				}
-				else if (current_category != slot_category)
+				else
 				{
-					/* started out as unknown type, so give preference to string type, if available */
-					if (current_category == STRING_TYPE)
-					{
-						slot_category = current_category;
-						slot_type = current_type;
-						/* forget all previous candidates */
-						candidates = current_candidate;
-						last_candidate = current_candidate;
-					}
-					else if (slot_category == STRING_TYPE)
-					{
-						/* forget this candidate */
-						if (last_candidate)
-							last_candidate->next = current_candidate->next;
-						else
-							candidates = current_candidate->next;
-					}
+					/* Remember conflict, but keep going (might find STRING) */
+					have_conflict = true;
 				}
-				else if (current_type != slot_type)
+			}
+		}
+		if (have_conflict && slot_category[i] != STRING_TYPE)
+		{
+			/* Failed to resolve category conflict at this position */
+			resolved_unknowns = false;
+			break;
+		}
+	}
+
+	if (resolved_unknowns)
+	{
+		/* Strip non-matching candidates */
+		ncandidates = 0;
+		last_candidate = NULL;
+		for (current_candidate = candidates;
+			 current_candidate != NULL;
+			 current_candidate = current_candidate->next)
+		{
+			bool	keepit = true;
+
+			current_typeids = current_candidate->args;
+			for (i = 0; i < nargs; i++)
+			{
+				if (input_typeids[i] != UNKNOWNOID)
+					continue;
+				current_type = current_typeids[i];
+				current_category = TypeCategory(current_type);
+				if (current_category != slot_category[i])
 				{
-					if (IsPreferredType(slot_category, current_type))
-					{
-						slot_type = current_type;
-						/* forget all previous candidates */
-						candidates = current_candidate;
-						last_candidate = current_candidate;
-					}
-					else if (IsPreferredType(slot_category, slot_type))
-					{
-						/* forget this candidate */
-						if (last_candidate)
-							last_candidate->next = current_candidate->next;
-						else
-							candidates = current_candidate->next;
-					}
-					else
-						last_candidate = current_candidate;
+					keepit = false;
+					break;
 				}
-				else
+				if (slot_has_preferred_type[i] &&
+					!IsPreferredType(current_category, current_type))
 				{
-					/* keep this candidate */
-					last_candidate = current_candidate;
+					keepit = false;
+					break;
 				}
 			}
-			if (last_candidate) /* terminate rebuilt list */
-				last_candidate->next = NULL;
+			if (keepit)
+			{
+				/* keep this candidate */
+				last_candidate = current_candidate;
+				ncandidates++;
+			}
+			else
+			{
+				/* forget this candidate */
+				if (last_candidate)
+					last_candidate->next = current_candidate->next;
+				else
+					candidates = current_candidate->next;
+			}
 		}
+		if (last_candidate)		/* terminate rebuilt list */
+			last_candidate->next = NULL;
 	}
 
-	if (candidates == NULL)
-		return NULL;			/* no remaining candidates */
-	if (candidates->next != NULL)
-		return NULL;			/* more than one remaining candidate */
-	return candidates->args;
+	if (ncandidates == 1)
+		return candidates->args;
+
+	return NULL;				/* failed to determine a unique candidate */
 }	/* oper_select_candidate() */