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.
This commit is contained in:
Tom Lane 2000-03-19 00:19:39 +00:00
parent f11d253e25
commit 440b0fea3d
2 changed files with 417 additions and 230 deletions

View 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,16 @@ 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;
@ -885,29 +921,13 @@ 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])
nmatch++;
else if (IS_BINARY_COMPATIBLE(current_typeids[i],
input_typeids[i]))
ncompat++;
}
if (input_typeids[i] != UNKNOWNOID &&
current_typeids[i] == input_typeids[i])
nmatch++;
}
/*
* 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 +952,118 @@ 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?
* 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 (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? */
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?
* 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 : ""));
}
}

View 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,65 @@ oper_select_candidate(int nargs,
if (last_candidate) /* terminate rebuilt list */
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)
{
if (ncandidates > 0)
current_typeids = current_candidate->args;
nmatch = 0;
for (i = 0; i < nargs; i++)
{
if (!can_coerce_type(nargs, input_typeids, candidates->args))
ncandidates = 0;
if (input_typeids[i] != UNKNOWNOID)
{
if (current_typeids[i] == input_typeids[i] ||
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... */
}
/*
* Still too many candidates?
* Now look for candidates which allow coercion and are preferred types.
* Keep all candidates if none match.
*/
if (last_candidate) /* terminate rebuilt list */
last_candidate->next = NULL;
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;
@ -253,10 +342,8 @@ 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] ||
IsPreferredType(current_category, current_typeids[i]))
nmatch++;
}
}
@ -279,27 +366,20 @@ 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?
* 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.
*/
/*
* 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);