diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2003-05-26 00:11:29 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2003-05-26 00:11:29 +0000 |
commit | f45df8c0144005739d09387cb594baaaa08295a6 (patch) | |
tree | 45bf02ceab43e8eb24ff7c961cff9a89e3db2770 /src/backend/parser | |
parent | 297c1658ed35dc0ac4a13c190f29cc5e2ad49a0b (diff) |
Cause CHAR(n) to TEXT or VARCHAR conversion to automatically strip trailing
blanks, in hopes of reducing the surprise factor for newbies. Remove
redundant operators for VARCHAR (it depends wholly on TEXT operations now).
Clean up resolution of ambiguous operators/functions to avoid surprising
choices for domains: domains are treated as equivalent to their base types
and binary-coercibility is no longer considered a preference item when
choosing among multiple operators/functions. IsBinaryCoercible now correctly
reflects the notion that you need *only* relabel the type to get from type
A to type B: that is, a domain is binary-coercible to its base type, but
not vice versa. Various marginal cleanup, including merging the essentially
duplicate resolution code in parse_func.c and parse_oper.c. Improve opr_sanity
regression test to understand about binary compatibility (using pg_cast),
and fix a couple of small errors in the catalogs revealed thereby.
Restructure "special operator" handling to fetch operators via index opclasses
rather than hardwiring assumptions about names (cleans up the pattern_ops
stuff a little).
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/parse_coerce.c | 150 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 213 | ||||
-rw-r--r-- | src/backend/parser/parse_oper.c | 421 |
3 files changed, 221 insertions, 563 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 9dc0c7f1c19..fc35c2d6d41 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.96 2003/04/29 22:13:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.97 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,7 +32,6 @@ static Node *coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, bool isExplicit); -static Oid PreferredType(CATEGORY category, Oid type); static Node *build_func_call(Oid funcid, Oid rettype, List *args, CoercionForm fformat); @@ -66,28 +65,43 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, if (can_coerce_type(1, &exprtype, &targettype, ccontext)) expr = coerce_type(pstate, expr, exprtype, targettype, ccontext, cformat); - /* - * String hacks to get transparent conversions for char and varchar: - * if a coercion to text is available, use it for forced coercions to - * char(n) or varchar(n). - * - * This is pretty grotty, but seems easier to maintain than providing - * entries in pg_cast that parallel all the ones for text. - */ - else if (ccontext >= COERCION_ASSIGNMENT && - (targettype == BPCHAROID || targettype == VARCHAROID)) + else if (ccontext >= COERCION_ASSIGNMENT) { - Oid text_id = TEXTOID; + /* + * String hacks to get transparent conversions for char and varchar: + * if a coercion to text is available, use it for forced coercions to + * char(n) or varchar(n) or domains thereof. + * + * This is pretty grotty, but seems easier to maintain than providing + * entries in pg_cast that parallel all the ones for text. + */ + Oid targetbasetype = getBaseType(targettype); - if (can_coerce_type(1, &exprtype, &text_id, ccontext)) + if (targetbasetype == BPCHAROID || targetbasetype == VARCHAROID) { - expr = coerce_type(pstate, expr, exprtype, text_id, - ccontext, cformat); - /* Need a RelabelType if no typmod coercion is performed */ - if (targettypmod < 0) - expr = (Node *) makeRelabelType((Expr *) expr, - targettype, -1, - cformat); + Oid text_id = TEXTOID; + + if (can_coerce_type(1, &exprtype, &text_id, ccontext)) + { + expr = coerce_type(pstate, expr, exprtype, text_id, + ccontext, cformat); + if (targetbasetype != targettype) + { + /* need to coerce to domain over char or varchar */ + expr = coerce_to_domain(expr, targetbasetype, targettype, + cformat); + } + else + { + /* need a RelabelType if no typmod coercion will be performed */ + if (targettypmod < 0) + expr = (Node *) makeRelabelType((Expr *) expr, + targettype, -1, + cformat); + } + } + else + expr = NULL; } else expr = NULL; @@ -923,7 +937,10 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* TypeCategory() - * Assign a category to the specified OID. + * Assign a category to the specified type OID. + * + * NB: this must not return INVALID_TYPE. + * * XXX This should be moved to system catalog lookups * to allow for better type extensibility. * - thomas 2001-09-30 @@ -1026,7 +1043,11 @@ TypeCategory(Oid inType) /* IsPreferredType() - * Check if this type is a preferred type. + * Check if this type is a preferred type for the given category. + * + * If category is INVALID_TYPE, then we'll return TRUE for preferred types + * of any category; otherwise, only for preferred types of that category. + * * XXX This should be moved to system catalog lookups * to allow for better type extensibility. * - thomas 2001-09-30 @@ -1034,39 +1055,34 @@ TypeCategory(Oid inType) bool IsPreferredType(CATEGORY category, Oid type) { - return (type == PreferredType(category, type)); -} /* IsPreferredType() */ + Oid preftype; + if (category == INVALID_TYPE) + category = TypeCategory(type); + else if (category != TypeCategory(type)) + return false; -/* PreferredType() - * Return the preferred type OID for the specified category. - * XXX This should be moved to system catalog lookups - * to allow for better type extensibility. - * - thomas 2001-09-30 - */ -static Oid -PreferredType(CATEGORY category, Oid type) -{ - Oid result; - + /* + * This switch should agree with TypeCategory(), above. Note that + * at this point, category certainly matches the type. + */ switch (category) { - case (INVALID_TYPE): case (UNKNOWN_TYPE): case (GENERIC_TYPE): - result = UNKNOWNOID; + preftype = UNKNOWNOID; break; case (BOOLEAN_TYPE): - result = BOOLOID; + preftype = BOOLOID; break; case (STRING_TYPE): - result = TEXTOID; + preftype = TEXTOID; break; case (BITSTRING_TYPE): - result = VARBITOID; + preftype = VARBITOID; break; case (NUMERIC_TYPE): @@ -1077,52 +1093,59 @@ PreferredType(CATEGORY category, Oid type) type == REGOPERATOROID || type == REGCLASSOID || type == REGTYPEOID) - result = OIDOID; + preftype = OIDOID; else - result = FLOAT8OID; + preftype = FLOAT8OID; break; case (DATETIME_TYPE): if (type == DATEOID) - result = TIMESTAMPOID; + preftype = TIMESTAMPOID; else - result = TIMESTAMPTZOID; + preftype = TIMESTAMPTZOID; break; case (TIMESPAN_TYPE): - result = INTERVALOID; + preftype = INTERVALOID; break; case (GEOMETRIC_TYPE): - result = type; + preftype = type; break; case (NETWORK_TYPE): - result = INETOID; + preftype = INETOID; break; case (USER_TYPE): - result = type; + preftype = type; break; default: - elog(ERROR, "PreferredType: unknown category"); - result = UNKNOWNOID; + elog(ERROR, "IsPreferredType: unknown category"); + preftype = UNKNOWNOID; break; } - return result; -} /* PreferredType() */ + + return (type == preftype); +} /* IsPreferredType() */ /* IsBinaryCoercible() * Check if srctype is binary-coercible to targettype. * * This notion allows us to cheat and directly exchange values without - * going through the trouble of calling a conversion function. + * going through the trouble of calling a conversion function. Note that + * in general, this should only be an implementation shortcut. Before 7.4, + * this was also used as a heuristic for resolving overloaded functions and + * operators, but that's basically a bad idea. * * As of 7.3, binary coercibility isn't hardwired into the code anymore. * We consider two types binary-coercible if there is an implicitly - * invokable, no-function-needed pg_cast entry. + * invokable, no-function-needed pg_cast entry. Also, a domain is always + * binary-coercible to its base type, though *not* vice versa (in the other + * direction, one must apply domain constraint checks before accepting the + * value as legitimate). * * This function replaces IsBinaryCompatible(), which was an inherently * symmetric test. Since the pg_cast entries aren't necessarily symmetric, @@ -1139,13 +1162,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype) if (srctype == targettype) return true; - /* Perhaps the types are domains; if so, look at their base types */ + /* If srctype is a domain, reduce to its base type */ if (OidIsValid(srctype)) srctype = getBaseType(srctype); - if (OidIsValid(targettype)) - targettype = getBaseType(targettype); - /* Somewhat-fast path if same base type */ + /* Somewhat-fast path for domain -> base type case */ if (srctype == targettype) return true; @@ -1174,8 +1195,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype) * ccontext determines the set of available casts. * * If we find a suitable entry in pg_cast, return TRUE, and set *funcid - * to the castfunc value (which may be InvalidOid for a binary-compatible - * coercion). + * to the castfunc value, which may be InvalidOid for a binary-compatible + * coercion. + * + * NOTE: *funcid == InvalidOid does not necessarily mean that no work is + * needed to do the coercion; if the target is a domain then we may need to + * apply domain constraint checking. If you want to check for a zero-effort + * conversion then use IsBinaryCoercible(). */ bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, @@ -1193,7 +1219,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, if (OidIsValid(targetTypeId)) targetTypeId = getBaseType(targetTypeId); - /* Domains are automatically binary-compatible with their base type */ + /* Domains are always coercible to and from their base type */ if (sourceTypeId == targetTypeId) return true; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 058b9aad73d..e9a40e03179 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.147 2003/04/29 22:13:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.148 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,6 @@ #include "access/heapam.h" #include "catalog/catname.h" -#include "catalog/namespace.h" #include "catalog/pg_inherits.h" #include "catalog/pg_proc.h" #include "lib/stringinfo.h" @@ -37,13 +36,7 @@ static Oid **argtype_inherit(int nargs, Oid *argtypes); static int find_inheritors(Oid relid, Oid **supervec); static Oid **gen_cross_product(InhPaths *arginh, int nargs); -static int match_argtypes(int nargs, - Oid *input_typeids, - FuncCandidateList function_typeids, - FuncCandidateList *candidates); static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid); -static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids, - FuncCandidateList candidates); static void unknown_attribute(const char *schemaname, const char *relname, const char *attname); @@ -355,21 +348,24 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, } -/* match_argtypes() +/* func_match_argtypes() * - * Given a list of possible typeid arrays to a function and an array of - * input typeids, produce a shortlist of those function typeid arrays - * that match the input typeids (either exactly or by coercion), and - * return the number of such arrays. + * Given a list of candidate functions (having the right name and number + * of arguments) and an array of input datatype OIDs, produce a shortlist of + * those candidates that actually accept the input datatypes (either exactly + * or by coercion), and return the number of such candidates. + * + * Note that can_coerce_type will assume that UNKNOWN inputs are coercible to + * anything, so candidates will not be eliminated on that basis. * * NB: okay to modify input list structure, as long as we find at least - * one match. + * one match. If no match at all, the list must remain unmodified. */ -static int -match_argtypes(int nargs, - Oid *input_typeids, - FuncCandidateList function_typeids, - FuncCandidateList *candidates) /* return value */ +int +func_match_argtypes(int nargs, + Oid *input_typeids, + FuncCandidateList raw_candidates, + FuncCandidateList *candidates) /* return value */ { FuncCandidateList current_candidate; FuncCandidateList next_candidate; @@ -377,7 +373,7 @@ match_argtypes(int nargs, *candidates = NULL; - for (current_candidate = function_typeids; + for (current_candidate = raw_candidates; current_candidate != NULL; current_candidate = next_candidate) { @@ -392,21 +388,65 @@ match_argtypes(int nargs, } return ncandidates; -} /* match_argtypes() */ +} /* func_match_argtypes() */ /* func_select_candidate() - * Given the input argtype array and more than one candidate - * for the function, attempt to resolve the conflict. + * Given the input argtype array and more than one candidate + * for the function, attempt to resolve the conflict. + * * Returns the selected candidate 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. + * Note that the caller has already determined that there is no candidate + * exactly matching the input argtypes, and has pruned away any "candidates" + * that aren't actually coercion-compatible with the input types. + * + * This is also used for resolving ambiguous operator references. Formerly + * parse_oper.c had its own, essentially duplicate code for the purpose. + * The following comments (formerly in parse_oper.c) are kept to record some + * of the history of these heuristics. + * + * OLD COMMENTS: + * + * 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. + * The important thing is that the code do as much as possible, + * while _never_ doing the wrong thing, where "the wrong thing" would + * be returning an operator when other better choices are available, + * or returning an operator which is a non-intuitive possibility. + * - thomas 1998-05-21 + * + * The comments below came from binary_oper_select_candidate(), and + * illustrate the issues and choices which are possible: + * - thomas 1998-05-20 + * + * current wisdom holds that the default operator should be one in which + * both operands have the same type (there will only be one such + * operator) + * + * 7.27.93 - I have decided not to do this; it's too hard to justify, and + * it's easy enough to typecast explicitly - avi + * [the rest of this routine was commented out since then - ay] + * + * 6/23/95 - I don't complete agree with avi. In particular, casting + * floats is a pain for users. Whatever the rationale behind not doing + * this is, I need the following special case to work. + * + * In the WHERE clause of a query, if a float is specified without + * quotes, we treat it as float8. I added the float48* operators so + * that we can operate on float4 and float8. But now we have more than + * one matching operator if the right arg is unknown (eg. float + * specified with quotes). This break some stuff in the regression + * test where there are floats in quotes not properly casted. Below is + * the solution. In addition to requiring the operator operates on the + * same type for both operands [as in the code Avi originally + * commented out], we also require that the operators be equivalent in + * some sense. (see equivalentOpersAfterPromotion for details.) + * - ay 6/95 */ -static FuncCandidateList +FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids, FuncCandidateList candidates) @@ -419,59 +459,28 @@ func_select_candidate(int nargs, int ncandidates; int nbestMatch, nmatch; + Oid input_base_typeids[FUNC_MAX_ARGS]; CATEGORY slot_category[FUNC_MAX_ARGS], current_category; bool slot_has_preferred_type[FUNC_MAX_ARGS]; bool resolved_unknowns; /* - * Run through all candidates and keep those with the most matches on - * exact types. Keep all candidates if none match. + * If any input types are domains, reduce them to their base types. + * This ensures that we will consider functions on the base type to be + * "exact matches" in the exact-match heuristic; it also makes it possible + * to do something useful with the type-category heuristics. Note that + * this makes it difficult, but not impossible, to use functions declared + * to take a domain as an input datatype. Such a function will be + * selected over the base-type function only if it is an exact match at + * all argument positions, and so was already chosen by our caller. */ - 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; + for (i = 0; i < nargs; i++) + input_base_typeids[i] = getBaseType(input_typeids[i]); /* - * 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. + * Run through all candidates and keep those with the most matches on + * exact types. Keep all candidates if none match. */ ncandidates = 0; nbestMatch = 0; @@ -484,11 +493,9 @@ func_select_candidate(int nargs, nmatch = 0; for (i = 0; i < nargs; i++) { - if (input_typeids[i] != UNKNOWNOID) - { - if (IsBinaryCoercible(input_typeids[i], current_typeids[i])) - nmatch++; - } + if (input_base_typeids[i] != UNKNOWNOID && + current_typeids[i] == input_base_typeids[i]) + nmatch++; } /* take this one as the best choice so far? */ @@ -516,10 +523,14 @@ func_select_candidate(int nargs, return candidates; /* - * 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. + * Still too many candidates? Now look for candidates which have either + * exact matches or preferred types at the args that will require coercion. + * (Restriction added in 7.4: preferred type must be of same category as + * input type; give no preference to cross-category conversions to + * preferred types.) Keep all candidates if none match. */ + for (i = 0; i < nargs; i++) /* avoid multiple lookups */ + slot_category[i] = TypeCategory(input_base_typeids[i]); ncandidates = 0; nbestMatch = 0; last_candidate = NULL; @@ -531,11 +542,10 @@ func_select_candidate(int nargs, nmatch = 0; for (i = 0; i < nargs; i++) { - if (input_typeids[i] != UNKNOWNOID) + if (input_base_typeids[i] != UNKNOWNOID) { - current_category = TypeCategory(current_typeids[i]); - if (current_typeids[i] == input_typeids[i] || - IsPreferredType(current_category, current_typeids[i])) + if (current_typeids[i] == input_base_typeids[i] || + IsPreferredType(slot_category[i], current_typeids[i])) nmatch++; } } @@ -565,6 +575,11 @@ func_select_candidate(int nargs, * Still too many candidates? Try assigning types for the unknown * columns. * + * NOTE: for a binary operator with one unknown and one non-unknown input, + * we already tried the heuristic of looking for a candidate with the + * known input type on both sides (see binary_oper_exact()). That's + * essentially a special case of the general algorithm we try next. + * * We do this by examining each unknown argument position to see if we * can determine a "type category" for it. If any candidate has an * input datatype of STRING category, use STRING category (this bias @@ -588,7 +603,7 @@ func_select_candidate(int nargs, { bool have_conflict; - if (input_typeids[i] != UNKNOWNOID) + if (input_base_typeids[i] != UNKNOWNOID) continue; resolved_unknowns = true; /* assume we can do it */ slot_category[i] = INVALID_TYPE; @@ -656,7 +671,7 @@ func_select_candidate(int nargs, current_typeids = current_candidate->args; for (i = 0; i < nargs; i++) { - if (input_typeids[i] != UNKNOWNOID) + if (input_base_typeids[i] != UNKNOWNOID) continue; current_type = current_typeids[i]; current_category = TypeCategory(current_type); @@ -694,7 +709,7 @@ func_select_candidate(int nargs, if (ncandidates == 1) return candidates; - return NULL; /* failed to determine a unique candidate */ + return NULL; /* failed to select a best candidate */ } /* func_select_candidate() */ @@ -734,16 +749,17 @@ func_get_detail(List *funcname, bool *retset, /* return value */ Oid **true_typeids) /* return value */ { - FuncCandidateList function_typeids; + FuncCandidateList raw_candidates; FuncCandidateList best_candidate; /* Get list of possible candidates from namespace search */ - function_typeids = FuncnameGetCandidates(funcname, nargs); + raw_candidates = FuncnameGetCandidates(funcname, nargs); /* - * See if there is an exact match + * Quickly check if there is an exact match to the input datatypes + * (there can be only one) */ - for (best_candidate = function_typeids; + for (best_candidate = raw_candidates; best_candidate != NULL; best_candidate = best_candidate->next) { @@ -815,7 +831,7 @@ func_get_detail(List *funcname, * didn't find an exact match, so now try to match up * candidates... */ - if (function_typeids != NULL) + if (raw_candidates != NULL) { Oid **input_typeid_vector = NULL; Oid *current_input_typeids; @@ -829,17 +845,18 @@ func_get_detail(List *funcname, do { - FuncCandidateList current_function_typeids; + FuncCandidateList current_candidates; int ncandidates; - ncandidates = match_argtypes(nargs, current_input_typeids, - function_typeids, - ¤t_function_typeids); + ncandidates = func_match_argtypes(nargs, + current_input_typeids, + raw_candidates, + ¤t_candidates); /* one match only? then run with it... */ if (ncandidates == 1) { - best_candidate = current_function_typeids; + best_candidate = current_candidates; break; } @@ -851,7 +868,7 @@ func_get_detail(List *funcname, { best_candidate = func_select_candidate(nargs, current_input_typeids, - current_function_typeids); + current_candidates); /* * If we were able to choose a best candidate, we're diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 6238258ed2f..21f73994c42 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,18 +8,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.63 2003/04/29 22:13:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.64 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/genam.h" -#include "access/heapam.h" -#include "catalog/catname.h" -#include "catalog/indexing.h" -#include "catalog/namespace.h" #include "catalog/pg_operator.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -289,107 +284,29 @@ binary_oper_exact(Oid arg1, Oid arg2, /* oper_select_candidate() - * Given the input argtype array and one or more candidates - * for the function argtype array, attempt to resolve the conflict. - * Returns the selected argtype array if the conflict can be resolved, - * otherwise returns NULL. + * Given the input argtype array and one or more candidates + * for the operator, attempt to resolve the conflict. * - * 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. + * Returns the OID of the selected operator if the conflict can be resolved, + * otherwise returns InvalidOid. * - * 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. - * The important thing is that the code do as much as possible, - * while _never_ doing the wrong thing, where "the wrong thing" would - * be returning an operator when other better choices are available, - * or returning an operator which is a non-intuitive possibility. - * - thomas 1998-05-21 - * - * The comments below came from binary_oper_select_candidate(), and - * illustrate the issues and choices which are possible: - * - thomas 1998-05-20 - * - * current wisdom holds that the default operator should be one in which - * both operands have the same type (there will only be one such - * operator) - * - * 7.27.93 - I have decided not to do this; it's too hard to justify, and - * it's easy enough to typecast explicitly - avi - * [the rest of this routine was commented out since then - ay] - * - * 6/23/95 - I don't complete agree with avi. In particular, casting - * floats is a pain for users. Whatever the rationale behind not doing - * this is, I need the following special case to work. - * - * In the WHERE clause of a query, if a float is specified without - * quotes, we treat it as float8. I added the float48* operators so - * that we can operate on float4 and float8. But now we have more than - * one matching operator if the right arg is unknown (eg. float - * specified with quotes). This break some stuff in the regression - * test where there are floats in quotes not properly casted. Below is - * the solution. In addition to requiring the operator operates on the - * same type for both operands [as in the code Avi originally - * commented out], we also require that the operators be equivalent in - * some sense. (see equivalentOpersAfterPromotion for details.) - * - ay 6/95 + * Note that the caller has already determined that there is no candidate + * exactly matching the input argtype(s). Incompatible candidates are not yet + * pruned away, however. */ static Oid oper_select_candidate(int nargs, Oid *input_typeids, FuncCandidateList candidates) { - FuncCandidateList current_candidate; - FuncCandidateList last_candidate; - Oid *current_typeids; - Oid current_type; - int unknownOids; - int i; int ncandidates; - int nbestMatch, - nmatch; - CATEGORY slot_category[FUNC_MAX_ARGS], - current_category; - bool slot_has_preferred_type[FUNC_MAX_ARGS]; - bool resolved_unknowns; /* - * 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.) + * Delete any candidates that cannot actually accept the given + * input types, whether directly or by coercion. */ - 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, - COERCION_IMPLICIT)) - { - 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; + ncandidates = func_match_argtypes(nargs, input_typeids, + candidates, &candidates); /* Done if no candidate or only one candidate survives */ if (ncandidates == 0) @@ -398,317 +315,15 @@ oper_select_candidate(int nargs, return candidates->oid; /* - * 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->oid; - - /* - * 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 (IsBinaryCoercible(input_typeids[i], current_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->oid; - - /* - * 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->oid; - - /* - * 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++) - { - if ((input_typeids[i] != UNKNOWNOID) - && (input_typeids[i] != InvalidOid)) - current_type = input_typeids[i]; - else - unknownOids = TRUE; - } - - if (unknownOids && (current_type != UNKNOWNOID)) - { - 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 (current_type == current_typeids[i]) - nmatch++; - } - if (nmatch == nargs) - return current_candidate->oid; - } - } - - /* - * Second try: same algorithm as for unknown resolution in - * parse_func.c. - * - * We do this by examining each unknown argument position to see if we - * can determine a "type category" for it. If any candidate has an - * input datatype of STRING category, use STRING category (this bias - * towards STRING is appropriate since unknown-type literals look like - * strings). Otherwise, if all the candidates agree on the type - * category of this argument position, use that category. Otherwise, - * fail because we cannot determine a category. - * - * If we are able to determine a type category, also notice whether any - * of the candidates takes a preferred datatype within the category. - * - * Having completed this examination, remove candidates that accept the - * wrong category at any unknown position. Also, if at least one - * candidate accepted a preferred type at a position, remove - * candidates that accept non-preferred types. - * - * If we are down to one candidate at the end, we win. + * Use the same heuristics as for ambiguous functions to resolve + * the conflict. */ - resolved_unknowns = false; - for (i = 0; i < nargs; i++) - { - bool have_conflict; - - if (input_typeids[i] != UNKNOWNOID) - continue; - resolved_unknowns = true; /* assume we can do it */ - slot_category[i] = INVALID_TYPE; - slot_has_preferred_type[i] = false; - have_conflict = false; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - current_type = current_typeids[i]; - current_category = TypeCategory(current_type); - if (slot_category[i] == INVALID_TYPE) - { - /* first candidate */ - slot_category[i] = current_category; - slot_has_preferred_type[i] = - IsPreferredType(current_category, current_type); - } - else if (current_category == slot_category[i]) - { - /* more candidates in same category */ - slot_has_preferred_type[i] |= - IsPreferredType(current_category, current_type); - } - else - { - /* category conflict! */ - if (current_category == STRING_TYPE) - { - /* STRING always wins if available */ - slot_category[i] = current_category; - slot_has_preferred_type[i] = - IsPreferredType(current_category, current_type); - } - else - { - /* - * Remember conflict, but keep going (might find - * STRING) - */ - have_conflict = true; - } - } - } - if (have_conflict && slot_category[i] != STRING_TYPE) - { - /* Failed to resolve category conflict at this position */ - resolved_unknowns = false; - break; - } - } + candidates = func_select_candidate(nargs, input_typeids, candidates); - if (resolved_unknowns) - { - /* Strip non-matching candidates */ - ncandidates = 0; - last_candidate = NULL; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - bool keepit = true; - - current_typeids = current_candidate->args; - for (i = 0; i < nargs; i++) - { - if (input_typeids[i] != UNKNOWNOID) - continue; - current_type = current_typeids[i]; - current_category = TypeCategory(current_type); - if (current_category != slot_category[i]) - { - keepit = false; - break; - } - if (slot_has_preferred_type[i] && - !IsPreferredType(current_category, current_type)) - { - keepit = false; - break; - } - } - if (keepit) - { - /* keep this candidate */ - last_candidate = current_candidate; - ncandidates++; - } - else - { - /* forget this candidate */ - if (last_candidate) - last_candidate->next = current_candidate->next; - else - candidates = current_candidate->next; - } - } - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; - } - - if (ncandidates == 1) + if (candidates) return candidates->oid; - return InvalidOid; /* failed to determine a unique candidate */ + return InvalidOid; /* failed to select a best candidate */ } /* oper_select_candidate() */ @@ -751,7 +366,7 @@ oper(List *opname, Oid ltypeId, Oid rtypeId, bool noError) /* * Unspecified type for one of the arguments? then use the - * other + * other (XXX this is probably dead code?) */ if (rtypeId == InvalidOid) rtypeId = ltypeId; |