Consider interpreting a function call as a trivial (binary-compatible)
authorTom Lane
Thu, 4 Oct 2001 22:06:46 +0000 (22:06 +0000)
committerTom Lane
Thu, 4 Oct 2001 22:06:46 +0000 (22:06 +0000)
type coercion after failing to find an exact match in pg_proc, but before
considering interpretations that involve a function call with one or
more argument type coercions.  This avoids surprises wherein what looks
like a type coercion is interpreted as coercing to some third type and
then to the destination type, as in Dave Blasby's bug report of 3-Oct-01.
See subsequent discussion in pghackers.

doc/src/sgml/typeconv.sgml
src/backend/commands/indexcmds.c
src/backend/parser/parse_func.c
src/include/parser/parse_func.h

index fdfc2934f9d0f46aa0b96c56bfa729e1fe29317b..70799e280448c2cc4e3e85fb0bd61cc8eafd50cf 100644 (file)
@@ -458,6 +458,17 @@ this step.)
 
 
 
+If no exact match appears in the catalog, see whether the function call appears
+to be a trivial type coercion request.  This happens if the function call
+has just one argument and the function name is the same as the (internal)
+name of some data type.  Furthermore, the function argument must be either
+an unknown-type literal or a type that is binary-compatible with the named
+data type.  When these conditions are met, the function argument is coerced
+to the named data type without any explicit function call.
+
+
+
+
 Look for the best match.
 
 
@@ -519,17 +530,6 @@ then fail.
 
 
 
-
-
-If no best match could be identified, see whether the function call appears
-to be a trivial type coercion request.  This happens if the function call
-has just one argument and the function name is the same as the (internal)
-name of some data type.  Furthermore, the function argument must be either
-an unknown-type literal or a type that is binary-compatible with the named
-data type.  When these conditions are met, the function argument is coerced
-to the named data type.
-
-
 
 
 Examples
index 2304dd0b0cdc355e5916b5dec56218da47cc201f..aaf0630a2a7af7184d12b20e2d6fa0a9b2541136 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.57 2001/08/21 16:36:02 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.58 2001/10/04 22:06:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -240,6 +240,7 @@ FuncIndexArgs(IndexInfo *indexInfo,
    List       *arglist;
    int         nargs = 0;
    int         i;
+   FuncDetailCode  fdresult;
    Oid         funcid;
    Oid         rettype;
    bool        retset;
@@ -282,9 +283,18 @@ FuncIndexArgs(IndexInfo *indexInfo,
     * that.  So, check to make sure that the selected function has
     * exact-match or binary-compatible input types.
     */
-   if (!func_get_detail(funcIndex->name, nargs, argTypes,
-                        &funcid, &rettype, &retset, &true_typeids))
-       func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
+   fdresult = func_get_detail(funcIndex->name, funcIndex->args,
+                              nargs, argTypes,
+                              &funcid, &rettype, &retset,
+                              &true_typeids);
+   if (fdresult != FUNCDETAIL_NORMAL)
+   {
+       if (fdresult == FUNCDETAIL_COERCION)
+           elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion"
+                "\n\tTry specifying the index opclass you want to use, instead");
+       else
+           func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
+   }
 
    if (retset)
        elog(ERROR, "DefineIndex: cannot index on a function returning a set");
index f72bddfc6c77802bd13f0fba5777aa002222b8f3..fc85ca89527c5765c4da0e2f95c4596113e63a77 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.110 2001/08/09 18:28:18 petere Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.111 2001/10/04 22:06:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -427,12 +427,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
    }
 
    /*
-    * func_get_detail looks up the function in the catalogs, does
-    * disambiguation for polymorphic functions, handles inheritance, and
-    * returns the funcid and type and set or singleton status of the
-    * function's return value.  it also returns the true argument types
-    * to the function.  if func_get_detail returns true, the function
-    * exists.  otherwise, there was an error.
+    * Is it a set, or a function?
     */
    if (attisset)
    {                           /* we know all of these fields already */
@@ -454,61 +449,29 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
    }
    else
    {
-       bool        exists;
+       FuncDetailCode  fdresult;
 
-       exists = func_get_detail(funcname, nargs, oid_array, &funcid,
-                                &rettype, &retset, &true_oid_array);
-       if (!exists)
+       /*
+        * func_get_detail looks up the function in the catalogs, does
+        * disambiguation for polymorphic functions, handles inheritance, and
+        * returns the funcid and type and set or singleton status of the
+        * function's return value.  it also returns the true argument types
+        * to the function.
+        */
+       fdresult = func_get_detail(funcname, fargs, nargs, oid_array,
+                                  &funcid, &rettype, &retset,
+                                  &true_oid_array);
+       if (fdresult == FUNCDETAIL_COERCION)
        {
-
            /*
-            * If we can't find a function (or can't find a unique
-            * function), see if this is really a type-coercion request:
-            * single-argument function call where the function name is a
-            * type name.  If so, and if we can do the coercion trivially,
-            * just go ahead and do it without requiring there to be a
-            * real function for it.
-            *
-            * "Trivial" coercions are ones that involve binary-compatible
-            * types and ones that are coercing a previously-unknown-type
-            * literal constant to a specific type.
-            *
-            * DO NOT try to generalize this code to nontrivial coercions,
-            * because you'll just set up an infinite recursion between
-            * this routine and coerce_type!  We have already failed to
-            * find a suitable "real" coercion function, so we have to
-            * fail unless this is a coercion that coerce_type can handle
-            * by itself. Make sure this code stays in sync with what
-            * coerce_type does!
+            * We can do it as a trivial coercion.
+            * coerce_type can handle these cases, so why duplicate code...
             */
-           if (nargs == 1)
-           {
-               Oid         targetType;
-
-               targetType = GetSysCacheOid(TYPENAME,
-                                           PointerGetDatum(funcname),
-                                           0, 0, 0);
-               if (OidIsValid(targetType))
-               {
-                   Oid         sourceType = oid_array[0];
-                   Node       *arg1 = lfirst(fargs);
-
-                   if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
-                       sourceType == targetType ||
-                       IS_BINARY_COMPATIBLE(sourceType, targetType))
-                   {
-
-                       /*
-                        * Ah-hah, we can do it as a trivial coercion.
-                        * coerce_type can handle these cases, so why
-                        * duplicate code...
-                        */
-                       return coerce_type(pstate, arg1,
-                                          sourceType, targetType, -1);
-                   }
-               }
-           }
-
+           return coerce_type(pstate, lfirst(fargs),
+                              oid_array[0], rettype, -1);
+       }
+       if (fdresult != FUNCDETAIL_NORMAL)
+       {
            /*
             * Oops.  Time to die.
             *
@@ -1130,6 +1093,7 @@ func_select_candidate(int nargs,
 
 
 /* func_get_detail()
+ *
  * Find the named function in the system catalogs.
  *
  * Attempt to find the named function in the system catalogs with
@@ -1137,19 +1101,21 @@ func_select_candidate(int nargs,
  * (exact match) is as quick as possible.
  *
  * If an exact match isn't found:
- * 1) get a vector of all possible input arg type arrays constructed
+ *  1) check for possible interpretation as a trivial type coercion
+ * 2) get a vector of all possible input arg type arrays constructed
  *    from the superclasses of the original input arg types
- * 2) get a list of all possible argument type arrays to the function
+ * 3) get a list of all possible argument type arrays to the function
  *    with given name and number of arguments
- * 3) for each input arg type array from vector #1:
+ * 4) for each input arg type array from vector #1:
  *  a) find how many of the function arg type arrays from list #2
  *     it can be coerced to
  *  b) if the answer is one, we have our function
  *  c) if the answer is more than one, attempt to resolve the conflict
  *  d) if the answer is zero, try the next array from vector #1
  */
-bool
+FuncDetailCode
 func_get_detail(char *funcname,
+               List *fargs,
                int nargs,
                Oid *argtypes,
                Oid *funcid,    /* return value */
@@ -1158,6 +1124,7 @@ func_get_detail(char *funcname,
                Oid **true_typeids)     /* return value */
 {
    HeapTuple   ftup;
+   CandidateList function_typeids;
 
    /* attempt to find with arguments exactly as specified... */
    ftup = SearchSysCache(PROCNAME,
@@ -1173,12 +1140,59 @@ func_get_detail(char *funcname,
    }
    else
    {
+       /*
+        * If we didn't find an exact match, next consider the possibility
+        * that this is really a type-coercion request: a single-argument
+        * function call where the function name is a type name.  If so,
+        * and if we can do the coercion trivially (no run-time function
+        * call needed), then go ahead and treat the "function call" as
+        * a coercion.  This interpretation needs to be given higher
+        * priority than interpretations involving a type coercion followed
+        * by a function call, otherwise we can produce surprising results.
+        * For example, we want "text(varchar)" to be interpreted as a
+        * trivial coercion, not as "text(name(varchar))" which the code
+        * below this point is entirely capable of selecting.
+        *
+        * "Trivial" coercions are ones that involve binary-compatible
+        * types and ones that are coercing a previously-unknown-type
+        * literal constant to a specific type.
+        *
+        * NB: it's important that this code stays in sync with what
+        * coerce_type can do, because the caller will try to apply
+        * coerce_type if we return FUNCDETAIL_COERCION.  If we return
+        * that result for something coerce_type can't handle, we'll
+        * cause infinite recursion between this module and coerce_type!
+        */
+       if (nargs == 1)
+       {
+           Oid         targetType;
+
+           targetType = GetSysCacheOid(TYPENAME,
+                                       PointerGetDatum(funcname),
+                                       0, 0, 0);
+           if (OidIsValid(targetType))
+           {
+               Oid         sourceType = argtypes[0];
+               Node       *arg1 = lfirst(fargs);
+
+               if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
+                   sourceType == targetType ||
+                   IS_BINARY_COMPATIBLE(sourceType, targetType))
+               {
+                   /* Yup, it's a type coercion */
+                   *funcid = InvalidOid;
+                   *rettype = targetType;
+                   *retset = false;
+                   *true_typeids = argtypes;
+                   return FUNCDETAIL_COERCION;
+               }
+           }
+       }
 
        /*
         * didn't find an exact match, so now try to match up
         * candidates...
         */
-       CandidateList function_typeids;
 
        function_typeids = func_get_candidates(funcname, nargs);
 
@@ -1268,9 +1282,10 @@ func_get_detail(char *funcname,
        *rettype = pform->prorettype;
        *retset = pform->proretset;
        ReleaseSysCache(ftup);
-       return true;
+       return FUNCDETAIL_NORMAL;
    }
-   return false;
+
+   return FUNCDETAIL_NOTFOUND;
 }  /* func_get_detail() */
 
 /*
index 105781b96c3984e324b92e1dcbf006a934172542..44c774db5ed9d1cb39c404eed9e6cdcedff9c1ac 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.31 2001/05/19 00:33:20 momjian Exp $
+ * $Id: parse_func.h,v 1.32 2001/10/04 22:06:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,15 @@ typedef struct _CandidateList
    struct _CandidateList *next;
 }         *CandidateList;
 
+/* Result codes for func_get_detail */
+typedef enum
+{
+   FUNCDETAIL_NOTFOUND,        /* no suitable interpretation */
+   FUNCDETAIL_NORMAL,          /* found a matching function */
+   FUNCDETAIL_COERCION         /* it's a type coercion request */
+} FuncDetailCode;
+
+
 extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
                        int precedence);
 extern Node *ParseFuncOrColumn(ParseState *pstate,
@@ -45,9 +54,10 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
                  bool agg_star, bool agg_distinct,
                  int precedence);
 
-extern bool func_get_detail(char *funcname, int nargs, Oid *argtypes,
-               Oid *funcid, Oid *rettype,
-               bool *retset, Oid **true_typeids);
+extern FuncDetailCode func_get_detail(char *funcname, List *fargs,
+                                     int nargs, Oid *argtypes,
+                                     Oid *funcid, Oid *rettype,
+                                     bool *retset, Oid **true_typeids);
 
 extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);