Commit 7d6af50f authored by Tom Lane's avatar Tom Lane

Make algorithm for resolving UNKNOWN function/operator inputs be

insensitive to the order of arguments.  Per pghackers discussion 12/10/00.
parent ff783fba
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -759,14 +759,15 @@ func_select_candidate(int nargs,
CandidateList current_candidate; CandidateList current_candidate;
CandidateList last_candidate; CandidateList last_candidate;
Oid *current_typeids; Oid *current_typeids;
Oid current_type;
int i; int i;
int ncandidates; int ncandidates;
int nbestMatch, int nbestMatch,
nmatch; nmatch;
CATEGORY slot_category, CATEGORY slot_category[FUNC_MAX_ARGS],
current_category; current_category;
Oid slot_type, bool slot_has_preferred_type[FUNC_MAX_ARGS];
current_type; bool resolved_unknowns;
/* /*
* Run through all candidates and keep those with the most matches on * Run through all candidates and keep those with the most matches on
...@@ -911,98 +912,133 @@ func_select_candidate(int nargs, ...@@ -911,98 +912,133 @@ func_select_candidate(int nargs,
* Still too many candidates? Try assigning types for the unknown * Still too many candidates? Try assigning types for the unknown
* columns. * columns.
* *
* We do this by examining each unknown argument position to see if all * We do this by examining each unknown argument position to see if we
* the candidates agree on the type category of that slot. If so, and * can determine a "type category" for it. If any candidate has an
* if some candidates accept the preferred type in that category, * input datatype of STRING category, use STRING category (this bias
* eliminate the candidates with other input types. If we are down to * towards STRING is appropriate since unknown-type literals look like
* one candidate at the end, we win. * 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 * If we are able to determine a type category, also notice whether
* eliminate some candidates because they are non-preferred at the * any of the candidates takes a preferred datatype within the category.
* first slot, we won't notice that they didn't have the same type *
* category for a later slot. * Having completed this examination, remove candidates that accept
* XXX Hmm. How else would you do this? These candidates are here because * the wrong category at any unknown position. Also, if at least one
* they all have the same number of matches on arguments with explicit * candidate accepted a preferred type at a position, remove candidates
* types, so from here on left-to-right resolution is as good as any. * that accept non-preferred types.
* Need a counterexample to see otherwise... *
* If we are down to one candidate at the end, we win.
*/ */
resolved_unknowns = false;
for (i = 0; i < nargs; i++) 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; current_typeids = current_candidate->args;
slot_type = InvalidOid; current_type = current_typeids[i];
last_candidate = NULL; current_category = TypeCategory(current_type);
for (current_candidate = candidates; if (slot_category[i] == INVALID_TYPE)
current_candidate != NULL;
current_candidate = current_candidate->next)
{ {
current_typeids = current_candidate->args; /* first candidate */
current_type = current_typeids[i]; slot_category[i] = current_category;
current_category = TypeCategory(current_type); slot_has_preferred_type[i] =
if (slot_category == INVALID_TYPE) 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; /* STRING always wins if available */
slot_type = current_type; slot_category[i] = current_category;
last_candidate = current_candidate; slot_has_preferred_type[i] =
IsPreferredType(current_category, current_type);
} }
else if (current_category != slot_category) else
{ {
/* started out as unknown type, so give preference to string type, if available */ /* Remember conflict, but keep going (might find STRING) */
if (current_category == STRING_TYPE) have_conflict = true;
{
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 (have_conflict && slot_category[i] != STRING_TYPE)
{
/* Failed to resolve category conflict at this position */
resolved_unknowns = false;
break;
}
}
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])
{ {
if (IsPreferredType(slot_category, current_type)) keepit = false;
{ break;
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;
} }
else if (slot_has_preferred_type[i] &&
!IsPreferredType(current_category, current_type))
{ {
/* keep this candidate */ keepit = false;
last_candidate = current_candidate; break;
} }
} }
if (last_candidate) /* terminate rebuilt list */ if (keepit)
last_candidate->next = NULL; {
/* 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 (candidates == NULL) if (ncandidates == 1)
return NULL; /* no remaining candidates */ return candidates->args;
if (candidates->next != NULL)
return NULL; /* more than one remaining candidate */
return candidates->args; return NULL; /* failed to determine a unique candidate */
} /* func_select_candidate() */ } /* func_select_candidate() */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -176,15 +176,16 @@ oper_select_candidate(int nargs,
CandidateList current_candidate; CandidateList current_candidate;
CandidateList last_candidate; CandidateList last_candidate;
Oid *current_typeids; Oid *current_typeids;
Oid current_type;
int unknownOids; int unknownOids;
int i; int i;
int ncandidates; int ncandidates;
int nbestMatch, int nbestMatch,
nmatch; nmatch;
CATEGORY slot_category, CATEGORY slot_category[FUNC_MAX_ARGS],
current_category; current_category;
Oid slot_type, bool slot_has_preferred_type[FUNC_MAX_ARGS];
current_type; bool resolved_unknowns;
/* /*
* First, delete any candidates that cannot actually accept the given * First, delete any candidates that cannot actually accept the given
...@@ -406,94 +407,135 @@ oper_select_candidate(int nargs, ...@@ -406,94 +407,135 @@ oper_select_candidate(int nargs,
} }
/* /*
* Second try: examine each unknown argument position to see if all * Second try: same algorithm as for unknown resolution in parse_func.c.
* 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 * We do this by examining each unknown argument position to see if we
* eliminate some candidates because they are non-preferred at the * can determine a "type category" for it. If any candidate has an
* first slot, we won't notice that they didn't have the same type * input datatype of STRING category, use STRING category (this bias
* category for a later slot. * 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++) 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; current_typeids = current_candidate->args;
slot_type = InvalidOid; current_type = current_typeids[i];
last_candidate = NULL; current_category = TypeCategory(current_type);
for (current_candidate = candidates; if (slot_category[i] == INVALID_TYPE)
current_candidate != NULL;
current_candidate = current_candidate->next)
{ {
current_typeids = current_candidate->args; /* first candidate */
current_type = current_typeids[i]; slot_category[i] = current_category;
current_category = TypeCategory(current_type); slot_has_preferred_type[i] =
/* first time through? Then we'll use this one for now */ IsPreferredType(current_category, current_type);
if (slot_category == INVALID_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; /* STRING always wins if available */
slot_type = current_type; slot_category[i] = current_category;
last_candidate = current_candidate; slot_has_preferred_type[i] =
IsPreferredType(current_category, current_type);
} }
else if (current_category != slot_category) else
{ {
/* started out as unknown type, so give preference to string type, if available */ /* Remember conflict, but keep going (might find STRING) */
if (current_category == STRING_TYPE) have_conflict = true;
{
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 (have_conflict && slot_category[i] != STRING_TYPE)
{
/* Failed to resolve category conflict at this position */
resolved_unknowns = false;
break;
}
}
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])
{ {
if (IsPreferredType(slot_category, current_type)) keepit = false;
{ break;
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;
} }
else if (slot_has_preferred_type[i] &&
!IsPreferredType(current_category, current_type))
{ {
/* keep this candidate */ keepit = false;
last_candidate = current_candidate; break;
} }
} }
if (last_candidate) /* terminate rebuilt list */ if (keepit)
last_candidate->next = NULL; {
/* 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 (candidates == NULL) if (ncandidates == 1)
return NULL; /* no remaining candidates */ return candidates->args;
if (candidates->next != NULL)
return NULL; /* more than one remaining candidate */ return NULL; /* failed to determine a unique candidate */
return candidates->args;
} /* oper_select_candidate() */ } /* oper_select_candidate() */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment