diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 48b90e56ef..4ec4c44bd4 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -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); diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index a0e0919fd0..e0ab9e92d5 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -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); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 54f2d7bd69..3da79cc495 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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; diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c index 3aa70b0d33..6c28b211ce 100644 --- a/src/backend/utils/adt/array_userfuncs.c +++ b/src/backend/utils/adt/array_userfuncs.c @@ -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; diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 808353a63d..d0a876a877 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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; diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 0d69ac1083..04e72e5341 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -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; diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 578a6b7d42..b1f16c2d42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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 */ diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 51844eac38..1cc6ed023e 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -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