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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 65 additions & 13 deletions
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

Lines changed: 3 additions & 2 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy