]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Make algorithm for resolving UNKNOWN function/operator inputs be
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 15 Dec 2000 19:22:03 +0000 (19:22 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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() */