Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 76e6602

Browse files
committed
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.
1 parent cbe99a9 commit 76e6602

File tree

3 files changed

+70
-17
lines changed

3 files changed

+70
-17
lines changed

src/backend/optimizer/prep/prepjointree.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
*
1818
* IDENTIFICATION
19-
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.55 2008/10/04 21:56:53 tgl Exp $
19+
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.56 2008/10/09 19:27:40 tgl Exp $
2020
*
2121
*-------------------------------------------------------------------------
2222
*/
@@ -383,7 +383,7 @@ inline_set_returning_functions(PlannerInfo *root)
383383
Query *funcquery;
384384

385385
/* Check safety of expansion, and expand if possible */
386-
funcquery = inline_set_returning_function(root, rte->funcexpr);
386+
funcquery = inline_set_returning_function(root, rte);
387387
if (funcquery)
388388
{
389389
/* Successful expansion, replace the rtable entry */

src/backend/optimizer/util/clauses.c

+65-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.268 2008/10/04 21:56:53 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.269 2008/10/09 19:27:40 tgl Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -111,6 +111,7 @@ static Query *substitute_actual_srf_parameters(Query *expr,
111111
int nargs, List *args);
112112
static Node *substitute_actual_srf_parameters_mutator(Node *node,
113113
substitute_actual_srf_parameters_context *context);
114+
static bool tlist_matches_coltypelist(List *tlist, List *coltypelist);
114115

115116

116117
/*****************************************************************************
@@ -3659,17 +3660,16 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod)
36593660
* inline_set_returning_function
36603661
* Attempt to "inline" a set-returning function in the FROM clause.
36613662
*
3662-
* "node" is the expression from an RTE_FUNCTION rangetable entry. If it
3663-
* represents a call of a set-returning SQL function that can safely be
3664-
* inlined, expand the function and return the substitute Query structure.
3665-
* Otherwise, return NULL.
3663+
* "rte" is an RTE_FUNCTION rangetable entry. If it represents a call of a
3664+
* set-returning SQL function that can safely be inlined, expand the function
3665+
* and return the substitute Query structure. Otherwise, return NULL.
36663666
*
36673667
* This has a good deal of similarity to inline_function(), but that's
36683668
* for the non-set-returning case, and there are enough differences to
36693669
* justify separate functions.
36703670
*/
36713671
Query *
3672-
inline_set_returning_function(PlannerInfo *root, Node *node)
3672+
inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
36733673
{
36743674
FuncExpr *fexpr;
36753675
HeapTuple func_tuple;
@@ -3686,6 +3686,8 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
36863686
Query *querytree;
36873687
int i;
36883688

3689+
Assert(rte->rtekind == RTE_FUNCTION);
3690+
36893691
/*
36903692
* It doesn't make a lot of sense for a SQL SRF to refer to itself
36913693
* in its own FROM clause, since that must cause infinite recursion
@@ -3695,9 +3697,9 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
36953697
check_stack_depth();
36963698

36973699
/* Fail if FROM item isn't a simple FuncExpr */
3698-
if (node == NULL || !IsA(node, FuncExpr))
3700+
fexpr = (FuncExpr *) rte->funcexpr;
3701+
if (fexpr == NULL || !IsA(fexpr, FuncExpr))
36993702
return NULL;
3700-
fexpr = (FuncExpr *) node;
37013703

37023704
/*
37033705
* The function must be declared to return a set, else inlining would
@@ -3707,10 +3709,6 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
37073709
if (!fexpr->funcretset)
37083710
return NULL;
37093711

3710-
/* Fail if function returns RECORD ... we don't have enough context */
3711-
if (fexpr->funcresulttype == RECORDOID)
3712-
return NULL;
3713-
37143712
/*
37153713
* Refuse to inline if the arguments contain any volatile functions or
37163714
* sub-selects. Volatile functions are rejected because inlining may
@@ -3837,9 +3835,20 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
38373835
if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype,
38383836
querytree_list,
38393837
true, NULL) &&
3840-
get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE)
3838+
(get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
3839+
fexpr->funcresulttype == RECORDOID))
38413840
goto fail; /* reject not-whole-tuple-result cases */
38423841

3842+
/*
3843+
* If it returns RECORD, we have to check against the column type list
3844+
* provided in the RTE; check_sql_fn_retval can't do that. (If no match,
3845+
* we just fail to inline, rather than complaining; see notes for
3846+
* tlist_matches_coltypelist.)
3847+
*/
3848+
if (fexpr->funcresulttype == RECORDOID &&
3849+
!tlist_matches_coltypelist(querytree->targetList, rte->funccoltypes))
3850+
goto fail;
3851+
38433852
/*
38443853
* Looks good --- substitute parameters into the query.
38453854
*/
@@ -3938,3 +3947,46 @@ substitute_actual_srf_parameters_mutator(Node *node,
39383947
substitute_actual_srf_parameters_mutator,
39393948
(void *) context);
39403949
}
3950+
3951+
/*
3952+
* Check whether a SELECT targetlist emits the specified column types,
3953+
* to see if it's safe to inline a function returning record.
3954+
*
3955+
* We insist on exact match here. The executor allows binary-coercible
3956+
* cases too, but we don't have a way to preserve the correct column types
3957+
* in the correct places if we inline the function in such a case.
3958+
*
3959+
* Note that we only check type OIDs not typmods; this agrees with what the
3960+
* executor would do at runtime, and attributing a specific typmod to a
3961+
* function result is largely wishful thinking anyway.
3962+
*/
3963+
static bool
3964+
tlist_matches_coltypelist(List *tlist, List *coltypelist)
3965+
{
3966+
ListCell *tlistitem;
3967+
ListCell *clistitem;
3968+
3969+
clistitem = list_head(coltypelist);
3970+
foreach(tlistitem, tlist)
3971+
{
3972+
TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
3973+
Oid coltype;
3974+
3975+
if (tle->resjunk)
3976+
continue; /* ignore junk columns */
3977+
3978+
if (clistitem == NULL)
3979+
return false; /* too many tlist items */
3980+
3981+
coltype = lfirst_oid(clistitem);
3982+
clistitem = lnext(clistitem);
3983+
3984+
if (exprType((Node *) tle->expr) != coltype)
3985+
return false; /* column type mismatch */
3986+
}
3987+
3988+
if (clistitem != NULL)
3989+
return false; /* too few tlist items */
3990+
3991+
return true;
3992+
}

src/include/optimizer/clauses.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.94 2008/08/25 22:42:34 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.95 2008/10/09 19:27:40 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -77,6 +77,7 @@ extern Node *eval_const_expressions(PlannerInfo *root, Node *node);
7777

7878
extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
7979

80-
extern Query *inline_set_returning_function(PlannerInfo *root, Node *node);
80+
extern Query *inline_set_returning_function(PlannerInfo *root,
81+
RangeTblEntry *rte);
8182

8283
#endif /* CLAUSES_H */

0 commit comments

Comments
 (0)