Another go-round with resolution of ambiguous functions and operators.
authorTom Lane
Sun, 19 Mar 2000 00:19:39 +0000 (00:19 +0000)
committerTom Lane
Sun, 19 Mar 2000 00:19:39 +0000 (00:19 +0000)
In function parsing, try for an actual function of the given name and
input types before trying to interpret the function call as a type
coercion request, rather than after.  Before, a function that had the
same name as a type and operated on a binary-compatible type wouldn't
get invoked.  Also, cross-pollinate between func_select_candidates and
oper_select_candidates to ensure that they use as nearly the same
resolution rules as possible.  A few other minor code cleanups too.

src/backend/parser/parse_func.c
src/backend/parser/parse_oper.c

index e24007a439313efd88fdce06086185b7c9250473..7134d5e78119213ad0fe5b28f99ae776f02275a5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.75 2000/03/16 06:35:07 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.76 2000/03/19 00:19:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -127,14 +127,14 @@ agg_get_candidates(char *aggname,
    HeapTuple   tup;
    Form_pg_aggregate agg;
    int         ncandidates = 0;
-
-   static ScanKeyData aggKey[1] = {
-   {0, Anum_pg_aggregate_aggname, F_NAMEEQ}};
+   ScanKeyData aggKey[1];
 
    *candidates = NULL;
 
-   fmgr_info(F_NAMEEQ, (FmgrInfo *) &aggKey[0].sk_func);
-   aggKey[0].sk_argument = NameGetDatum(aggname);
+   ScanKeyEntryInitialize(&aggKey[0], 0,
+                          Anum_pg_aggregate_aggname,
+                          F_NAMEEQ,
+                          NameGetDatum(aggname));
 
    pg_aggregate_desc = heap_openr(AggregateRelationName, AccessShareLock);
    pg_aggregate_scan = heap_beginscan(pg_aggregate_desc,
@@ -145,10 +145,11 @@ agg_get_candidates(char *aggname,
 
    while (HeapTupleIsValid(tup = heap_getnext(pg_aggregate_scan, 0)))
    {
+       agg = (Form_pg_aggregate) GETSTRUCT(tup);
+
        current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
        current_candidate->args = (Oid *) palloc(sizeof(Oid));
 
-       agg = (Form_pg_aggregate) GETSTRUCT(tup);
        current_candidate->args[0] = agg->aggbasetype;
        current_candidate->next = *candidates;
        *candidates = current_candidate;
@@ -477,40 +478,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
        }
    }
 
-   /*
-    * See if this is really a type-coercion request: single-argument
-    * function call where the function name is a type name.  If so,
-    * and if we can do the coercion trivially, just go ahead and do it
-    * without requiring there to be a real function for it.  "Trivial"
-    * coercions are ones that involve binary-compatible types and ones
-    * that are coercing a previously-unknown-type literal constant
-    * to a specific type.
-    */
-   if (nargs == 1)
-   {
-       Type        tp;
-
-       tp = SearchSysCacheTuple(TYPENAME,
-                                PointerGetDatum(funcname),
-                                0, 0, 0);
-       if (HeapTupleIsValid(tp))
-       {
-           Oid     targetType = typeTypeId(tp);
-           Node   *arg1 = lfirst(fargs);
-           Oid     sourceType = exprType(arg1);
-
-           if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
-               sourceType == targetType ||
-               IS_BINARY_COMPATIBLE(sourceType, targetType))
-           {
-               /*
-                * coerce_type can handle these cases, so why duplicate code...
-                */
-               return coerce_type(pstate, arg1, sourceType, targetType, -1);
-           }
-       }
-   }
-
    /*
     * If we dropped through to here it's really a function (or a set,
     * which is implemented as a function). Extract arg type info and
@@ -615,9 +582,74 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
        exists = func_get_detail(funcname, nargs, oid_array, &funcid,
                                 &rettype, &retset, &true_oid_array);
        if (!exists)
-           elog(ERROR, "No such function '%s' with the specified attributes",
-                funcname);
+       {
+           /*
+            * If we can't find a function (or can't find a unique function),
+            * see if this is really a type-coercion request: single-argument
+            * function call where the function name is a type name.  If so,
+            * and if we can do the coercion trivially, just go ahead and do
+            * it without requiring there to be a real function for it.
+            *
+            * "Trivial" coercions are ones that involve binary-compatible
+            * types and ones that are coercing a previously-unknown-type
+            * literal constant to a specific type.
+            *
+            * DO NOT try to generalize this code to nontrivial coercions,
+            * because you'll just set up an infinite recursion between this
+            * routine and coerce_type!  We have already failed to find a
+            * suitable "real" coercion function, so we have to fail unless
+            * this is a coercion that coerce_type can handle by itself.
+            * Make sure this code stays in sync with what coerce_type does!
+            */
+           if (nargs == 1)
+           {
+               Type        tp;
 
+               tp = SearchSysCacheTuple(TYPENAME,
+                                        PointerGetDatum(funcname),
+                                        0, 0, 0);
+               if (HeapTupleIsValid(tp))
+               {
+                   Oid     sourceType = oid_array[0];
+                   Oid     targetType = typeTypeId(tp);
+                   Node   *arg1 = lfirst(fargs);
+
+                   if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
+                       sourceType == targetType ||
+                       IS_BINARY_COMPATIBLE(sourceType, targetType))
+                   {
+                       /* Ah-hah, we can do it as a trivial coercion.
+                        * coerce_type can handle these cases, so why
+                        * duplicate code...
+                        */
+                       return coerce_type(pstate, arg1,
+                                          sourceType, targetType, -1);
+                   }
+               }
+           }
+
+           /*
+            * Oops.  Time to die.
+            *
+            * If there is a single argument of complex type, we might be
+            * dealing with the PostQuel notation rel.function instead of
+            * the more usual function(rel).  Give a nonspecific error
+            * message that will cover both cases.
+            */
+           if (nargs == 1)
+           {
+               Type    tp = typeidType(oid_array[0]);
+               if (typeTypeFlag(tp) == 'c')
+                   elog(ERROR, "No such attribute or function '%s'",
+                        funcname);
+           }
+
+           /* Else generate a detailed complaint */
+           func_error(NULL, funcname, nargs, oid_array,
+                      "Unable to identify a function that satisfies the "
+                      "given argument types"
+                      "\n\tYou may need to add explicit typecasts");
+       }
    }
 
    /* got it */
@@ -760,7 +792,7 @@ func_get_candidates(char *funcname, int nargs)
    heapRelation = heap_openr(ProcedureRelationName, AccessShareLock);
    ScanKeyEntryInitialize(&skey,
                           (bits16) 0x0,
-                          (AttrNumber) 1,
+                          (AttrNumber) Anum_pg_proc_proname,
                           (RegProcedure) F_NAMEEQ,
                           (Datum) funcname);
 
@@ -851,8 +883,13 @@ match_argtypes(int nargs,
 /* func_select_candidate()
  * Given the input argtype array and more than one candidate
  * for the function argtype array, attempt to resolve the conflict.
- * returns the selected argtype array if the conflict can be resolved,
+ * Returns the selected argtype array if the conflict can be resolved,
  * otherwise returns NULL.
+ *
+ * By design, this is pretty similar to oper_select_candidate in parse_oper.c.
+ * However, the calling convention is a little different: we assume the caller
+ * already pruned away "candidates" that aren't actually coercion-compatible
+ * with the input types, whereas oper_select_candidate must do that itself.
  */
 static Oid *
 func_select_candidate(int nargs,
@@ -865,17 +902,62 @@ func_select_candidate(int nargs,
    int         i;
    int         ncandidates;
    int         nbestMatch,
-               nmatch,
-               ncompat;
+               nmatch;
    CATEGORY    slot_category,
                current_category;
    Oid         slot_type,
                current_type;
 
-/*
- * Run through all candidates and keep those with the most matches
- * on explicit types. Keep all candidates if none match.
- */
+   /*
+    * Run through all candidates and keep those with the most matches
+    * on exact types. Keep all candidates if none match.
+    */
+   ncandidates = 0;
+   nbestMatch = 0;
+   last_candidate = NULL;
+   for (current_candidate = candidates;
+        current_candidate != NULL;
+        current_candidate = current_candidate->next)
+   {
+       current_typeids = current_candidate->args;
+       nmatch = 0;
+       for (i = 0; i < nargs; i++)
+       {
+           if (input_typeids[i] != UNKNOWNOID &&
+               current_typeids[i] == input_typeids[i])
+               nmatch++;
+       }
+
+       /* take this one as the best choice so far? */
+       if ((nmatch > nbestMatch) || (last_candidate == NULL))
+       {
+           nbestMatch = nmatch;
+           candidates = current_candidate;
+           last_candidate = current_candidate;
+           ncandidates = 1;
+       }
+       /* no worse than the last choice, so keep this one too? */
+       else if (nmatch == nbestMatch)
+       {
+           last_candidate->next = current_candidate;
+           last_candidate = current_candidate;
+           ncandidates++;
+       }
+       /* otherwise, don't bother keeping this one... */
+   }
+
+   if (last_candidate)         /* terminate rebuilt list */
+       last_candidate->next = NULL;
+
+   if (ncandidates == 1)
+       return candidates->args;
+
+   /*
+    * Still too many candidates?
+    * Run through all candidates and keep those with the most matches
+    * on exact types + binary-compatible types.
+    * Keep all candidates if none match.
+    */
    ncandidates = 0;
    nbestMatch = 0;
    last_candidate = NULL;
@@ -885,29 +967,17 @@ func_select_candidate(int nargs,
    {
        current_typeids = current_candidate->args;
        nmatch = 0;
-       ncompat = 0;
        for (i = 0; i < nargs; i++)
        {
            if (input_typeids[i] != UNKNOWNOID)
            {
-               if (current_typeids[i] == input_typeids[i])
+               if (current_typeids[i] == input_typeids[i] ||
+                   IS_BINARY_COMPATIBLE(current_typeids[i],
+                                        input_typeids[i]))
                    nmatch++;
-               else if (IS_BINARY_COMPATIBLE(current_typeids[i],
-                                             input_typeids[i]))
-                   ncompat++;
            }
        }
 
-       /*
-        * If we find an exact match at all arg positions, we're done;
-        * there can be only one such candidate.
-        */
-       if (nmatch == nargs)
-           return current_candidate->args;
-
-       /* Otherwise, use match+compat as the score. */
-       nmatch += ncompat;
-
        /* take this one as the best choice so far? */
        if ((nmatch > nbestMatch) || (last_candidate == NULL))
        {
@@ -932,20 +1002,68 @@ func_select_candidate(int nargs,
    if (ncandidates == 1)
        return candidates->args;
 
-/*
- * 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.
- *
- * 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.
- */
+   /*
+    * Still too many candidates?
+    * Now look for candidates which are preferred types at the args that
+    * will require coercion.
+    * Keep all candidates if none match.
+    */
+   ncandidates = 0;
+   nbestMatch = 0;
+   last_candidate = NULL;
+   for (current_candidate = candidates;
+        current_candidate != NULL;
+        current_candidate = current_candidate->next)
+   {
+       current_typeids = current_candidate->args;
+       nmatch = 0;
+       for (i = 0; i < nargs; i++)
+       {
+           if (input_typeids[i] != UNKNOWNOID)
+           {
+               current_category = TypeCategory(current_typeids[i]);
+               if (current_typeids[i] == input_typeids[i] ||
+                   IsPreferredType(current_category, current_typeids[i]))
+                   nmatch++;
+           }
+       }
+
+       if ((nmatch > nbestMatch) || (last_candidate == NULL))
+       {
+           nbestMatch = nmatch;
+           candidates = current_candidate;
+           last_candidate = current_candidate;
+           ncandidates = 1;
+       }
+       else if (nmatch == nbestMatch)
+       {
+           last_candidate->next = current_candidate;
+           last_candidate = current_candidate;
+           ncandidates++;
+       }
+   }
+
+   if (last_candidate)         /* terminate rebuilt list */
+       last_candidate->next = NULL;
+
+   if (ncandidates == 1)
+       return candidates->args;
+
+   /*
+    * 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.
+    *
+    * 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.
+    */
    for (i = 0; i < nargs; i++)
    {
        if (input_typeids[i] == UNKNOWNOID)
@@ -1002,16 +1120,11 @@ func_select_candidate(int nargs,
        }
    }
 
-   ncandidates = 0;
-   for (current_candidate = candidates;
-        current_candidate != NULL;
-        current_candidate = current_candidate->next)
-       ncandidates++;
-
-   if (ncandidates == 1)
-       return candidates->args;
-
-   return NULL;
+   if (candidates == NULL)
+       return NULL;            /* no remaining candidates */
+   if (candidates->next != NULL)
+       return NULL;            /* more than one remaining candidate */
+   return candidates->args;
 }  /* func_select_candidate() */
 
 
@@ -1043,12 +1156,7 @@ func_get_detail(char *funcname,
                bool *retset,   /* return value */
                Oid **true_typeids)     /* return value */
 {
-   Oid       **input_typeid_vector;
-   Oid        *current_input_typeids;
-   CandidateList function_typeids;
-   CandidateList current_function_typeids;
    HeapTuple   ftup;
-   Form_pg_proc pform;
 
    /* attempt to find with arguments exactly as specified... */
    ftup = SearchSysCacheTuple(PROCNAME,
@@ -1056,23 +1164,37 @@ func_get_detail(char *funcname,
                               Int32GetDatum(nargs),
                               PointerGetDatum(oid_array),
                               0);
-   *true_typeids = oid_array;
 
-   /* didn't find an exact match, so now try to match up candidates... */
-   if (!HeapTupleIsValid(ftup))
+   if (HeapTupleIsValid(ftup))
    {
+       /* given argument types are the right ones */
+       *true_typeids = oid_array;
+   }
+   else
+   {
+       /* didn't find an exact match, so now try to match up candidates... */
+       CandidateList function_typeids;
+
        function_typeids = func_get_candidates(funcname, nargs);
 
        /* found something, so let's look through them... */
        if (function_typeids != NULL)
        {
-           int         ncandidates;
+           Oid       **input_typeid_vector = NULL;
+           Oid        *current_input_typeids;
 
-           input_typeid_vector = argtype_inherit(nargs, oid_array);
+           /*
+            * First we will search with the given oid_array, then with
+            * variants based on replacing complex types with their
+            * inheritance ancestors.  Stop as soon as any match is found.
+            */
            current_input_typeids = oid_array;
 
            do
            {
+               CandidateList current_function_typeids;
+               int         ncandidates;
+
                ncandidates = match_argtypes(nargs, current_input_typeids,
                                             function_typeids,
                                             ¤t_function_typeids);
@@ -1087,29 +1209,22 @@ func_get_detail(char *funcname,
                                          PointerGetDatum(*true_typeids),
                                               0);
                    Assert(HeapTupleIsValid(ftup));
+                   break;
                }
 
                /*
                 * multiple candidates? then better decide or throw an
                 * error...
                 */
-               else if (ncandidates > 1)
+               if (ncandidates > 1)
                {
                    *true_typeids = func_select_candidate(nargs,
                                                   current_input_typeids,
                                               current_function_typeids);
 
-                   /* couldn't decide, so quit */
-                   if (*true_typeids == NULL)
-                   {
-                       func_error(NULL, funcname, nargs, oid_array,
-                                  "Unable to identify a function which satisfies the given argument types"
-                                  "\n\tYou will have to retype your query using explicit typecasts");
-                   }
-
-                   /* found something, so use the first one... */
-                   else
+                   if (*true_typeids != NULL)
                    {
+                       /* was able to choose a best candidate */
                        ftup = SearchSysCacheTuple(PROCNAME,
                                               PointerGetDatum(funcname),
                                                   Int32GetDatum(nargs),
@@ -1117,35 +1232,34 @@ func_get_detail(char *funcname,
                                                   0);
                        Assert(HeapTupleIsValid(ftup));
                    }
+                   /* otherwise, ambiguous function call, so fail by
+                    * exiting loop with ftup still NULL.
+                    */
+                   break;
                }
+
+               /*
+                * No match here, so try the next inherited type vector.
+                * First time through, we need to compute the list of vectors.
+                */
+               if (input_typeid_vector == NULL)
+                   input_typeid_vector = argtype_inherit(nargs, oid_array);
+
                current_input_typeids = *input_typeid_vector++;
            }
-           while (current_input_typeids != InvalidOid && ncandidates == 0);
+           while (current_input_typeids != NULL);
        }
    }
 
-   if (!HeapTupleIsValid(ftup))
+   if (HeapTupleIsValid(ftup))
    {
-       Type        tp;
+       Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
 
-       if (nargs == 1)
-       {
-           tp = typeidType(oid_array[0]);
-           if (typeTypeFlag(tp) == 'c')
-               elog(ERROR, "No such attribute or function '%s'", funcname);
-       }
-   }
-   else
-   {
-       pform = (Form_pg_proc) GETSTRUCT(ftup);
        *funcid = ftup->t_data->t_oid;
        *rettype = pform->prorettype;
        *retset = pform->proretset;
-
        return true;
    }
-
-   /* shouldn't reach here */
    return false;
 }  /* func_get_detail() */
 
@@ -1677,11 +1791,13 @@ func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg)
    if (caller == NULL)
    {
        elog(ERROR, "Function '%s(%s)' does not exist%s%s",
-            funcname, p, ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : ""));
+            funcname, p,
+            ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : ""));
    }
    else
    {
        elog(ERROR, "%s: function '%s(%s)' does not exist%s%s",
-            caller, funcname, p, ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : ""));
+            caller, funcname, p,
+            ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : ""));
    }
 }
index c944d235784f16f9ad6b7b623266d7f85c445f91..fd41f4f3a0fa05c2bb2e475965434732d2e1f9da 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.38 2000/03/18 19:53:54 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.39 2000/03/19 00:19:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,10 +30,9 @@ static Operator oper_exact(char *op, Oid arg1, Oid arg2);
 static Operator oper_inexact(char *op, Oid arg1, Oid arg2);
 static int binary_oper_get_candidates(char *opname,
                                      CandidateList *candidates);
-static int unary_oper_get_candidates(char *op,
-                         Oid typeId,
-                         CandidateList *candidates,
-                         char rightleft);
+static int unary_oper_get_candidates(char *opname,
+                                    CandidateList *candidates,
+                                    char rightleft);
 static void op_error(char *op, Oid arg1, Oid arg2);
 static void unary_op_error(char *op, Oid arg, bool is_left_op);
 
@@ -66,7 +65,8 @@ oprid(Operator op)
 
 /* binary_oper_get_candidates()
  * given opname, find all possible input type pairs for which an operator
- * named opname exists.  Build a list of the candidate input types.
+ * named opname exists.
+ * Build a list of the candidate input types.
  * Returns number of candidates found.
  */
 static int
@@ -79,7 +79,7 @@ binary_oper_get_candidates(char *opname,
    HeapTuple   tup;
    Form_pg_operator oper;
    int         ncandidates = 0;
-   ScanKeyData opKey[3];
+   ScanKeyData opKey[2];
 
    *candidates = NULL;
 
@@ -102,10 +102,11 @@ binary_oper_get_candidates(char *opname,
 
    while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0)))
    {
+       oper = (Form_pg_operator) GETSTRUCT(tup);
+
        current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
        current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));
 
-       oper = (Form_pg_operator) GETSTRUCT(tup);
        current_candidate->args[0] = oper->oprleft;
        current_candidate->args[1] = oper->oprright;
        current_candidate->next = *candidates;
@@ -123,9 +124,16 @@ binary_oper_get_candidates(char *opname,
 /* oper_select_candidate()
  * Given the input argtype array and more than one candidate
  * for the function argtype array, attempt to resolve the conflict.
- * returns the selected argtype array if the conflict can be resolved,
+ * Returns the selected argtype array if the conflict can be resolved,
  * otherwise returns NULL.
  *
+ * By design, this is pretty similar to func_select_candidate in parse_func.c.
+ * However, we can do a couple of extra things here because we know we can
+ * have no more than two args to deal with.  Also, the calling convention
+ * is a little different: we must prune away "candidates" that aren't actually
+ * coercion-compatible with the input types, whereas in parse_func.c that
+ * gets done by match_argtypes before func_select_candidate is called.
+ *
  * This routine is new code, replacing binary_oper_select_candidate()
  * which dates from v4.2/v1.0.x days. It tries very hard to match up
  * operators with types, including allowing type coercions if necessary.
@@ -173,20 +181,57 @@ oper_select_candidate(int nargs,
    Oid        *current_typeids;
    int         unknownOids;
    int         i;
-
    int         ncandidates;
    int         nbestMatch,
                nmatch;
-
    CATEGORY    slot_category,
                current_category;
    Oid         slot_type,
                current_type;
 
-/*
- * Run through all candidates and keep those with the most matches
- * on exact types. Keep all candidates if none match.
- */
+   /*
+    * First, delete any candidates that cannot actually accept the given
+    * input types, whether directly or by coercion.  (Note that
+    * can_coerce_type will assume that UNKNOWN inputs are coercible to
+    * anything, so candidates will not be eliminated on that basis.)
+    */
+   ncandidates = 0;
+   last_candidate = NULL;
+   for (current_candidate = candidates;
+        current_candidate != NULL;
+        current_candidate = current_candidate->next)
+   {
+       if (can_coerce_type(nargs, input_typeids, current_candidate->args))
+       {
+           if (last_candidate == NULL)
+           {
+               candidates = current_candidate;
+               last_candidate = current_candidate;
+               ncandidates = 1;
+           }
+           else
+           {
+               last_candidate->next = current_candidate;
+               last_candidate = current_candidate;
+               ncandidates++;
+           }
+       }
+       /* otherwise, don't bother keeping this one... */
+   }
+
+   if (last_candidate)         /* terminate rebuilt list */
+       last_candidate->next = NULL;
+
+   /* Done if no candidate or only one candidate survives */
+   if (ncandidates == 0)
+       return NULL;
+   if (ncandidates == 1)
+       return candidates->args;
+
+   /*
+    * Run through all candidates and keep those with the most matches
+    * on exact types. Keep all candidates if none match.
+    */
    ncandidates = 0;
    nbestMatch = 0;
    last_candidate = NULL;
@@ -198,8 +243,8 @@ oper_select_candidate(int nargs,
        nmatch = 0;
        for (i = 0; i < nargs; i++)
        {
-           if ((input_typeids[i] != UNKNOWNOID)
-               && (current_typeids[i] == input_typeids[i]))
+           if (input_typeids[i] != UNKNOWNOID &&
+               current_typeids[i] == input_typeids[i])
                nmatch++;
        }
 
@@ -224,21 +269,15 @@ oper_select_candidate(int nargs,
    if (last_candidate)         /* terminate rebuilt list */
        last_candidate->next = NULL;
 
-   if (ncandidates <= 1)
-   {
-       if (ncandidates > 0)
-       {
-           if (!can_coerce_type(nargs, input_typeids, candidates->args))
-               ncandidates = 0;
-       }
-       return (ncandidates == 1) ? candidates->args : NULL;
-   }
+   if (ncandidates == 1)
+       return candidates->args;
 
-/*
- * Still too many candidates?
- * Now look for candidates which allow coercion and are preferred types.
- * Keep all candidates if none match.
- */
+   /*
+    * Still too many candidates?
+    * Run through all candidates and keep those with the most matches
+    * on exact types + binary-compatible types.
+    * Keep all candidates if none match.
+    */
    ncandidates = 0;
    nbestMatch = 0;
    last_candidate = NULL;
@@ -252,15 +291,14 @@ oper_select_candidate(int nargs,
        {
            if (input_typeids[i] != UNKNOWNOID)
            {
-               current_category = TypeCategory(current_typeids[i]);
-               if (current_typeids[i] == input_typeids[i])
-                   nmatch++;
-               else if (IsPreferredType(current_category, current_typeids[i])
-                        && can_coerce_type(1, &input_typeids[i], ¤t_typeids[i]))
+               if (current_typeids[i] == input_typeids[i] ||
+                   IS_BINARY_COMPATIBLE(current_typeids[i],
+                                        input_typeids[i]))
                    nmatch++;
            }
        }
 
+       /* take this one as the best choice so far? */
        if ((nmatch > nbestMatch) || (last_candidate == NULL))
        {
            nbestMatch = nmatch;
@@ -268,38 +306,80 @@ oper_select_candidate(int nargs,
            last_candidate = current_candidate;
            ncandidates = 1;
        }
+       /* no worse than the last choice, so keep this one too? */
        else if (nmatch == nbestMatch)
        {
            last_candidate->next = current_candidate;
            last_candidate = current_candidate;
            ncandidates++;
        }
+       /* otherwise, don't bother keeping this one... */
    }
 
    if (last_candidate)         /* terminate rebuilt list */
        last_candidate->next = NULL;
 
-   if (ncandidates <= 1)
+   if (ncandidates == 1)
+       return candidates->args;
+
+   /*
+    * Still too many candidates?
+    * Now look for candidates which are preferred types at the args that
+    * will require coercion.
+    * Keep all candidates if none match.
+    */
+   ncandidates = 0;
+   nbestMatch = 0;
+   last_candidate = NULL;
+   for (current_candidate = candidates;
+        current_candidate != NULL;
+        current_candidate = current_candidate->next)
    {
-       if (ncandidates > 0)
+       current_typeids = current_candidate->args;
+       nmatch = 0;
+       for (i = 0; i < nargs; i++)
+       {
+           if (input_typeids[i] != UNKNOWNOID)
+           {
+               current_category = TypeCategory(current_typeids[i]);
+               if (current_typeids[i] == input_typeids[i] ||
+                   IsPreferredType(current_category, current_typeids[i]))
+                   nmatch++;
+           }
+       }
+
+       if ((nmatch > nbestMatch) || (last_candidate == NULL))
+       {
+           nbestMatch = nmatch;
+           candidates = current_candidate;
+           last_candidate = current_candidate;
+           ncandidates = 1;
+       }
+       else if (nmatch == nbestMatch)
        {
-           if (!can_coerce_type(nargs, input_typeids, candidates->args))
-               ncandidates = 0;
+           last_candidate->next = current_candidate;
+           last_candidate = current_candidate;
+           ncandidates++;
        }
-       return (ncandidates == 1) ? candidates->args : NULL;
    }
 
-/*
- * Still too many candidates?
- * Try assigning types for the unknown columns.
- *
- * First try: if we have an unknown and a non-unknown input, see whether
- * there is a candidate all of whose input types are the same as the known
- * input type (there can be at most one such candidate).  If so, use that
- * candidate.  NOTE that this is cool only because operators can't
- * have more than 2 args, so taking the last non-unknown as current_type
- * can yield only one possibility if there is also an unknown.
- */
+   if (last_candidate)         /* terminate rebuilt list */
+       last_candidate->next = NULL;
+
+   if (ncandidates == 1)
+       return candidates->args;
+
+   /*
+    * Still too many candidates?
+    * Try assigning types for the unknown columns.
+    *
+    * First try: if we have an unknown and a non-unknown input, see whether
+    * there is a candidate all of whose input types are the same as the known
+    * input type (there can be at most one such candidate).  If so, use that
+    * candidate.  NOTE that this is cool only because operators can't
+    * have more than 2 args, so taking the last non-unknown as current_type
+    * can yield only one possibility if there is also an unknown.
+    */
    unknownOids = FALSE;
    current_type = UNKNOWNOID;
    for (i = 0; i < nargs; i++)
@@ -325,25 +405,22 @@ oper_select_candidate(int nargs,
                    nmatch++;
            }
            if (nmatch == nargs)
-           {
-               /* coercion check here is probably redundant, but be safe */
-               if (can_coerce_type(nargs, input_typeids, current_typeids))
-                   return current_typeids;
-           }
+               return current_typeids;
        }
    }
 
-/*
- * 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.
- *
- * 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.
- */
+   /*
+    * 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.
+    *
+    * 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.
+    */
    for (i = 0; i < nargs; i++)
    {
        if (input_typeids[i] == UNKNOWNOID)
@@ -400,20 +477,11 @@ oper_select_candidate(int nargs,
        }
    }
 
-   ncandidates = 0;
-   last_candidate = NULL;
-   for (current_candidate = candidates;
-        current_candidate != NULL;
-        current_candidate = current_candidate->next)
-   {
-       if (can_coerce_type(nargs, input_typeids, current_candidate->args))
-       {
-           ncandidates++;
-           last_candidate = current_candidate;
-       }
-   }
-
-   return (ncandidates == 1) ? last_candidate->args : NULL;
+   if (candidates == NULL)
+       return NULL;            /* no remaining candidates */
+   if (candidates->next != NULL)
+       return NULL;            /* more than one remaining candidate */
+   return candidates->args;
 }  /* oper_select_candidate() */
 
 
@@ -529,13 +597,13 @@ oper(char *opname, Oid ltypeId, Oid rtypeId, bool noWarnings)
 
 
 /* unary_oper_get_candidates()
- * given opname and typeId, find all possible types for which
- * a right/left unary operator named opname exists,
- * such that typeId can be coerced to it
+ * given opname, find all possible types for which
+ * a right/left unary operator named opname exists.
+ * Build a list of the candidate input types.
+ * Returns number of candidates found.
  */
 static int
-unary_oper_get_candidates(char *op,
-                         Oid typeId,
+unary_oper_get_candidates(char *opname,
                          CandidateList *candidates,
                          char rightleft)
 {
@@ -545,17 +613,19 @@ unary_oper_get_candidates(char *op,
    HeapTuple   tup;
    Form_pg_operator oper;
    int         ncandidates = 0;
-
-   static ScanKeyData opKey[2] = {
-       {0, Anum_pg_operator_oprname, F_NAMEEQ},
-   {0, Anum_pg_operator_oprkind, F_CHAREQ}};
+   ScanKeyData opKey[2];
 
    *candidates = NULL;
 
-   fmgr_info(F_NAMEEQ, (FmgrInfo *) &opKey[0].sk_func);
-   opKey[0].sk_argument = NameGetDatum(op);
-   fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
-   opKey[1].sk_argument = CharGetDatum(rightleft);
+   ScanKeyEntryInitialize(&opKey[0], 0,
+                          Anum_pg_operator_oprname,
+                          F_NAMEEQ,
+                          NameGetDatum(opname));
+
+   ScanKeyEntryInitialize(&opKey[1], 0,
+                          Anum_pg_operator_oprkind,
+                          F_CHAREQ,
+                          CharGetDatum(rightleft));
 
    pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock);
    pg_operator_scan = heap_beginscan(pg_operator_desc,
@@ -566,10 +636,11 @@ unary_oper_get_candidates(char *op,
 
    while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0)))
    {
+       oper = (Form_pg_operator) GETSTRUCT(tup);
+
        current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
        current_candidate->args = (Oid *) palloc(sizeof(Oid));
 
-       oper = (Form_pg_operator) GETSTRUCT(tup);
        if (rightleft == 'r')
            current_candidate->args[0] = oper->oprleft;
        else
@@ -606,7 +677,7 @@ right_oper(char *op, Oid arg)
    if (!HeapTupleIsValid(tup))
    {
        /* Try for inexact matches */
-       ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
+       ncandidates = unary_oper_get_candidates(op, &candidates, 'r');
        if (ncandidates == 0)
        {
            unary_op_error(op, arg, FALSE);
@@ -658,7 +729,7 @@ left_oper(char *op, Oid arg)
    if (!HeapTupleIsValid(tup))
    {
        /* Try for inexact matches */
-       ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
+       ncandidates = unary_oper_get_candidates(op, &candidates, 'l');
        if (ncandidates == 0)
        {
            unary_op_error(op, arg, TRUE);