]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Another go-round with resolution of ambiguous functions and operators.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 19 Mar 2000 00:19:39 +0000 (00:19 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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,
                                                                                         &current_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], &current_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);