diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 688c5bfa30..9902b0cf92 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.94 2000/11/16 22:30:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.95 2000/12/15 19:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -759,14 +759,15 @@ func_select_candidate(int nargs, CandidateList current_candidate; CandidateList last_candidate; Oid *current_typeids; + Oid current_type; int i; int ncandidates; int nbestMatch, nmatch; - CATEGORY slot_category, + CATEGORY slot_category[FUNC_MAX_ARGS], current_category; - Oid slot_type, - current_type; + bool slot_has_preferred_type[FUNC_MAX_ARGS]; + bool resolved_unknowns; /* * Run through all candidates and keep those with the most matches on @@ -911,98 +912,133 @@ func_select_candidate(int nargs, * 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. + * 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. * - * 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. - * XXX Hmm. How else would you do this? These candidates are here because - * they all have the same number of matches on arguments with explicit - * types, so from here on left-to-right resolution is as good as any. - * Need a counterexample to see otherwise... + * 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. */ + resolved_unknowns = false; for (i = 0; i < nargs; i++) { - if (input_typeids[i] == UNKNOWNOID) + 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) { - slot_category = INVALID_TYPE; - slot_type = InvalidOid; - last_candidate = NULL; - 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) { - current_typeids = current_candidate->args; - current_type = current_typeids[i]; - current_category = TypeCategory(current_type); - if (slot_category == 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) { - slot_category = current_category; - slot_type = current_type; - last_candidate = current_candidate; - } - else if (current_category != slot_category) - { - /* started out as unknown type, so give preference to string type, if available */ - if (current_category == STRING_TYPE) - { - slot_category = current_category; - slot_type = current_type; - /* forget all previous candidates */ - candidates = current_candidate; - last_candidate = current_candidate; - } - else if (slot_category == STRING_TYPE) - { - /* forget this candidate */ - if (last_candidate) - last_candidate->next = current_candidate->next; - else - candidates = current_candidate->next; - } - } - else if (current_type != slot_type) - { - if (IsPreferredType(slot_category, current_type)) - { - slot_type = current_type; - /* forget all previous candidates */ - candidates = current_candidate; - last_candidate = current_candidate; - } - else if (IsPreferredType(slot_category, slot_type)) - { - /* forget this candidate */ - if (last_candidate) - last_candidate->next = current_candidate->next; - else - candidates = current_candidate->next; - } - else - last_candidate = current_candidate; + /* STRING always wins if available */ + slot_category[i] = current_category; + slot_has_preferred_type[i] = + IsPreferredType(current_category, current_type); } else { - /* keep this candidate */ - last_candidate = current_candidate; + /* Remember conflict, but keep going (might find STRING) */ + have_conflict = true; } } - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; + } + if (have_conflict && slot_category[i] != STRING_TYPE) + { + /* Failed to resolve category conflict at this position */ + resolved_unknowns = false; + break; } } - if (candidates == NULL) - return NULL; /* no remaining candidates */ - if (candidates->next != NULL) - return NULL; /* more than one remaining candidate */ + 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; - return candidates->args; + 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) + return candidates->args; + + return NULL; /* failed to determine a unique candidate */ } /* func_select_candidate() */ diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index a44a740dc9..dc1c267366 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.44 2000/11/16 22:30:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.45 2000/12/15 19:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -176,15 +176,16 @@ oper_select_candidate(int nargs, CandidateList current_candidate; CandidateList last_candidate; Oid *current_typeids; + Oid current_type; int unknownOids; int i; int ncandidates; int nbestMatch, nmatch; - CATEGORY slot_category, + CATEGORY slot_category[FUNC_MAX_ARGS], current_category; - Oid slot_type, - current_type; + bool slot_has_preferred_type[FUNC_MAX_ARGS]; + bool resolved_unknowns; /* * First, delete any candidates that cannot actually accept the given @@ -406,94 +407,135 @@ oper_select_candidate(int nargs, } /* - * 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. + * Second try: same algorithm as for unknown resolution in parse_func.c. * - * 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. + * 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. */ + resolved_unknowns = false; for (i = 0; i < nargs; i++) { - if (input_typeids[i] == UNKNOWNOID) + 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) { - slot_category = INVALID_TYPE; - slot_type = InvalidOid; - last_candidate = NULL; - 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) { - current_typeids = current_candidate->args; - current_type = current_typeids[i]; - current_category = TypeCategory(current_type); - /* first time through? Then we'll use this one for now */ - if (slot_category == 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) { - slot_category = current_category; - slot_type = current_type; - last_candidate = current_candidate; - } - else if (current_category != slot_category) - { - /* started out as unknown type, so give preference to string type, if available */ - if (current_category == STRING_TYPE) - { - slot_category = current_category; - slot_type = current_type; - /* forget all previous candidates */ - candidates = current_candidate; - last_candidate = current_candidate; - } - else if (slot_category == STRING_TYPE) - { - /* forget this candidate */ - if (last_candidate) - last_candidate->next = current_candidate->next; - else - candidates = current_candidate->next; - } - } - else if (current_type != slot_type) - { - if (IsPreferredType(slot_category, current_type)) - { - slot_type = current_type; - /* forget all previous candidates */ - candidates = current_candidate; - last_candidate = current_candidate; - } - else if (IsPreferredType(slot_category, slot_type)) - { - /* forget this candidate */ - if (last_candidate) - last_candidate->next = current_candidate->next; - else - candidates = current_candidate->next; - } - else - last_candidate = current_candidate; + /* STRING always wins if available */ + slot_category[i] = current_category; + slot_has_preferred_type[i] = + IsPreferredType(current_category, current_type); } else { - /* keep this candidate */ - last_candidate = current_candidate; + /* Remember conflict, but keep going (might find STRING) */ + have_conflict = true; } } - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; + } + if (have_conflict && slot_category[i] != STRING_TYPE) + { + /* Failed to resolve category conflict at this position */ + resolved_unknowns = false; + break; } } - if (candidates == NULL) - return NULL; /* no remaining candidates */ - if (candidates->next != NULL) - return NULL; /* more than one remaining candidate */ - return candidates->args; + 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) + return candidates->args; + + return NULL; /* failed to determine a unique candidate */ } /* oper_select_candidate() */