Make algorithm for resolving UNKNOWN function/operator inputs be
authorTom Lane
Fri, 15 Dec 2000 19:22:03 +0000 (19:22 +0000)
committerTom Lane
Fri, 15 Dec 2000 19:22:03 +0000 (19:22 +0000)
insensitive to the order of arguments.  Per pghackers discussion 12/10/00.

src/backend/parser/parse_func.c
src/backend/parser/parse_oper.c

index 688c5bfa306c4ebb2227d25f02500efbb8fdd981..9902b0cf92d3f201027e85da49f1e220a055619c 100644 (file)
@@ -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;
+                   /* STRING always wins if available */
+                   slot_category[i] = current_category;
+                   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 */
-                   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;
-                   }
+                   /* Remember conflict, but keep going (might find STRING) */
+                   have_conflict = true;
                }
-               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))
-                   {
-                       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;
+                   keepit = false;
+                   break;
                }
-               else
+               if (slot_has_preferred_type[i] &&
+                   !IsPreferredType(current_category, current_type))
                {
-                   /* keep this candidate */
-                   last_candidate = current_candidate;
+                   keepit = false;
+                   break;
                }
            }
-           if (last_candidate) /* terminate rebuilt list */
-               last_candidate->next = NULL;
+           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 (candidates == NULL)
-       return NULL;            /* no remaining candidates */
-   if (candidates->next != NULL)
-       return NULL;            /* more than one remaining candidate */
+   if (ncandidates == 1)
+       return candidates->args;
 
-   return candidates->args;
+   return NULL;                /* failed to determine a unique candidate */
 }  /* func_select_candidate() */
 
 
index a44a740dc926045b6dba4f7cfaa5835805a164fe..dc1c267366e0a95745b5e33eb639bb9177ec3981 100644 (file)
@@ -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;
+                   /* STRING always wins if available */
+                   slot_category[i] = current_category;
+                   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 */
-                   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;
-                   }
+                   /* Remember conflict, but keep going (might find STRING) */
+                   have_conflict = true;
                }
-               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))
-                   {
-                       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;
+                   keepit = false;
+                   break;
                }
-               else
+               if (slot_has_preferred_type[i] &&
+                   !IsPreferredType(current_category, current_type))
                {
-                   /* keep this candidate */
-                   last_candidate = current_candidate;
+                   keepit = false;
+                   break;
                }
            }
-           if (last_candidate) /* terminate rebuilt list */
-               last_candidate->next = NULL;
+           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 (candidates == NULL)
-       return NULL;            /* no remaining candidates */
-   if (candidates->next != NULL)
-       return NULL;            /* more than one remaining candidate */
-   return candidates->args;
+   if (ncandidates == 1)
+       return candidates->args;
+
+   return NULL;                /* failed to determine a unique candidate */
 }  /* oper_select_candidate() */