diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2000-03-19 00:19:39 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2000-03-19 00:19:39 +0000 |
commit | 440b0fea3d3e3ed6a07f3777fd12a70fb7fbf12b (patch) | |
tree | 348546899e2222a738fc43ae3c03802df6db1b51 /src/backend/parser/parse_func.c | |
parent | f11d253e25fccc93704803d0840a8e7ada46249e (diff) | |
download | postgresql-440b0fea3d3e3ed6a07f3777fd12a70fb7fbf12b.tar.gz postgresql-440b0fea3d3e3ed6a07f3777fd12a70fb7fbf12b.zip |
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.
Diffstat (limited to 'src/backend/parser/parse_func.c')
-rw-r--r-- | src/backend/parser/parse_func.c | 374 |
1 files changed, 245 insertions, 129 deletions
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index e24007a4393..7134d5e7811 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.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; @@ -478,40 +479,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 * transform relation name arguments into varnodes of the appropriate @@ -615,9 +582,74 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, exists = func_get_detail(funcname, nargs, oid_array, &funcid, &rettype, &retset, &true_oid_array); if (!exists) - elog(ERROR, "No such function '%s' with the specified attributes", - funcname); + { + /* + * If we can't find a function (or can't find a unique function), + * see if this is really a type-coercion request: single-argument + * function call where the function name is a type name. If so, + * and if we can do the coercion trivially, just go ahead and do + * it without requiring there to be a real function for it. + * + * "Trivial" coercions are ones that involve binary-compatible + * types and ones that are coercing a previously-unknown-type + * literal constant to a specific type. + * + * DO NOT try to generalize this code to nontrivial coercions, + * because you'll just set up an infinite recursion between this + * routine and coerce_type! We have already failed to find a + * suitable "real" coercion function, so we have to fail unless + * this is a coercion that coerce_type can handle by itself. + * Make sure this code stays in sync with what coerce_type does! + */ + if (nargs == 1) + { + Type tp; + tp = SearchSysCacheTuple(TYPENAME, + PointerGetDatum(funcname), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Oid sourceType = oid_array[0]; + Oid targetType = typeTypeId(tp); + Node *arg1 = lfirst(fargs); + + if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) || + sourceType == targetType || + IS_BINARY_COMPATIBLE(sourceType, targetType)) + { + /* Ah-hah, we can do it as a trivial coercion. + * coerce_type can handle these cases, so why + * duplicate code... + */ + return coerce_type(pstate, arg1, + sourceType, targetType, -1); + } + } + } + + /* + * Oops. Time to die. + * + * If there is a single argument of complex type, we might be + * dealing with the PostQuel notation rel.function instead of + * the more usual function(rel). Give a nonspecific error + * message that will cover both cases. + */ + if (nargs == 1) + { + Type tp = typeidType(oid_array[0]); + if (typeTypeFlag(tp) == 'c') + elog(ERROR, "No such attribute or function '%s'", + funcname); + } + + /* Else generate a detailed complaint */ + func_error(NULL, funcname, nargs, oid_array, + "Unable to identify a function that satisfies the " + "given argument types" + "\n\tYou may need to add explicit typecasts"); + } } /* got it */ @@ -760,7 +792,7 @@ func_get_candidates(char *funcname, int nargs) heapRelation = heap_openr(ProcedureRelationName, AccessShareLock); ScanKeyEntryInitialize(&skey, (bits16) 0x0, - (AttrNumber) 1, + (AttrNumber) Anum_pg_proc_proname, (RegProcedure) F_NAMEEQ, (Datum) funcname); @@ -851,8 +883,13 @@ match_argtypes(int nargs, /* func_select_candidate() * Given the input argtype array and more than one candidate * for the function argtype array, attempt to resolve the conflict. - * returns the selected argtype array if the conflict can be resolved, + * Returns the selected argtype array if the conflict can be resolved, * otherwise returns NULL. + * + * By design, this is pretty similar to oper_select_candidate in parse_oper.c. + * However, the calling convention is a little different: we assume the caller + * already pruned away "candidates" that aren't actually coercion-compatible + * with the input types, whereas oper_select_candidate must do that itself. */ static Oid * func_select_candidate(int nargs, @@ -865,17 +902,62 @@ func_select_candidate(int nargs, int i; int ncandidates; int nbestMatch, - nmatch, - ncompat; + nmatch; CATEGORY slot_category, current_category; Oid slot_type, current_type; -/* - * Run through all candidates and keep those with the most matches - * on explicit types. Keep all candidates if none match. - */ + /* + * Run through all candidates and keep those with the most matches + * on exact types. Keep all candidates if none match. + */ + ncandidates = 0; + nbestMatch = 0; + last_candidate = NULL; + for (current_candidate = candidates; + current_candidate != NULL; + current_candidate = current_candidate->next) + { + current_typeids = current_candidate->args; + nmatch = 0; + for (i = 0; i < nargs; i++) + { + if (input_typeids[i] != UNKNOWNOID && + current_typeids[i] == input_typeids[i]) + nmatch++; + } + + /* take this one as the best choice so far? */ + if ((nmatch > nbestMatch) || (last_candidate == NULL)) + { + nbestMatch = nmatch; + candidates = current_candidate; + last_candidate = current_candidate; + ncandidates = 1; + } + /* no worse than the last choice, so keep this one too? */ + else if (nmatch == nbestMatch) + { + last_candidate->next = current_candidate; + last_candidate = current_candidate; + ncandidates++; + } + /* otherwise, don't bother keeping this one... */ + } + + if (last_candidate) /* terminate rebuilt list */ + last_candidate->next = NULL; + + if (ncandidates == 1) + return candidates->args; + + /* + * Still too many candidates? + * Run through all candidates and keep those with the most matches + * on exact types + binary-compatible types. + * Keep all candidates if none match. + */ ncandidates = 0; nbestMatch = 0; last_candidate = NULL; @@ -885,29 +967,17 @@ func_select_candidate(int nargs, { current_typeids = current_candidate->args; nmatch = 0; - ncompat = 0; for (i = 0; i < nargs; i++) { if (input_typeids[i] != UNKNOWNOID) { - if (current_typeids[i] == input_typeids[i]) + if (current_typeids[i] == input_typeids[i] || + IS_BINARY_COMPATIBLE(current_typeids[i], + input_typeids[i])) nmatch++; - else if (IS_BINARY_COMPATIBLE(current_typeids[i], - input_typeids[i])) - ncompat++; } } - /* - * If we find an exact match at all arg positions, we're done; - * there can be only one such candidate. - */ - if (nmatch == nargs) - return current_candidate->args; - - /* Otherwise, use match+compat as the score. */ - nmatch += ncompat; - /* take this one as the best choice so far? */ if ((nmatch > nbestMatch) || (last_candidate == NULL)) { @@ -932,20 +1002,68 @@ func_select_candidate(int nargs, if (ncandidates == 1) return candidates->args; -/* - * Still too many candidates? - * Try assigning types for the unknown columns. - * - * We do this by examining each unknown argument position to see if all the - * candidates agree on the type category of that slot. If so, and if some - * candidates accept the preferred type in that category, eliminate the - * candidates with other input types. If we are down to one candidate - * at the end, we win. - * - * XXX It's kinda bogus to do this left-to-right, isn't it? If we eliminate - * some candidates because they are non-preferred at the first slot, we won't - * notice that they didn't have the same type category for a later slot. - */ + /* + * Still too many candidates? + * Now look for candidates which are preferred types at the args that + * will require coercion. + * Keep all candidates if none match. + */ + ncandidates = 0; + nbestMatch = 0; + last_candidate = NULL; + for (current_candidate = candidates; + current_candidate != NULL; + current_candidate = current_candidate->next) + { + current_typeids = current_candidate->args; + nmatch = 0; + for (i = 0; i < nargs; i++) + { + if (input_typeids[i] != UNKNOWNOID) + { + current_category = TypeCategory(current_typeids[i]); + if (current_typeids[i] == input_typeids[i] || + IsPreferredType(current_category, current_typeids[i])) + nmatch++; + } + } + + if ((nmatch > nbestMatch) || (last_candidate == NULL)) + { + nbestMatch = nmatch; + candidates = current_candidate; + last_candidate = current_candidate; + ncandidates = 1; + } + else if (nmatch == nbestMatch) + { + last_candidate->next = current_candidate; + last_candidate = current_candidate; + ncandidates++; + } + } + + if (last_candidate) /* terminate rebuilt list */ + last_candidate->next = NULL; + + if (ncandidates == 1) + return candidates->args; + + /* + * Still too many candidates? + * Try assigning types for the unknown columns. + * + * We do this by examining each unknown argument position to see if all the + * candidates agree on the type category of that slot. If so, and if some + * candidates accept the preferred type in that category, eliminate the + * candidates with other input types. If we are down to one candidate + * at the end, we win. + * + * XXX It's kinda bogus to do this left-to-right, isn't it? If we + * eliminate some candidates because they are non-preferred at the first + * slot, we won't notice that they didn't have the same type category for + * a later slot. + */ for (i = 0; i < nargs; i++) { if (input_typeids[i] == UNKNOWNOID) @@ -1002,16 +1120,11 @@ func_select_candidate(int nargs, } } - ncandidates = 0; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - ncandidates++; - - if (ncandidates == 1) - return candidates->args; - - return NULL; + if (candidates == NULL) + return NULL; /* no remaining candidates */ + if (candidates->next != NULL) + return NULL; /* more than one remaining candidate */ + return candidates->args; } /* func_select_candidate() */ @@ -1043,12 +1156,7 @@ func_get_detail(char *funcname, bool *retset, /* return value */ Oid **true_typeids) /* return value */ { - Oid **input_typeid_vector; - Oid *current_input_typeids; - CandidateList function_typeids; - CandidateList current_function_typeids; HeapTuple ftup; - Form_pg_proc pform; /* attempt to find with arguments exactly as specified... */ ftup = SearchSysCacheTuple(PROCNAME, @@ -1056,23 +1164,37 @@ func_get_detail(char *funcname, Int32GetDatum(nargs), PointerGetDatum(oid_array), 0); - *true_typeids = oid_array; - /* didn't find an exact match, so now try to match up candidates... */ - if (!HeapTupleIsValid(ftup)) + if (HeapTupleIsValid(ftup)) { + /* given argument types are the right ones */ + *true_typeids = oid_array; + } + else + { + /* didn't find an exact match, so now try to match up candidates... */ + CandidateList function_typeids; + function_typeids = func_get_candidates(funcname, nargs); /* found something, so let's look through them... */ if (function_typeids != NULL) { - int ncandidates; + Oid **input_typeid_vector = NULL; + Oid *current_input_typeids; - input_typeid_vector = argtype_inherit(nargs, oid_array); + /* + * First we will search with the given oid_array, then with + * variants based on replacing complex types with their + * inheritance ancestors. Stop as soon as any match is found. + */ current_input_typeids = oid_array; do { + CandidateList current_function_typeids; + int ncandidates; + ncandidates = match_argtypes(nargs, current_input_typeids, function_typeids, ¤t_function_typeids); @@ -1087,29 +1209,22 @@ func_get_detail(char *funcname, PointerGetDatum(*true_typeids), 0); Assert(HeapTupleIsValid(ftup)); + break; } /* * multiple candidates? then better decide or throw an * error... */ - else if (ncandidates > 1) + if (ncandidates > 1) { *true_typeids = func_select_candidate(nargs, current_input_typeids, current_function_typeids); - /* couldn't decide, so quit */ - if (*true_typeids == NULL) - { - func_error(NULL, funcname, nargs, oid_array, - "Unable to identify a function which satisfies the given argument types" - "\n\tYou will have to retype your query using explicit typecasts"); - } - - /* found something, so use the first one... */ - else + if (*true_typeids != NULL) { + /* was able to choose a best candidate */ ftup = SearchSysCacheTuple(PROCNAME, PointerGetDatum(funcname), Int32GetDatum(nargs), @@ -1117,35 +1232,34 @@ func_get_detail(char *funcname, 0); Assert(HeapTupleIsValid(ftup)); } + /* otherwise, ambiguous function call, so fail by + * exiting loop with ftup still NULL. + */ + break; } + + /* + * No match here, so try the next inherited type vector. + * First time through, we need to compute the list of vectors. + */ + if (input_typeid_vector == NULL) + input_typeid_vector = argtype_inherit(nargs, oid_array); + current_input_typeids = *input_typeid_vector++; } - while (current_input_typeids != InvalidOid && ncandidates == 0); + while (current_input_typeids != NULL); } } - if (!HeapTupleIsValid(ftup)) + if (HeapTupleIsValid(ftup)) { - Type tp; + Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup); - if (nargs == 1) - { - tp = typeidType(oid_array[0]); - if (typeTypeFlag(tp) == 'c') - elog(ERROR, "No such attribute or function '%s'", funcname); - } - } - else - { - pform = (Form_pg_proc) GETSTRUCT(ftup); *funcid = ftup->t_data->t_oid; *rettype = pform->prorettype; *retset = pform->proretset; - return true; } - - /* shouldn't reach here */ return false; } /* func_get_detail() */ @@ -1677,11 +1791,13 @@ func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg) if (caller == NULL) { elog(ERROR, "Function '%s(%s)' does not exist%s%s", - funcname, p, ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : "")); + funcname, p, + ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : "")); } else { elog(ERROR, "%s: function '%s(%s)' does not exist%s%s", - caller, funcname, p, ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : "")); + caller, funcname, p, + ((msg != NULL) ? "\n\t" : ""), ((msg != NULL) ? msg : "")); } } |