1
0
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:
Tom Lane
2008-03-18 22:04:14 +00:00
parent 433c5238bf
commit 0d49838df6
8 changed files with 424 additions and 48 deletions

View File

@ -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),