Commit 440b0fea authored by Tom Lane's avatar Tom Lane

Another go-round with resolution of ambiguous functions and operators.

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.
parent f11d253e
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -127,14 +127,14 @@ agg_get_candidates(char *aggname,
HeapTuple tup; HeapTuple tup;
Form_pg_aggregate agg; Form_pg_aggregate agg;
int ncandidates = 0; int ncandidates = 0;
ScanKeyData aggKey[1];
static ScanKeyData aggKey[1] = {
{0, Anum_pg_aggregate_aggname, F_NAMEEQ}};
*candidates = NULL; *candidates = NULL;
fmgr_info(F_NAMEEQ, (FmgrInfo *) &aggKey[0].sk_func); ScanKeyEntryInitialize(&aggKey[0], 0,
aggKey[0].sk_argument = NameGetDatum(aggname); Anum_pg_aggregate_aggname,
F_NAMEEQ,
NameGetDatum(aggname));
pg_aggregate_desc = heap_openr(AggregateRelationName, AccessShareLock); pg_aggregate_desc = heap_openr(AggregateRelationName, AccessShareLock);
pg_aggregate_scan = heap_beginscan(pg_aggregate_desc, pg_aggregate_scan = heap_beginscan(pg_aggregate_desc,
...@@ -145,10 +145,11 @@ agg_get_candidates(char *aggname, ...@@ -145,10 +145,11 @@ agg_get_candidates(char *aggname,
while (HeapTupleIsValid(tup = heap_getnext(pg_aggregate_scan, 0))) while (HeapTupleIsValid(tup = heap_getnext(pg_aggregate_scan, 0)))
{ {
agg = (Form_pg_aggregate) GETSTRUCT(tup);
current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
current_candidate->args = (Oid *) palloc(sizeof(Oid)); current_candidate->args = (Oid *) palloc(sizeof(Oid));
agg = (Form_pg_aggregate) GETSTRUCT(tup);
current_candidate->args[0] = agg->aggbasetype; current_candidate->args[0] = agg->aggbasetype;
current_candidate->next = *candidates; current_candidate->next = *candidates;
*candidates = current_candidate; *candidates = current_candidate;
...@@ -477,40 +478,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ...@@ -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, * 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 * which is implemented as a function). Extract arg type info and
...@@ -615,9 +582,74 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ...@@ -615,9 +582,74 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
exists = func_get_detail(funcname, nargs, oid_array, &funcid, exists = func_get_detail(funcname, nargs, oid_array, &funcid,
&rettype, &retset, &true_oid_array); &rettype, &retset, &true_oid_array);
if (!exists) if (!exists)
elog(ERROR, "No such function '%s' with the specified attributes", {
/*
* 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); 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 */ /* got it */
...@@ -760,7 +792,7 @@ func_get_candidates(char *funcname, int nargs) ...@@ -760,7 +792,7 @@ func_get_candidates(char *funcname, int nargs)
heapRelation = heap_openr(ProcedureRelationName, AccessShareLock); heapRelation = heap_openr(ProcedureRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16) 0x0, (bits16) 0x0,
(AttrNumber) 1, (AttrNumber) Anum_pg_proc_proname,
(RegProcedure) F_NAMEEQ, (RegProcedure) F_NAMEEQ,
(Datum) funcname); (Datum) funcname);
...@@ -851,8 +883,13 @@ match_argtypes(int nargs, ...@@ -851,8 +883,13 @@ match_argtypes(int nargs,
/* func_select_candidate() /* func_select_candidate()
* Given the input argtype array and more than one candidate * Given the input argtype array and more than one candidate
* for the function argtype array, attempt to resolve the conflict. * 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. * 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 * static Oid *
func_select_candidate(int nargs, func_select_candidate(int nargs,
...@@ -865,16 +902,15 @@ func_select_candidate(int nargs, ...@@ -865,16 +902,15 @@ func_select_candidate(int nargs,
int i; int i;
int ncandidates; int ncandidates;
int nbestMatch, int nbestMatch,
nmatch, nmatch;
ncompat;
CATEGORY slot_category, CATEGORY slot_category,
current_category; current_category;
Oid slot_type, Oid slot_type,
current_type; current_type;
/* /*
* Run through all candidates and keep those with the most matches * Run through all candidates and keep those with the most matches
* on explicit types. Keep all candidates if none match. * on exact types. Keep all candidates if none match.
*/ */
ncandidates = 0; ncandidates = 0;
nbestMatch = 0; nbestMatch = 0;
...@@ -885,28 +921,62 @@ func_select_candidate(int nargs, ...@@ -885,28 +921,62 @@ func_select_candidate(int nargs,
{ {
current_typeids = current_candidate->args; current_typeids = current_candidate->args;
nmatch = 0; nmatch = 0;
ncompat = 0;
for (i = 0; i < nargs; i++) for (i = 0; i < nargs; i++)
{ {
if (input_typeids[i] != UNKNOWNOID) if (input_typeids[i] != UNKNOWNOID &&
{ current_typeids[i] == input_typeids[i])
if (current_typeids[i] == input_typeids[i])
nmatch++; nmatch++;
else if (IS_BINARY_COMPATIBLE(current_typeids[i],
input_typeids[i]))
ncompat++;
} }
/* 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;
/* /*
* If we find an exact match at all arg positions, we're done; * Still too many candidates?
* there can be only one such candidate. * Run through all candidates and keep those with the most matches
* on exact types + binary-compatible types.
* Keep all candidates if none match.
*/ */
if (nmatch == nargs) ncandidates = 0;
return current_candidate->args; nbestMatch = 0;
last_candidate = NULL;
/* Otherwise, use match+compat as the score. */ for (current_candidate = candidates;
nmatch += ncompat; 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)
{
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? */ /* take this one as the best choice so far? */
if ((nmatch > nbestMatch) || (last_candidate == NULL)) if ((nmatch > nbestMatch) || (last_candidate == NULL))
...@@ -932,7 +1002,54 @@ func_select_candidate(int nargs, ...@@ -932,7 +1002,54 @@ func_select_candidate(int nargs,
if (ncandidates == 1) if (ncandidates == 1)
return candidates->args; 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)
{
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? * Still too many candidates?
* Try assigning types for the unknown columns. * Try assigning types for the unknown columns.
* *
...@@ -942,9 +1059,10 @@ func_select_candidate(int nargs, ...@@ -942,9 +1059,10 @@ func_select_candidate(int nargs,
* candidates with other input types. If we are down to one candidate * candidates with other input types. If we are down to one candidate
* at the end, we win. * at the end, we win.
* *
* XXX It's kinda bogus to do this left-to-right, isn't it? If we eliminate * XXX It's kinda bogus to do this left-to-right, isn't it? If we
* some candidates because they are non-preferred at the first slot, we won't * eliminate some candidates because they are non-preferred at the first
* notice that they didn't have the same type category for a later slot. * slot, we won't notice that they didn't have the same type category for
* a later slot.
*/ */
for (i = 0; i < nargs; i++) for (i = 0; i < nargs; i++)
{ {
...@@ -1002,16 +1120,11 @@ func_select_candidate(int nargs, ...@@ -1002,16 +1120,11 @@ func_select_candidate(int nargs,
} }
} }
ncandidates = 0; if (candidates == NULL)
for (current_candidate = candidates; return NULL; /* no remaining candidates */
current_candidate != NULL; if (candidates->next != NULL)
current_candidate = current_candidate->next) return NULL; /* more than one remaining candidate */
ncandidates++;
if (ncandidates == 1)
return candidates->args; return candidates->args;
return NULL;
} /* func_select_candidate() */ } /* func_select_candidate() */
...@@ -1043,12 +1156,7 @@ func_get_detail(char *funcname, ...@@ -1043,12 +1156,7 @@ func_get_detail(char *funcname,
bool *retset, /* return value */ bool *retset, /* return value */
Oid **true_typeids) /* return value */ Oid **true_typeids) /* return value */
{ {
Oid **input_typeid_vector;
Oid *current_input_typeids;
CandidateList function_typeids;
CandidateList current_function_typeids;
HeapTuple ftup; HeapTuple ftup;
Form_pg_proc pform;
/* attempt to find with arguments exactly as specified... */ /* attempt to find with arguments exactly as specified... */
ftup = SearchSysCacheTuple(PROCNAME, ftup = SearchSysCacheTuple(PROCNAME,
...@@ -1056,23 +1164,37 @@ func_get_detail(char *funcname, ...@@ -1056,23 +1164,37 @@ func_get_detail(char *funcname,
Int32GetDatum(nargs), Int32GetDatum(nargs),
PointerGetDatum(oid_array), PointerGetDatum(oid_array),
0); 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); function_typeids = func_get_candidates(funcname, nargs);
/* found something, so let's look through them... */ /* found something, so let's look through them... */
if (function_typeids != NULL) 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; current_input_typeids = oid_array;
do do
{ {
CandidateList current_function_typeids;
int ncandidates;
ncandidates = match_argtypes(nargs, current_input_typeids, ncandidates = match_argtypes(nargs, current_input_typeids,
function_typeids, function_typeids,
&current_function_typeids); &current_function_typeids);
...@@ -1087,29 +1209,22 @@ func_get_detail(char *funcname, ...@@ -1087,29 +1209,22 @@ func_get_detail(char *funcname,
PointerGetDatum(*true_typeids), PointerGetDatum(*true_typeids),
0); 0);
Assert(HeapTupleIsValid(ftup)); Assert(HeapTupleIsValid(ftup));
break;
} }
/* /*
* multiple candidates? then better decide or throw an * multiple candidates? then better decide or throw an
* error... * error...
*/ */
else if (ncandidates > 1) if (ncandidates > 1)
{ {
*true_typeids = func_select_candidate(nargs, *true_typeids = func_select_candidate(nargs,
current_input_typeids, current_input_typeids,
current_function_typeids); current_function_typeids);
/* couldn't decide, so quit */ if (*true_typeids != NULL)
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
{ {
/* was able to choose a best candidate */
ftup = SearchSysCacheTuple(PROCNAME, ftup = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(funcname), PointerGetDatum(funcname),
Int32GetDatum(nargs), Int32GetDatum(nargs),
...@@ -1117,35 +1232,34 @@ func_get_detail(char *funcname, ...@@ -1117,35 +1232,34 @@ func_get_detail(char *funcname,
0); 0);
Assert(HeapTupleIsValid(ftup)); 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++; 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; *funcid = ftup->t_data->t_oid;
*rettype = pform->prorettype; *rettype = pform->prorettype;
*retset = pform->proretset; *retset = pform->proretset;
return true; return true;
} }
/* shouldn't reach here */
return false; return false;
} /* func_get_detail() */ } /* func_get_detail() */
...@@ -1677,11 +1791,13 @@ func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg) ...@@ -1677,11 +1791,13 @@ func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg)
if (caller == NULL) if (caller == NULL)
{ {
elog(ERROR, "Function '%s(%s)' does not exist%s%s", 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 else
{ {
elog(ERROR, "%s: function '%s(%s)' does not exist%s%s", 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 : ""));
} }
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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,8 +30,7 @@ static Operator oper_exact(char *op, Oid arg1, Oid arg2); ...@@ -30,8 +30,7 @@ static Operator oper_exact(char *op, Oid arg1, Oid arg2);
static Operator oper_inexact(char *op, Oid arg1, Oid arg2); static Operator oper_inexact(char *op, Oid arg1, Oid arg2);
static int binary_oper_get_candidates(char *opname, static int binary_oper_get_candidates(char *opname,
CandidateList *candidates); CandidateList *candidates);
static int unary_oper_get_candidates(char *op, static int unary_oper_get_candidates(char *opname,
Oid typeId,
CandidateList *candidates, CandidateList *candidates,
char rightleft); char rightleft);
static void op_error(char *op, Oid arg1, Oid arg2); static void op_error(char *op, Oid arg1, Oid arg2);
...@@ -66,7 +65,8 @@ oprid(Operator op) ...@@ -66,7 +65,8 @@ oprid(Operator op)
/* binary_oper_get_candidates() /* binary_oper_get_candidates()
* given opname, find all possible input type pairs for which an operator * 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. * Returns number of candidates found.
*/ */
static int static int
...@@ -79,7 +79,7 @@ binary_oper_get_candidates(char *opname, ...@@ -79,7 +79,7 @@ binary_oper_get_candidates(char *opname,
HeapTuple tup; HeapTuple tup;
Form_pg_operator oper; Form_pg_operator oper;
int ncandidates = 0; int ncandidates = 0;
ScanKeyData opKey[3]; ScanKeyData opKey[2];
*candidates = NULL; *candidates = NULL;
...@@ -102,10 +102,11 @@ binary_oper_get_candidates(char *opname, ...@@ -102,10 +102,11 @@ binary_oper_get_candidates(char *opname,
while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0))) while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0)))
{ {
oper = (Form_pg_operator) GETSTRUCT(tup);
current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
current_candidate->args = (Oid *) palloc(2 * sizeof(Oid)); current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));
oper = (Form_pg_operator) GETSTRUCT(tup);
current_candidate->args[0] = oper->oprleft; current_candidate->args[0] = oper->oprleft;
current_candidate->args[1] = oper->oprright; current_candidate->args[1] = oper->oprright;
current_candidate->next = *candidates; current_candidate->next = *candidates;
...@@ -123,9 +124,16 @@ binary_oper_get_candidates(char *opname, ...@@ -123,9 +124,16 @@ binary_oper_get_candidates(char *opname,
/* oper_select_candidate() /* oper_select_candidate()
* Given the input argtype array and more than one candidate * Given the input argtype array and more than one candidate
* for the function argtype array, attempt to resolve the conflict. * 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. * 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() * 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 * 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. * operators with types, including allowing type coercions if necessary.
...@@ -173,17 +181,54 @@ oper_select_candidate(int nargs, ...@@ -173,17 +181,54 @@ oper_select_candidate(int nargs,
Oid *current_typeids; Oid *current_typeids;
int unknownOids; int unknownOids;
int i; int i;
int ncandidates; int ncandidates;
int nbestMatch, int nbestMatch,
nmatch; nmatch;
CATEGORY slot_category, CATEGORY slot_category,
current_category; current_category;
Oid slot_type, Oid slot_type,
current_type; current_type;
/* /*
* 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 * Run through all candidates and keep those with the most matches
* on exact types. Keep all candidates if none match. * on exact types. Keep all candidates if none match.
*/ */
...@@ -198,8 +243,8 @@ oper_select_candidate(int nargs, ...@@ -198,8 +243,8 @@ oper_select_candidate(int nargs,
nmatch = 0; nmatch = 0;
for (i = 0; i < nargs; i++) for (i = 0; i < nargs; i++)
{ {
if ((input_typeids[i] != UNKNOWNOID) if (input_typeids[i] != UNKNOWNOID &&
&& (current_typeids[i] == input_typeids[i])) current_typeids[i] == input_typeids[i])
nmatch++; nmatch++;
} }
...@@ -224,19 +269,63 @@ oper_select_candidate(int nargs, ...@@ -224,19 +269,63 @@ oper_select_candidate(int nargs,
if (last_candidate) /* terminate rebuilt list */ if (last_candidate) /* terminate rebuilt list */
last_candidate->next = NULL; last_candidate->next = NULL;
if (ncandidates <= 1) 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;
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 (ncandidates > 0) if (input_typeids[i] != UNKNOWNOID)
{ {
if (!can_coerce_type(nargs, input_typeids, candidates->args)) if (current_typeids[i] == input_typeids[i] ||
ncandidates = 0; IS_BINARY_COMPATIBLE(current_typeids[i],
input_typeids[i]))
nmatch++;
} }
return (ncandidates == 1) ? candidates->args : NULL;
} }
/* /* 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? * Still too many candidates?
* Now look for candidates which allow coercion and are preferred types. * Now look for candidates which are preferred types at the args that
* will require coercion.
* Keep all candidates if none match. * Keep all candidates if none match.
*/ */
ncandidates = 0; ncandidates = 0;
...@@ -253,10 +342,8 @@ oper_select_candidate(int nargs, ...@@ -253,10 +342,8 @@ oper_select_candidate(int nargs,
if (input_typeids[i] != UNKNOWNOID) if (input_typeids[i] != UNKNOWNOID)
{ {
current_category = TypeCategory(current_typeids[i]); current_category = TypeCategory(current_typeids[i]);
if (current_typeids[i] == input_typeids[i]) if (current_typeids[i] == input_typeids[i] ||
nmatch++; IsPreferredType(current_category, current_typeids[i]))
else if (IsPreferredType(current_category, current_typeids[i])
&& can_coerce_type(1, &input_typeids[i], &current_typeids[i]))
nmatch++; nmatch++;
} }
} }
...@@ -279,17 +366,10 @@ oper_select_candidate(int nargs, ...@@ -279,17 +366,10 @@ oper_select_candidate(int nargs,
if (last_candidate) /* terminate rebuilt list */ if (last_candidate) /* terminate rebuilt list */
last_candidate->next = NULL; last_candidate->next = NULL;
if (ncandidates <= 1) if (ncandidates == 1)
{ return candidates->args;
if (ncandidates > 0)
{
if (!can_coerce_type(nargs, input_typeids, candidates->args))
ncandidates = 0;
}
return (ncandidates == 1) ? candidates->args : NULL;
}
/* /*
* Still too many candidates? * Still too many candidates?
* Try assigning types for the unknown columns. * Try assigning types for the unknown columns.
* *
...@@ -325,24 +405,21 @@ oper_select_candidate(int nargs, ...@@ -325,24 +405,21 @@ oper_select_candidate(int nargs,
nmatch++; nmatch++;
} }
if (nmatch == nargs) 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 * 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 agree on the type category of that slot. If so, and if some
* candidates accept the preferred type in that category, eliminate the * candidates accept the preferred type in that category, eliminate the
* candidates with other input types. If we are down to one candidate * candidates with other input types. If we are down to one candidate
* at the end, we win. * at the end, we win.
* *
* XXX It's kinda bogus to do this left-to-right, isn't it? If we eliminate * XXX It's kinda bogus to do this left-to-right, isn't it? If we
* some candidates because they are non-preferred at the first slot, we won't * eliminate some candidates because they are non-preferred at the first
* notice that they didn't have the same type category for a later slot. * slot, we won't notice that they didn't have the same type category for
* a later slot.
*/ */
for (i = 0; i < nargs; i++) for (i = 0; i < nargs; i++)
{ {
...@@ -400,20 +477,11 @@ oper_select_candidate(int nargs, ...@@ -400,20 +477,11 @@ oper_select_candidate(int nargs,
} }
} }
ncandidates = 0; if (candidates == NULL)
last_candidate = NULL; return NULL; /* no remaining candidates */
for (current_candidate = candidates; if (candidates->next != NULL)
current_candidate != NULL; return NULL; /* more than one remaining candidate */
current_candidate = current_candidate->next) return candidates->args;
{
if (can_coerce_type(nargs, input_typeids, current_candidate->args))
{
ncandidates++;
last_candidate = current_candidate;
}
}
return (ncandidates == 1) ? last_candidate->args : NULL;
} /* oper_select_candidate() */ } /* oper_select_candidate() */
...@@ -529,13 +597,13 @@ oper(char *opname, Oid ltypeId, Oid rtypeId, bool noWarnings) ...@@ -529,13 +597,13 @@ oper(char *opname, Oid ltypeId, Oid rtypeId, bool noWarnings)
/* unary_oper_get_candidates() /* unary_oper_get_candidates()
* given opname and typeId, find all possible types for which * given opname, find all possible types for which
* a right/left unary operator named opname exists, * a right/left unary operator named opname exists.
* such that typeId can be coerced to it * Build a list of the candidate input types.
* Returns number of candidates found.
*/ */
static int static int
unary_oper_get_candidates(char *op, unary_oper_get_candidates(char *opname,
Oid typeId,
CandidateList *candidates, CandidateList *candidates,
char rightleft) char rightleft)
{ {
...@@ -545,17 +613,19 @@ unary_oper_get_candidates(char *op, ...@@ -545,17 +613,19 @@ unary_oper_get_candidates(char *op,
HeapTuple tup; HeapTuple tup;
Form_pg_operator oper; Form_pg_operator oper;
int ncandidates = 0; int ncandidates = 0;
ScanKeyData opKey[2];
static ScanKeyData opKey[2] = {
{0, Anum_pg_operator_oprname, F_NAMEEQ},
{0, Anum_pg_operator_oprkind, F_CHAREQ}};
*candidates = NULL; *candidates = NULL;
fmgr_info(F_NAMEEQ, (FmgrInfo *) &opKey[0].sk_func); ScanKeyEntryInitialize(&opKey[0], 0,
opKey[0].sk_argument = NameGetDatum(op); Anum_pg_operator_oprname,
fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func); F_NAMEEQ,
opKey[1].sk_argument = CharGetDatum(rightleft); NameGetDatum(opname));
ScanKeyEntryInitialize(&opKey[1], 0,
Anum_pg_operator_oprkind,
F_CHAREQ,
CharGetDatum(rightleft));
pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock); pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock);
pg_operator_scan = heap_beginscan(pg_operator_desc, pg_operator_scan = heap_beginscan(pg_operator_desc,
...@@ -566,10 +636,11 @@ unary_oper_get_candidates(char *op, ...@@ -566,10 +636,11 @@ unary_oper_get_candidates(char *op,
while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0))) while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0)))
{ {
oper = (Form_pg_operator) GETSTRUCT(tup);
current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
current_candidate->args = (Oid *) palloc(sizeof(Oid)); current_candidate->args = (Oid *) palloc(sizeof(Oid));
oper = (Form_pg_operator) GETSTRUCT(tup);
if (rightleft == 'r') if (rightleft == 'r')
current_candidate->args[0] = oper->oprleft; current_candidate->args[0] = oper->oprleft;
else else
...@@ -606,7 +677,7 @@ right_oper(char *op, Oid arg) ...@@ -606,7 +677,7 @@ right_oper(char *op, Oid arg)
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
{ {
/* Try for inexact matches */ /* Try for inexact matches */
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r'); ncandidates = unary_oper_get_candidates(op, &candidates, 'r');
if (ncandidates == 0) if (ncandidates == 0)
{ {
unary_op_error(op, arg, FALSE); unary_op_error(op, arg, FALSE);
...@@ -658,7 +729,7 @@ left_oper(char *op, Oid arg) ...@@ -658,7 +729,7 @@ left_oper(char *op, Oid arg)
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
{ {
/* Try for inexact matches */ /* Try for inexact matches */
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l'); ncandidates = unary_oper_get_candidates(op, &candidates, 'l');
if (ncandidates == 0) if (ncandidates == 0)
{ {
unary_op_error(op, arg, TRUE); unary_op_error(op, arg, TRUE);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment