diff options
Diffstat (limited to 'src/backend/catalog/pg_proc.c')
-rw-r--r-- | src/backend/catalog/pg_proc.c | 176 |
1 files changed, 100 insertions, 76 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 2624fdaf25d..f2d8e70589c 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.82 2002/08/02 18:15:05 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.83 2002/08/04 19:48:09 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,6 +25,7 @@ #include "miscadmin.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" +#include "parser/parse_relation.h" #include "parser/parse_type.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" @@ -33,7 +34,7 @@ #include "utils/syscache.h" -static void checkretval(Oid rettype, List *queryTreeList); +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); @@ -367,94 +368,113 @@ checkretval(Oid rettype, List *queryTreeList) */ tlistlen = ExecCleanTargetListLength(tlist); - /* - * For base-type returns, the target list should have exactly one - * entry, and its type should agree with what the user declared. (As - * of Postgres 7.2, we accept binary-compatible types too.) - */ typerelid = typeidTypeRelid(rettype); - if (typerelid == InvalidOid) + + if (fn_typtype == 'b') { - if (tlistlen != 1) - elog(ERROR, "function declared to return %s returns multiple columns in final SELECT", - format_type_be(rettype)); + /* + * For base-type returns, the target list should have exactly one + * entry, and its type should agree with what the user declared. (As + * of Postgres 7.2, we accept binary-compatible types too.) + */ - restype = ((TargetEntry *) lfirst(tlist))->resdom->restype; - if (!IsBinaryCompatible(restype, rettype)) - elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", - format_type_be(rettype), format_type_be(restype)); + if (typerelid == InvalidOid) + { + if (tlistlen != 1) + elog(ERROR, "function declared to return %s returns multiple columns in final SELECT", + format_type_be(rettype)); - return; - } + restype = ((TargetEntry *) lfirst(tlist))->resdom->restype; + if (!IsBinaryCompatible(restype, rettype)) + elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", + format_type_be(rettype), format_type_be(restype)); - /* - * If the target list is of length 1, and the type of the varnode in - * the target list matches the declared return type, this is okay. - * This can happen, for example, where the body of the function is - * 'SELECT func2()', where func2 has the same return type as the - * function that's calling it. - */ - if (tlistlen == 1) - { - restype = ((TargetEntry *) lfirst(tlist))->resdom->restype; - if (IsBinaryCompatible(restype, rettype)) return; - } - - /* - * By here, the procedure returns a tuple or set of tuples. This part - * of the typechecking is a hack. We look up the relation that is the - * declared return type, and scan the non-deleted attributes to ensure - * that they match the datatypes of the non-resjunk columns. - */ - reln = heap_open(typerelid, AccessShareLock); - relnatts = reln->rd_rel->relnatts; - rellogcols = 0; /* we'll count nondeleted cols as we go */ - colindex = 0; + } - foreach(tlistitem, tlist) + /* + * If the target list is of length 1, and the type of the varnode in + * the target list matches the declared return type, this is okay. + * This can happen, for example, where the body of the function is + * 'SELECT func2()', where func2 has the same return type as the + * function that's calling it. + */ + if (tlistlen == 1) + { + restype = ((TargetEntry *) lfirst(tlist))->resdom->restype; + if (IsBinaryCompatible(restype, rettype)) + return; + } + } + else if (fn_typtype == 'c') { - TargetEntry *tle = (TargetEntry *) lfirst(tlistitem); - Form_pg_attribute attr; - Oid tletype; - Oid atttype; + /* + * By here, the procedure returns a tuple or set of tuples. This part + * of the typechecking is a hack. We look up the relation that is the + * declared return type, and scan the non-deleted attributes to ensure + * that they match the datatypes of the non-resjunk columns. + */ + reln = heap_open(typerelid, AccessShareLock); + relnatts = reln->rd_rel->relnatts; + rellogcols = 0; /* we'll count nondeleted cols as we go */ + colindex = 0; + + foreach(tlistitem, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tlistitem); + Form_pg_attribute attr; + Oid tletype; + Oid atttype; + + if (tle->resdom->resjunk) + continue; + + do { + colindex++; + if (colindex > relnatts) + elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", + format_type_be(rettype), rellogcols); + attr = reln->rd_att->attrs[colindex - 1]; + } while (attr->attisdropped); + rellogcols++; - if (tle->resdom->resjunk) - continue; + tletype = exprType(tle->expr); + atttype = attr->atttypid; + if (!IsBinaryCompatible(tletype, atttype)) + elog(ERROR, "function declared to return %s returns %s instead of %s at column %d", + format_type_be(rettype), + format_type_be(tletype), + format_type_be(atttype), + rellogcols); + } - do { + for (;;) + { colindex++; if (colindex > relnatts) - elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", - format_type_be(rettype), rellogcols); - attr = reln->rd_att->attrs[colindex - 1]; - } while (attr->attisdropped); - rellogcols++; - - tletype = exprType(tle->expr); - atttype = attr->atttypid; - if (!IsBinaryCompatible(tletype, atttype)) - elog(ERROR, "function declared to return %s returns %s instead of %s at column %d", - format_type_be(rettype), - format_type_be(tletype), - format_type_be(atttype), - rellogcols); - } + break; + if (!reln->rd_att->attrs[colindex - 1]->attisdropped) + rellogcols++; + } - for (;;) - { - colindex++; - if (colindex > relnatts) - break; - if (!reln->rd_att->attrs[colindex - 1]->attisdropped) - rellogcols++; - } + if (tlistlen != rellogcols) + elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", + format_type_be(rettype), rellogcols); - if (tlistlen != rellogcols) - elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", - format_type_be(rettype), rellogcols); + heap_close(reln, AccessShareLock); - heap_close(reln, AccessShareLock); + return; + } + else if (fn_typtype == 'p' && rettype == RECORDOID) + { + /* + * For RECORD return type, defer this check until we get the + * first tuple. + */ + return; + } + else + elog(ERROR, "Unknown kind of return type specified for function"); } @@ -553,6 +573,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) bool isnull; Datum tmp; char *prosrc; + char functyptype; tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0); if (!HeapTupleIsValid(tuple)) @@ -569,8 +590,11 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp)); + /* check typtype to see if we have a predetermined return type */ + functyptype = typeid_get_typtype(proc->prorettype); + querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs); - checkretval(proc->prorettype, querytree_list); + checkretval(proc->prorettype, functyptype, querytree_list); ReleaseSysCache(tuple); PG_RETURN_BOOL(true); |