mirror of
https://github.com/postgres/postgres.git
synced 2025-06-29 10:41:53 +03:00
Arrange to "inline" SQL functions that appear in a query's FROM clause,
are declared to return set, and consist of just a single SELECT. We can replace the FROM-item with a sub-SELECT and then optimize much as if we were dealing with a view. Patch from Richard Rowell, cleaned up by me.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.120 2008/01/01 19:45:49 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.121 2008/03/18 22:04:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -20,6 +20,7 @@
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/functions.h"
|
||||
#include "funcapi.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
@ -269,6 +270,7 @@ init_sql_fcache(FmgrInfo *finfo)
|
||||
fcache->returnsTuple = check_sql_fn_retval(foid,
|
||||
rettype,
|
||||
queryTree_list,
|
||||
false,
|
||||
&fcache->junkFilter);
|
||||
|
||||
/* Finally, plan the queries */
|
||||
@ -856,7 +858,9 @@ ShutdownSQLFunction(Datum arg)
|
||||
*
|
||||
* The return value of a sql function is the value returned by
|
||||
* 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.
|
||||
* to be sure that the user is returning the type he claims. There are
|
||||
* also a couple of strange-looking features to assist callers in dealing
|
||||
* with allowed special cases, such as binary-compatible result types.
|
||||
*
|
||||
* For a polymorphic function the passed rettype must be the actual resolved
|
||||
* output type of the function; we should never see a polymorphic pseudotype
|
||||
@ -868,6 +872,10 @@ ShutdownSQLFunction(Datum arg)
|
||||
* allow "SELECT rowtype_expression", this may be false even when the declared
|
||||
* function return type is a rowtype.
|
||||
*
|
||||
* If insertRelabels is true, then binary-compatible cases are dealt with
|
||||
* by actually inserting RelabelType nodes into the final SELECT; obviously
|
||||
* the caller must pass a parsetree that it's okay to modify in this case.
|
||||
*
|
||||
* If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined
|
||||
* to convert the function's tuple result to the correct output tuple type.
|
||||
* Whenever the result value is false (ie, the function isn't returning a
|
||||
@ -875,6 +883,7 @@ ShutdownSQLFunction(Datum arg)
|
||||
*/
|
||||
bool
|
||||
check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
bool insertRelabels,
|
||||
JunkFilter **junkFilter)
|
||||
{
|
||||
Query *parse;
|
||||
@ -945,10 +954,12 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
rettype == VOIDOID)
|
||||
{
|
||||
/*
|
||||
* For scalar-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.)
|
||||
* For scalar-type returns, the target list must have exactly one
|
||||
* non-junk entry, and its type must agree with what the user
|
||||
* declared; except we allow binary-compatible types too.
|
||||
*/
|
||||
TargetEntry *tle;
|
||||
|
||||
if (tlistlen != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
@ -956,7 +967,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
format_type_be(rettype)),
|
||||
errdetail("Final SELECT must return exactly one column.")));
|
||||
|
||||
restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);
|
||||
/* We assume here that non-junk TLEs must come first in tlists */
|
||||
tle = (TargetEntry *) linitial(tlist);
|
||||
Assert(!tle->resjunk);
|
||||
|
||||
restype = exprType((Node *) tle->expr);
|
||||
if (!IsBinaryCoercible(restype, rettype))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
@ -964,6 +979,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
format_type_be(rettype)),
|
||||
errdetail("Actual return type is %s.",
|
||||
format_type_be(restype))));
|
||||
if (insertRelabels && restype != rettype)
|
||||
tle->expr = (Expr *) makeRelabelType(tle->expr,
|
||||
rettype,
|
||||
-1,
|
||||
COERCE_DONTCARE);
|
||||
}
|
||||
else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
|
||||
{
|
||||
@ -977,14 +997,24 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
* 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.
|
||||
* 'SELECT func2()', where func2 has the same composite return type
|
||||
* as the function that's calling it.
|
||||
*/
|
||||
if (tlistlen == 1)
|
||||
{
|
||||
restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);
|
||||
TargetEntry *tle = (TargetEntry *) linitial(tlist);
|
||||
|
||||
Assert(!tle->resjunk);
|
||||
restype = exprType((Node *) tle->expr);
|
||||
if (IsBinaryCoercible(restype, rettype))
|
||||
{
|
||||
if (insertRelabels && restype != rettype)
|
||||
tle->expr = (Expr *) makeRelabelType(tle->expr,
|
||||
rettype,
|
||||
-1,
|
||||
COERCE_DONTCARE);
|
||||
return false; /* NOT returning whole tuple */
|
||||
}
|
||||
}
|
||||
|
||||
/* Is the rowtype fixed, or determined only at runtime? */
|
||||
@ -1043,6 +1073,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
format_type_be(tletype),
|
||||
format_type_be(atttype),
|
||||
tuplogcols)));
|
||||
if (insertRelabels && tletype != atttype)
|
||||
tle->expr = (Expr *) makeRelabelType(tle->expr,
|
||||
atttype,
|
||||
-1,
|
||||
COERCE_DONTCARE);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
@ -1070,14 +1105,6 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
/* Report that we are returning entire tuple result */
|
||||
return true;
|
||||
}
|
||||
else if (IsPolymorphicType(rettype))
|
||||
{
|
||||
/* This should already have been caught ... */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("cannot determine result data type"),
|
||||
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
|
||||
}
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
|
Reference in New Issue
Block a user