]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
SQL functions can have arguments and results declared ANYARRAY or
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 Jul 2003 00:04:39 +0000 (00:04 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 Jul 2003 00:04:39 +0000 (00:04 +0000)
ANYELEMENT.  The effect is to postpone typechecking of the function
body until runtime.  Documentation is still lacking.

Original patch by Joe Conway, modified to postpone type checking
by Tom Lane.

src/backend/catalog/pg_proc.c
src/backend/executor/functions.c
src/backend/optimizer/util/clauses.c
src/backend/utils/adt/array_userfuncs.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/fmgr/fmgr.c
src/include/catalog/pg_proc.h
src/include/fmgr.h

index 48b90e56ef1034aea86e92db9a522f4c46b0be80..4ec4c44bd402f0a30e6c73c3e1e58fdc46667d07 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.97 2003/06/15 17:59:10 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.98 2003/07/01 00:04:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,7 +33,6 @@
 #include "utils/syscache.h"
 
 
-static void checkretval(Oid rettype, char fn_typtype, List *queryTreeList);
 Datum          fmgr_internal_validator(PG_FUNCTION_ARGS);
 Datum          fmgr_c_validator(PG_FUNCTION_ARGS);
 Datum          fmgr_sql_validator(PG_FUNCTION_ARGS);
@@ -317,15 +316,20 @@ ProcedureCreate(const char *procedureName,
 }
 
 /*
- * checkretval() -- check return value of a list of sql parse trees.
+ * check_sql_fn_retval() -- check return value of a list of sql parse trees.
  *
  * The return value of a sql function is the value returned by
- * the final query in the function.  We do some ad-hoc define-time
- * type checking here to be sure that the user is returning the
- * type he claims.
+ * the final query in the function.  We do some ad-hoc type checking here
+ * to be sure that the user is returning the type he claims.
+ *
+ * This is normally applied during function definition, but in the case
+ * of a function with polymorphic arguments, we instead apply it during
+ * function execution startup.  The rettype is then the actual resolved
+ * output type of the function, rather than the declared type.  (Therefore,
+ * we should never see ANYARRAY or ANYELEMENT as rettype.)
  */
-static void
-checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
+void
+check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
 {
        Query      *parse;
        int                     cmd;
@@ -472,7 +476,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
 
                relation_close(reln, AccessShareLock);
        }
-       else if (fn_typtype == 'p' && rettype == RECORDOID)
+       else if (rettype == RECORDOID)
        {
                /* Shouldn't have a typerelid */
                Assert(typerelid == InvalidOid);
@@ -482,6 +486,14 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
                 * tuple.
                 */
        }
+       else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+       {
+               /*
+                * This should already have been caught ...
+                */
+               elog(ERROR, "functions returning ANYARRAY or ANYELEMENT must " \
+                        "have at least one argument of either type");
+       }
        else
                elog(ERROR, "return type %s is not supported for SQL functions",
                         format_type_be(rettype));
@@ -505,7 +517,9 @@ fmgr_internal_validator(PG_FUNCTION_ARGS)
        Datum           tmp;
        char       *prosrc;
 
-       tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
+       tuple = SearchSysCache(PROCOID,
+                                                  ObjectIdGetDatum(funcoid),
+                                                  0, 0, 0);
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup of function %u failed", funcoid);
        proc = (Form_pg_proc) GETSTRUCT(tuple);
@@ -544,7 +558,9 @@ fmgr_c_validator(PG_FUNCTION_ARGS)
        char       *prosrc;
        char       *probin;
 
-       tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
+       tuple = SearchSysCache(PROCOID,
+                                                  ObjectIdGetDatum(funcoid),
+                                                  0, 0, 0);
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup of function %u failed", funcoid);
        proc = (Form_pg_proc) GETSTRUCT(tuple);
@@ -585,38 +601,62 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
        Datum           tmp;
        char       *prosrc;
        char            functyptype;
+       bool            haspolyarg;
        int                     i;
 
-       tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
+       tuple = SearchSysCache(PROCOID,
+                                                  ObjectIdGetDatum(funcoid),
+                                                  0, 0, 0);
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup of function %u failed", funcoid);
        proc = (Form_pg_proc) GETSTRUCT(tuple);
 
        functyptype = get_typtype(proc->prorettype);
 
-       /* Disallow pseudotypes in arguments and result */
-       /* except that return type can be RECORD or VOID */
+       /* Disallow pseudotype result */
+       /* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */
        if (functyptype == 'p' &&
                proc->prorettype != RECORDOID &&
-               proc->prorettype != VOIDOID)
+               proc->prorettype != VOIDOID &&
+               proc->prorettype != ANYARRAYOID &&
+               proc->prorettype != ANYELEMENTOID)
                elog(ERROR, "SQL functions cannot return type %s",
                         format_type_be(proc->prorettype));
 
+       /* Disallow pseudotypes in arguments */
+       /* except for ANYARRAY or ANYELEMENT */
+       haspolyarg = false;
        for (i = 0; i < proc->pronargs; i++)
        {
                if (get_typtype(proc->proargtypes[i]) == 'p')
-                       elog(ERROR, "SQL functions cannot have arguments of type %s",
-                                format_type_be(proc->proargtypes[i]));
+               {
+                       if (proc->proargtypes[i] == ANYARRAYOID ||
+                               proc->proargtypes[i] == ANYELEMENTOID)
+                               haspolyarg = true;
+                       else
+                               elog(ERROR, "SQL functions cannot have arguments of type %s",
+                                        format_type_be(proc->proargtypes[i]));
+               }
        }
 
-       tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
-       if (isnull)
-               elog(ERROR, "null prosrc");
+       /*
+        * We can't precheck the function definition if there are any polymorphic
+        * input types, because actual datatypes of expression results will be
+        * unresolvable.  The check will be done at runtime instead.
+        */
+       if (!haspolyarg)
+       {
+               tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
+               if (isnull)
+                       elog(ERROR, "null prosrc");
 
-       prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+               prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
-       querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
-       checkretval(proc->prorettype, functyptype, querytree_list);
+               querytree_list = pg_parse_and_rewrite(prosrc,
+                                                                                         proc->proargtypes,
+                                                                                         proc->pronargs);
+               check_sql_fn_retval(proc->prorettype, functyptype, querytree_list);
+       }
 
        ReleaseSysCache(tuple);
 
index a0e0919fd0b61f01f9545e1c02f0e2d875f6553b..e0ab9e92d5010ec2474122a82a18a7c192936756 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.66 2003/06/12 17:29:26 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.67 2003/07/01 00:04:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -76,7 +77,8 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
 
 /* non-export function prototypes */
 static execution_state *init_execution_state(char *src,
-                                        Oid *argOidVect, int nargs);
+                                        Oid *argOidVect, int nargs,
+                                        Oid rettype, bool haspolyarg);
 static void init_sql_fcache(FmgrInfo *finfo);
 static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
 static TupleTableSlot *postquel_getnext(execution_state *es);
@@ -90,7 +92,8 @@ static void ShutdownSQLFunction(Datum arg);
 
 
 static execution_state *
-init_execution_state(char *src, Oid *argOidVect, int nargs)
+init_execution_state(char *src, Oid *argOidVect, int nargs,
+                                        Oid rettype, bool haspolyarg)
 {
        execution_state *firstes;
        execution_state *preves;
@@ -99,6 +102,13 @@ init_execution_state(char *src, Oid *argOidVect, int nargs)
 
        queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
 
+       /*
+        * If the function has any arguments declared as polymorphic types,
+        * then it wasn't type-checked at definition time; must do so now.
+        */
+       if (haspolyarg)
+               check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
+
        firstes = NULL;
        preves = NULL;
 
@@ -133,17 +143,21 @@ static void
 init_sql_fcache(FmgrInfo *finfo)
 {
        Oid                     foid = finfo->fn_oid;
+       Oid                     rettype;
        HeapTuple       procedureTuple;
        HeapTuple       typeTuple;
        Form_pg_proc procedureStruct;
        Form_pg_type typeStruct;
        SQLFunctionCachePtr fcache;
        Oid                *argOidVect;
+       bool            haspolyarg;
        char       *src;
        int                     nargs;
        Datum           tmp;
        bool            isNull;
 
+       fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
+
        /*
         * get the procedure tuple corresponding to the given function Oid
         */
@@ -153,30 +167,37 @@ init_sql_fcache(FmgrInfo *finfo)
        if (!HeapTupleIsValid(procedureTuple))
                elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
                         foid);
-
        procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
 
        /*
-        * get the return type from the procedure tuple
+        * get the result type from the procedure tuple, and check for
+        * polymorphic result type; if so, find out the actual result type.
         */
+       rettype = procedureStruct->prorettype;
+
+       if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+       {
+               rettype = get_fn_expr_rettype(finfo);
+               if (rettype == InvalidOid)
+                       elog(ERROR, "could not determine actual result type for function declared %s",
+                                format_type_be(procedureStruct->prorettype));
+       }
+
+       /* Now look up the actual result type */
        typeTuple = SearchSysCache(TYPEOID,
-                                                  ObjectIdGetDatum(procedureStruct->prorettype),
+                                                          ObjectIdGetDatum(rettype),
                                                           0, 0, 0);
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
-                        procedureStruct->prorettype);
-
+                        rettype);
        typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
 
-       fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
-
        /*
         * get the type length and by-value flag from the type tuple
         */
        fcache->typlen = typeStruct->typlen;
 
-       if (typeStruct->typtype != 'c' &&
-               procedureStruct->prorettype != RECORDOID)
+       if (typeStruct->typtype != 'c' && rettype != RECORDOID)
        {
                /* The return type is not a composite type, so just use byval */
                fcache->typbyval = typeStruct->typbyval;
@@ -205,17 +226,35 @@ init_sql_fcache(FmgrInfo *finfo)
                fcache->funcSlot = NULL;
 
        /*
-        * Parse and plan the queries.  We need the argument info to pass
+        * Parse and plan the queries.  We need the argument type info to pass
         * to the parser.
         */
        nargs = procedureStruct->pronargs;
+       haspolyarg = false;
 
        if (nargs > 0)
        {
+               int             argnum;
+
                argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
                memcpy(argOidVect,
                           procedureStruct->proargtypes,
                           nargs * sizeof(Oid));
+               /* Resolve any polymorphic argument types */
+               for (argnum = 0; argnum < nargs; argnum++)
+               {
+                       Oid             argtype = argOidVect[argnum];
+
+                       if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
+                       {
+                               argtype = get_fn_expr_argtype(finfo, argnum);
+                               if (argtype == InvalidOid)
+                                       elog(ERROR, "could not determine actual type of argument declared %s",
+                                                format_type_be(argOidVect[argnum]));
+                               argOidVect[argnum] = argtype;
+                               haspolyarg = true;
+                       }
+               }
        }
        else
                argOidVect = (Oid *) NULL;
@@ -229,7 +268,8 @@ init_sql_fcache(FmgrInfo *finfo)
                         foid);
        src = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
-       fcache->func_state = init_execution_state(src, argOidVect, nargs);
+       fcache->func_state = init_execution_state(src, argOidVect, nargs,
+                                                                                         rettype, haspolyarg);
 
        pfree(src);
 
index 54f2d7bd69b2a4c0e330ac539e8e6ce80cd250f7..3da79cc4957cdaafdf05ec361183dc57dc58422e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.142 2003/06/29 00:33:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.143 2003/07/01 00:04:37 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1731,6 +1731,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
        int                *usecounts;
        List       *arg;
        int                     i;
+       int                     j;
 
        /*
         * Forget it if the function is not SQL-language or has other
@@ -1742,12 +1743,20 @@ inline_function(Oid funcid, Oid result_type, List *args,
                funcform->pronargs != length(args))
                return NULL;
 
-       /* Forget it if declared return type is tuple or void */
+       /* Forget it if declared return type is not base or domain */
        result_typtype = get_typtype(funcform->prorettype);
        if (result_typtype != 'b' &&
                result_typtype != 'd')
                return NULL;
 
+       /* Forget it if any declared argument type is polymorphic */
+       for (j = 0; j < funcform->pronargs; j++)
+       {
+               if (funcform->proargtypes[j] == ANYARRAYOID ||
+                       funcform->proargtypes[j] == ANYELEMENTOID)
+                       return NULL;
+       }
+
        /* Check for recursive function, and give up trying to expand if so */
        if (oidMember(funcid, active_fns))
                return NULL;
index 3aa70b0d3324c87975ec9a8bc311498d148d282e..6c28b211cebb4c801169843f6c86c776fc39731a 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.4 2003/06/27 00:33:25 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.5 2003/07/01 00:04:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,8 +37,8 @@ array_push(PG_FUNCTION_ARGS)
        int16           typlen;
        bool            typbyval;
        char            typalign;
-       Oid                     arg0_typeid = get_fn_expr_argtype(fcinfo, 0);
-       Oid                     arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
+       Oid                     arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
+       Oid                     arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
        Oid                     arg0_elemid;
        Oid                     arg1_elemid;
        ArrayMetaState *my_extra;
index 808353a63d4299344438d083c024f28ef72db0c0..d0a876a877b1fc54bb9fa2bdb5ad8c3559012695 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.92 2003/06/27 00:33:25 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.93 2003/07/01 00:04:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2792,7 +2792,7 @@ array_type_coerce(PG_FUNCTION_ARGS)
 
        if (my_extra->srctype != src_elem_type)
        {
-               Oid                     tgt_type = get_fn_expr_rettype(fcinfo);
+               Oid                     tgt_type = get_fn_expr_rettype(fmgr_info);
                Oid                     tgt_elem_type;
                Oid                     funcId;
 
index 0d69ac1083e600dcf5c35b24e376ec567ac86678..04e72e53413a46f3e53d3f9223fefce542d4f194 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.71 2003/06/29 00:33:44 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.72 2003/07/01 00:04:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1616,16 +1616,19 @@ pg_detoast_datum_slice(struct varlena * datum, int32 first, int32 count)
 
 /*-------------------------------------------------------------------------
  *             Support routines for extracting info from fn_expr parse tree
+ *
+ * These are needed by polymorphic functions, which accept multiple possible
+ * input types and need help from the parser to know what they've got.
  *-------------------------------------------------------------------------
  */
 
 /*
- * Get the OID of the function return type
+ * Get the actual type OID of the function return type
  *
  * Returns InvalidOid if information is not available
  */
 Oid
-get_fn_expr_rettype(FunctionCallInfo fcinfo)
+get_fn_expr_rettype(FmgrInfo *flinfo)
 {
        Node   *expr;
 
@@ -1633,21 +1636,21 @@ get_fn_expr_rettype(FunctionCallInfo fcinfo)
         * can't return anything useful if we have no FmgrInfo or if
         * its fn_expr node has not been initialized
         */
-       if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+       if (!flinfo || !flinfo->fn_expr)
                return InvalidOid;
 
-       expr = fcinfo->flinfo->fn_expr;
+       expr = flinfo->fn_expr;
 
        return exprType(expr);
 }
 
 /*
- * Get the type OID of a specific function argument (counting from 0)
+ * Get the actual type OID of a specific function argument (counting from 0)
  *
  * Returns InvalidOid if information is not available
  */
 Oid
-get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
+get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
 {
        Node   *expr;
        List   *args;
@@ -1657,10 +1660,10 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
         * can't return anything useful if we have no FmgrInfo or if
         * its fn_expr node has not been initialized
         */
-       if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+       if (!flinfo || !flinfo->fn_expr)
                return InvalidOid;
 
-       expr = fcinfo->flinfo->fn_expr;
+       expr = flinfo->fn_expr;
 
        if (IsA(expr, FuncExpr))
                args = ((FuncExpr *) expr)->args;
index 578a6b7d42a639f02263cf3dc45096b99de9d055..b1f16c2d4214b2630297c9d8b3ae0f25802305ea 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.308 2003/06/27 00:33:25 tgl Exp $
+ * $Id: pg_proc.h,v 1.309 2003/07/01 00:04:38 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -3438,4 +3438,7 @@ extern Oid ProcedureCreate(const char *procedureName,
                                int parameterCount,
                                const Oid *parameterTypes);
 
+extern void check_sql_fn_retval(Oid rettype, char fn_typtype,
+                                                               List *queryTreeList);
+
 #endif   /* PG_PROC_H */
index 51844eac38bb794a2f6badd70c00b1b7bf707ad0..1cc6ed023e45a8a0fb233f711883c7e267a211bd 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fmgr.h,v 1.29 2003/06/25 21:30:32 momjian Exp $
+ * $Id: fmgr.h,v 1.30 2003/07/01 00:04:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -378,8 +378,8 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
  */
 extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
 extern Oid     fmgr_internal_function(const char *proname);
-extern Oid     get_fn_expr_rettype(FunctionCallInfo fcinfo);
-extern Oid     get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
+extern Oid     get_fn_expr_rettype(FmgrInfo *flinfo);
+extern Oid     get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
 
 /*
  * Routines in dfmgr.c