Commit 0d49838d authored by Tom Lane's avatar Tom Lane

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.
parent 433c5238
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.148 2008/01/01 19:45:48 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.149 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -601,7 +601,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
proc->proargtypes.values,
proc->pronargs);
(void) check_sql_fn_retval(funcoid, proc->prorettype,
querytree_list, NULL);
querytree_list,
false, NULL);
}
else
querytree_list = pg_parse_query(prosrc);
......
......@@ -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),
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.226 2008/01/01 19:45:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.227 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -253,13 +253,20 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
/*
* Look for IN clauses at the top level of WHERE, and transform them into
* joins. Note that this step only handles IN clauses originally at top
* level of WHERE; if we pull up any subqueries in the next step, their
* INs are processed just before pulling them up.
* level of WHERE; if we pull up any subqueries below, their INs are
* processed just before pulling them up.
*/
if (parse->hasSubLinks)
parse->jointree->quals = pull_up_IN_clauses(root,
parse->jointree->quals);
/*
* Scan the rangetable for set-returning functions, and inline them
* if possible (producing subqueries that might get pulled up next).
* Recursion issues here are handled in the same way as for IN clauses.
*/
inline_set_returning_functions(root);
/*
* Check to see if any subqueries in the rangetable can be merged into
* this query.
......
......@@ -5,6 +5,7 @@
*
* NOTE: the intended sequence for invoking these operations is
* pull_up_IN_clauses
* inline_set_returning_functions
* pull_up_subqueries
* do expression preprocessing (including flattening JOIN alias vars)
* reduce_outer_joins
......@@ -15,7 +16,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.49 2008/01/01 19:45:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.50 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -124,6 +125,52 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node)
return node;
}
/*
* inline_set_returning_functions
* Attempt to "inline" set-returning functions in the FROM clause.
*
* If an RTE_FUNCTION rtable entry invokes a set-returning function that
* contains just a simple SELECT, we can convert the rtable entry to an
* RTE_SUBQUERY entry exposing the SELECT directly. This is especially
* useful if the subquery can then be "pulled up" for further optimization,
* but we do it even if not, to reduce executor overhead.
*
* This has to be done before we have started to do any optimization of
* subqueries, else any such steps wouldn't get applied to subqueries
* obtained via inlining. However, we do it after pull_up_IN_clauses
* so that we can inline any functions used in IN subselects.
*
* Like most of the planner, this feels free to scribble on its input data
* structure.
*/
void
inline_set_returning_functions(PlannerInfo *root)
{
ListCell *rt;
foreach(rt, root->parse->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
if (rte->rtekind == RTE_FUNCTION)
{
Query *funcquery;
/* Check safety of expansion, and expand if possible */
funcquery = inline_set_returning_function(root, rte->funcexpr);
if (funcquery)
{
/* Successful expansion, replace the rtable entry */
rte->rtekind = RTE_SUBQUERY;
rte->subquery = funcquery;
rte->funcexpr = NULL;
rte->funccoltypes = NIL;
rte->funccoltypmods = NIL;
}
}
}
}
/*
* pull_up_subqueries
* Look for subqueries in the rangetable that can be pulled up into
......@@ -296,6 +343,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->query_level = root->query_level;
subroot->planner_cxt = CurrentMemoryContext;
subroot->init_plans = NIL;
subroot->eq_classes = NIL;
subroot->in_info_list = NIL;
subroot->append_rel_list = NIL;
......@@ -307,6 +355,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subquery->jointree->quals = pull_up_IN_clauses(subroot,
subquery->jointree->quals);
/*
* Similarly, inline any set-returning functions in its rangetable.
*/
inline_set_returning_functions(subroot);
/*
* Recursively pull up the subquery's subqueries, so that
* pull_up_subqueries' processing is complete for its jointree and
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/functions.h,v 1.30 2008/01/01 19:45:57 momjian Exp $
* $PostgreSQL: pgsql/src/include/executor/functions.h,v 1.31 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -21,6 +21,7 @@ extern Datum fmgr_sql(PG_FUNCTION_ARGS);
extern bool check_sql_fn_retval(Oid func_id, Oid rettype,
List *queryTreeList,
bool insertRelabels,
JunkFilter **junkFilter);
#endif /* FUNCTIONS_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.88 2008/01/01 19:45:58 momjian Exp $
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.89 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -79,6 +79,8 @@ extern Node *eval_const_expressions(Node *node);
extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
extern Query *inline_set_returning_function(PlannerInfo *root, Node *node);
extern bool expression_tree_walker(Node *node, bool (*walker) (),
void *context);
extern Node *expression_tree_mutator(Node *node, Node *(*mutator) (),
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.59 2008/01/01 19:45:58 momjian Exp $
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.60 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,6 +22,7 @@
* prototypes for prepjointree.c
*/
extern Node *pull_up_IN_clauses(PlannerInfo *root, Node *node);
extern void inline_set_returning_functions(PlannerInfo *root);
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
bool below_outer_join, bool append_rel_member);
extern void reduce_outer_joins(PlannerInfo *root);
......
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