1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-22 12:22:45 +03:00

SQL functions can have arguments and results declared ANYARRAY or

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.
This commit is contained in:
Tom Lane
2003-07-01 00:04:39 +00:00
parent 71e9f3b07f
commit d6d07a0eea
8 changed files with 152 additions and 57 deletions

View 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);