Commit 76e66024 authored by Tom Lane's avatar Tom Lane

Improve the recently-added code for inlining set-returning functions so that

it can handle functions returning setof record.  The case was left undone
originally, but it turns out to be simple to fix.
parent cbe99a97
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.55 2008/10/04 21:56:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.56 2008/10/09 19:27:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -383,7 +383,7 @@ inline_set_returning_functions(PlannerInfo *root) ...@@ -383,7 +383,7 @@ inline_set_returning_functions(PlannerInfo *root)
Query *funcquery; Query *funcquery;
/* Check safety of expansion, and expand if possible */ /* Check safety of expansion, and expand if possible */
funcquery = inline_set_returning_function(root, rte->funcexpr); funcquery = inline_set_returning_function(root, rte);
if (funcquery) if (funcquery)
{ {
/* Successful expansion, replace the rtable entry */ /* Successful expansion, replace the rtable entry */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.268 2008/10/04 21:56:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.269 2008/10/09 19:27:40 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -111,6 +111,7 @@ static Query *substitute_actual_srf_parameters(Query *expr, ...@@ -111,6 +111,7 @@ static Query *substitute_actual_srf_parameters(Query *expr,
int nargs, List *args); int nargs, List *args);
static Node *substitute_actual_srf_parameters_mutator(Node *node, static Node *substitute_actual_srf_parameters_mutator(Node *node,
substitute_actual_srf_parameters_context *context); substitute_actual_srf_parameters_context *context);
static bool tlist_matches_coltypelist(List *tlist, List *coltypelist);
/***************************************************************************** /*****************************************************************************
...@@ -3659,17 +3660,16 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod) ...@@ -3659,17 +3660,16 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod)
* inline_set_returning_function * inline_set_returning_function
* Attempt to "inline" a set-returning function in the FROM clause. * Attempt to "inline" a set-returning function in the FROM clause.
* *
* "node" is the expression from an RTE_FUNCTION rangetable entry. If it * "rte" is an RTE_FUNCTION rangetable entry. If it represents a call of a
* represents a call of a set-returning SQL function that can safely be * set-returning SQL function that can safely be inlined, expand the function
* inlined, expand the function and return the substitute Query structure. * and return the substitute Query structure. Otherwise, return NULL.
* Otherwise, return NULL.
* *
* This has a good deal of similarity to inline_function(), but that's * This has a good deal of similarity to inline_function(), but that's
* for the non-set-returning case, and there are enough differences to * for the non-set-returning case, and there are enough differences to
* justify separate functions. * justify separate functions.
*/ */
Query * Query *
inline_set_returning_function(PlannerInfo *root, Node *node) inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
{ {
FuncExpr *fexpr; FuncExpr *fexpr;
HeapTuple func_tuple; HeapTuple func_tuple;
...@@ -3686,6 +3686,8 @@ inline_set_returning_function(PlannerInfo *root, Node *node) ...@@ -3686,6 +3686,8 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
Query *querytree; Query *querytree;
int i; int i;
Assert(rte->rtekind == RTE_FUNCTION);
/* /*
* It doesn't make a lot of sense for a SQL SRF to refer to itself * It doesn't make a lot of sense for a SQL SRF to refer to itself
* in its own FROM clause, since that must cause infinite recursion * in its own FROM clause, since that must cause infinite recursion
...@@ -3695,9 +3697,9 @@ inline_set_returning_function(PlannerInfo *root, Node *node) ...@@ -3695,9 +3697,9 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
check_stack_depth(); check_stack_depth();
/* Fail if FROM item isn't a simple FuncExpr */ /* Fail if FROM item isn't a simple FuncExpr */
if (node == NULL || !IsA(node, FuncExpr)) fexpr = (FuncExpr *) rte->funcexpr;
if (fexpr == NULL || !IsA(fexpr, FuncExpr))
return NULL; return NULL;
fexpr = (FuncExpr *) node;
/* /*
* The function must be declared to return a set, else inlining would * The function must be declared to return a set, else inlining would
...@@ -3707,10 +3709,6 @@ inline_set_returning_function(PlannerInfo *root, Node *node) ...@@ -3707,10 +3709,6 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
if (!fexpr->funcretset) if (!fexpr->funcretset)
return NULL; return NULL;
/* Fail if function returns RECORD ... we don't have enough context */
if (fexpr->funcresulttype == RECORDOID)
return NULL;
/* /*
* Refuse to inline if the arguments contain any volatile functions or * Refuse to inline if the arguments contain any volatile functions or
* sub-selects. Volatile functions are rejected because inlining may * sub-selects. Volatile functions are rejected because inlining may
...@@ -3837,9 +3835,20 @@ inline_set_returning_function(PlannerInfo *root, Node *node) ...@@ -3837,9 +3835,20 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype, if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype,
querytree_list, querytree_list,
true, NULL) && true, NULL) &&
get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE) (get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
fexpr->funcresulttype == RECORDOID))
goto fail; /* reject not-whole-tuple-result cases */ goto fail; /* reject not-whole-tuple-result cases */
/*
* If it returns RECORD, we have to check against the column type list
* provided in the RTE; check_sql_fn_retval can't do that. (If no match,
* we just fail to inline, rather than complaining; see notes for
* tlist_matches_coltypelist.)
*/
if (fexpr->funcresulttype == RECORDOID &&
!tlist_matches_coltypelist(querytree->targetList, rte->funccoltypes))
goto fail;
/* /*
* Looks good --- substitute parameters into the query. * Looks good --- substitute parameters into the query.
*/ */
...@@ -3938,3 +3947,46 @@ substitute_actual_srf_parameters_mutator(Node *node, ...@@ -3938,3 +3947,46 @@ substitute_actual_srf_parameters_mutator(Node *node,
substitute_actual_srf_parameters_mutator, substitute_actual_srf_parameters_mutator,
(void *) context); (void *) context);
} }
/*
* Check whether a SELECT targetlist emits the specified column types,
* to see if it's safe to inline a function returning record.
*
* We insist on exact match here. The executor allows binary-coercible
* cases too, but we don't have a way to preserve the correct column types
* in the correct places if we inline the function in such a case.
*
* Note that we only check type OIDs not typmods; this agrees with what the
* executor would do at runtime, and attributing a specific typmod to a
* function result is largely wishful thinking anyway.
*/
static bool
tlist_matches_coltypelist(List *tlist, List *coltypelist)
{
ListCell *tlistitem;
ListCell *clistitem;
clistitem = list_head(coltypelist);
foreach(tlistitem, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
Oid coltype;
if (tle->resjunk)
continue; /* ignore junk columns */
if (clistitem == NULL)
return false; /* too many tlist items */
coltype = lfirst_oid(clistitem);
clistitem = lnext(clistitem);
if (exprType((Node *) tle->expr) != coltype)
return false; /* column type mismatch */
}
if (clistitem != NULL)
return false; /* too few tlist items */
return true;
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, 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/optimizer/clauses.h,v 1.94 2008/08/25 22:42:34 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.95 2008/10/09 19:27:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -77,6 +77,7 @@ extern Node *eval_const_expressions(PlannerInfo *root, Node *node); ...@@ -77,6 +77,7 @@ extern Node *eval_const_expressions(PlannerInfo *root, Node *node);
extern Node *estimate_expression_value(PlannerInfo *root, Node *node); extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
extern Query *inline_set_returning_function(PlannerInfo *root, Node *node); extern Query *inline_set_returning_function(PlannerInfo *root,
RangeTblEntry *rte);
#endif /* CLAUSES_H */ #endif /* CLAUSES_H */
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