Commit 8efbe30d authored by Tom Lane's avatar Tom Lane

check_sql_fn_retval has always thought that we supported doing

'SELECT foo()' in a SQL function returning a rowtype, to simply pass
back the results of another function returning the same rowtype.
However, that hasn't actually worked in many years.  Now it works again.
parent 3dd1ca03
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.114 2004/04/01 21:28:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.115 2004/04/02 23:14:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -362,8 +362,13 @@ create_parameternames_array(int parameterCount, const char *parameterNames[]) ...@@ -362,8 +362,13 @@ create_parameternames_array(int parameterCount, const char *parameterNames[])
* function execution startup. The rettype is then the actual resolved * function execution startup. The rettype is then the actual resolved
* output type of the function, rather than the declared type. (Therefore, * output type of the function, rather than the declared type. (Therefore,
* we should never see ANYARRAY or ANYELEMENT as rettype.) * we should never see ANYARRAY or ANYELEMENT as rettype.)
*
* The return value is true if the function returns the entire tuple result
* of its final SELECT, and false otherwise. Note that because we allow
* "SELECT rowtype_expression", this may be false even when the declared
* function return type is a rowtype.
*/ */
void bool
check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList) check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
{ {
Query *parse; Query *parse;
...@@ -387,7 +392,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList) ...@@ -387,7 +392,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
errmsg("return type mismatch in function declared to return %s", errmsg("return type mismatch in function declared to return %s",
format_type_be(rettype)), format_type_be(rettype)),
errdetail("Function's final statement must be a SELECT."))); errdetail("Function's final statement must be a SELECT.")));
return; return false;
} }
/* find the final query */ /* find the final query */
...@@ -408,7 +413,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList) ...@@ -408,7 +413,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
errmsg("return type mismatch in function declared to return %s", errmsg("return type mismatch in function declared to return %s",
format_type_be(rettype)), format_type_be(rettype)),
errdetail("Function's final statement must not be a SELECT."))); errdetail("Function's final statement must not be a SELECT.")));
return; return false;
} }
/* by here, the function is declared to return some type */ /* by here, the function is declared to return some type */
...@@ -468,7 +473,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList) ...@@ -468,7 +473,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
{ {
restype = ((TargetEntry *) lfirst(tlist))->resdom->restype; restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
if (IsBinaryCoercible(restype, rettype)) if (IsBinaryCoercible(restype, rettype))
return; return false; /* NOT returning whole tuple */
} }
/* /*
...@@ -536,16 +541,31 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList) ...@@ -536,16 +541,31 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
errdetail("Final SELECT returns too few columns."))); errdetail("Final SELECT returns too few columns.")));
relation_close(reln, AccessShareLock); relation_close(reln, AccessShareLock);
/* Report that we are returning entire tuple result */
return true;
} }
else if (rettype == RECORDOID) else if (rettype == RECORDOID)
{ {
/* Shouldn't have a typerelid */ /*
Assert(typerelid == InvalidOid); * 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 (IsBinaryCoercible(restype, rettype))
return false; /* NOT returning whole tuple */
}
/* /*
* For RECORD return type, defer this check until we get the first * Otherwise assume we are returning the whole tuple. Crosschecking
* tuple. * against what the caller expects will happen at runtime.
*/ */
return true;
} }
else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID) else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
{ {
...@@ -560,6 +580,8 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList) ...@@ -560,6 +580,8 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type %s is not supported for SQL functions", errmsg("return type %s is not supported for SQL functions",
format_type_be(rettype)))); format_type_be(rettype))));
return false;
} }
...@@ -751,7 +773,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) ...@@ -751,7 +773,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
querytree_list = pg_parse_and_rewrite(prosrc, querytree_list = pg_parse_and_rewrite(prosrc,
proc->proargtypes, proc->proargtypes,
proc->pronargs); proc->pronargs);
check_sql_fn_retval(proc->prorettype, functyptype, querytree_list); (void) check_sql_fn_retval(proc->prorettype, functyptype,
querytree_list);
} }
else else
querytree_list = pg_parse_query(prosrc); querytree_list = pg_parse_query(prosrc);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.79 2004/04/01 21:28:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.80 2004/04/02 23:14:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -73,9 +73,7 @@ typedef SQLFunctionCache *SQLFunctionCachePtr; ...@@ -73,9 +73,7 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
/* non-export function prototypes */ /* non-export function prototypes */
static execution_state *init_execution_state(char *src, static execution_state *init_execution_state(List *queryTree_list);
Oid *argOidVect, int nargs,
Oid rettype, bool haspolyarg);
static void init_sql_fcache(FmgrInfo *finfo); static void init_sql_fcache(FmgrInfo *finfo);
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache); static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
static TupleTableSlot *postquel_getnext(execution_state *es); static TupleTableSlot *postquel_getnext(execution_state *es);
...@@ -90,25 +88,11 @@ static void ShutdownSQLFunction(Datum arg); ...@@ -90,25 +88,11 @@ static void ShutdownSQLFunction(Datum arg);
static execution_state * static execution_state *
init_execution_state(char *src, Oid *argOidVect, int nargs, init_execution_state(List *queryTree_list)
Oid rettype, bool haspolyarg)
{ {
execution_state *firstes; execution_state *firstes = NULL;
execution_state *preves; execution_state *preves = NULL;
List *queryTree_list, List *qtl_item;
*qtl_item;
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;
foreach(qtl_item, queryTree_list) foreach(qtl_item, queryTree_list)
{ {
...@@ -151,6 +135,7 @@ init_sql_fcache(FmgrInfo *finfo) ...@@ -151,6 +135,7 @@ init_sql_fcache(FmgrInfo *finfo)
bool haspolyarg; bool haspolyarg;
char *src; char *src;
int nargs; int nargs;
List *queryTree_list;
Datum tmp; Datum tmp;
bool isNull; bool isNull;
...@@ -191,7 +176,9 @@ init_sql_fcache(FmgrInfo *finfo) ...@@ -191,7 +176,9 @@ init_sql_fcache(FmgrInfo *finfo)
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
/* /*
* get the type length and by-value flag from the type tuple * get the type length and by-value flag from the type tuple; also
* do a preliminary check for returnsTuple (this may prove inaccurate,
* see below).
*/ */
fcache->typlen = typeStruct->typlen; fcache->typlen = typeStruct->typlen;
fcache->typbyval = typeStruct->typbyval; fcache->typbyval = typeStruct->typbyval;
...@@ -199,7 +186,7 @@ init_sql_fcache(FmgrInfo *finfo) ...@@ -199,7 +186,7 @@ init_sql_fcache(FmgrInfo *finfo)
rettype == RECORDOID); rettype == RECORDOID);
/* /*
* Parse and plan the queries. We need the argument type info to pass * Parse and rewrite the queries. We need the argument type info to pass
* to the parser. * to the parser.
*/ */
nargs = procedureStruct->pronargs; nargs = procedureStruct->pronargs;
...@@ -242,8 +229,25 @@ init_sql_fcache(FmgrInfo *finfo) ...@@ -242,8 +229,25 @@ init_sql_fcache(FmgrInfo *finfo)
elog(ERROR, "null prosrc for function %u", foid); elog(ERROR, "null prosrc for function %u", foid);
src = DatumGetCString(DirectFunctionCall1(textout, tmp)); src = DatumGetCString(DirectFunctionCall1(textout, tmp));
fcache->func_state = init_execution_state(src, argOidVect, nargs, queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
rettype, haspolyarg);
/*
* If the function has any arguments declared as polymorphic types,
* then it wasn't type-checked at definition time; must do so now.
*
* Also, force a type-check if the declared return type is a rowtype;
* we need to find out whether we are actually returning the whole
* tuple result, or just regurgitating a rowtype expression result.
* In the latter case we clear returnsTuple because we need not act
* different from the scalar result case.
*/
if (haspolyarg || fcache->returnsTuple)
fcache->returnsTuple = check_sql_fn_retval(rettype,
get_typtype(rettype),
queryTree_list);
/* Finally, plan the queries */
fcache->func_state = init_execution_state(queryTree_list);
pfree(src); pfree(src);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.168 2004/04/02 19:06:57 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.169 2004/04/02 23:14:08 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -1992,8 +1992,8 @@ inline_function(Oid funcid, Oid result_type, List *args, ...@@ -1992,8 +1992,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
* probably not important, but let's be careful.) * probably not important, but let's be careful.)
*/ */
if (polymorphic) if (polymorphic)
check_sql_fn_retval(result_type, get_typtype(result_type), (void) check_sql_fn_retval(result_type, get_typtype(result_type),
querytree_list); querytree_list);
/* /*
* Additional validity checks on the expression. It mustn't return a * Additional validity checks on the expression. It mustn't return a
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.323 2004/04/01 21:28:45 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.324 2004/04/02 23:14:08 tgl Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -3560,7 +3560,7 @@ extern Oid ProcedureCreate(const char *procedureName, ...@@ -3560,7 +3560,7 @@ extern Oid ProcedureCreate(const char *procedureName,
const Oid *parameterTypes, const Oid *parameterTypes,
const char *parameterNames[]); const char *parameterNames[]);
extern void check_sql_fn_retval(Oid rettype, char fn_typtype, extern bool check_sql_fn_retval(Oid rettype, char fn_typtype,
List *queryTreeList); List *queryTreeList);
extern bool function_parse_error_transpose(const char *prosrc); extern bool function_parse_error_transpose(const char *prosrc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment