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

Commit 0d49838

Browse files
committed
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.
1 parent 433c523 commit 0d49838

File tree

8 files changed

+424
-48
lines changed

8 files changed

+424
-48
lines changed

src/backend/catalog/pg_proc.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.148 2008/01/01 19:45:48 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.149 2008/03/18 22:04:14 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -601,7 +601,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
601601
proc->proargtypes.values,
602602
proc->pronargs);
603603
(void) check_sql_fn_retval(funcoid, proc->prorettype,
604-
querytree_list, NULL);
604+
querytree_list,
605+
false, NULL);
605606
}
606607
else
607608
querytree_list = pg_parse_query(prosrc);

src/backend/executor/functions.c

+44-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.120 2008/01/01 19:45:49 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.121 2008/03/18 22:04:14 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -20,6 +20,7 @@
2020
#include "commands/trigger.h"
2121
#include "executor/functions.h"
2222
#include "funcapi.h"
23+
#include "nodes/makefuncs.h"
2324
#include "parser/parse_coerce.h"
2425
#include "parser/parse_expr.h"
2526
#include "tcop/tcopprot.h"
@@ -269,6 +270,7 @@ init_sql_fcache(FmgrInfo *finfo)
269270
fcache->returnsTuple = check_sql_fn_retval(foid,
270271
rettype,
271272
queryTree_list,
273+
false,
272274
&fcache->junkFilter);
273275

274276
/* Finally, plan the queries */
@@ -856,7 +858,9 @@ ShutdownSQLFunction(Datum arg)
856858
*
857859
* The return value of a sql function is the value returned by
858860
* the final query in the function. We do some ad-hoc type checking here
859-
* to be sure that the user is returning the type he claims.
861+
* to be sure that the user is returning the type he claims. There are
862+
* also a couple of strange-looking features to assist callers in dealing
863+
* with allowed special cases, such as binary-compatible result types.
860864
*
861865
* For a polymorphic function the passed rettype must be the actual resolved
862866
* output type of the function; we should never see a polymorphic pseudotype
@@ -868,13 +872,18 @@ ShutdownSQLFunction(Datum arg)
868872
* allow "SELECT rowtype_expression", this may be false even when the declared
869873
* function return type is a rowtype.
870874
*
875+
* If insertRelabels is true, then binary-compatible cases are dealt with
876+
* by actually inserting RelabelType nodes into the final SELECT; obviously
877+
* the caller must pass a parsetree that it's okay to modify in this case.
878+
*
871879
* If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined
872880
* to convert the function's tuple result to the correct output tuple type.
873881
* Whenever the result value is false (ie, the function isn't returning a
874882
* tuple result), *junkFilter is set to NULL.
875883
*/
876884
bool
877885
check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
886+
bool insertRelabels,
878887
JunkFilter **junkFilter)
879888
{
880889
Query *parse;
@@ -945,25 +954,36 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
945954
rettype == VOIDOID)
946955
{
947956
/*
948-
* For scalar-type returns, the target list should have exactly one
949-
* entry, and its type should agree with what the user declared. (As
950-
* of Postgres 7.2, we accept binary-compatible types too.)
957+
* For scalar-type returns, the target list must have exactly one
958+
* non-junk entry, and its type must agree with what the user
959+
* declared; except we allow binary-compatible types too.
951960
*/
961+
TargetEntry *tle;
962+
952963
if (tlistlen != 1)
953964
ereport(ERROR,
954965
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
955966
errmsg("return type mismatch in function declared to return %s",
956967
format_type_be(rettype)),
957968
errdetail("Final SELECT must return exactly one column.")));
958969

959-
restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);
970+
/* We assume here that non-junk TLEs must come first in tlists */
971+
tle = (TargetEntry *) linitial(tlist);
972+
Assert(!tle->resjunk);
973+
974+
restype = exprType((Node *) tle->expr);
960975
if (!IsBinaryCoercible(restype, rettype))
961976
ereport(ERROR,
962977
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
963978
errmsg("return type mismatch in function declared to return %s",
964979
format_type_be(rettype)),
965980
errdetail("Actual return type is %s.",
966981
format_type_be(restype))));
982+
if (insertRelabels && restype != rettype)
983+
tle->expr = (Expr *) makeRelabelType(tle->expr,
984+
rettype,
985+
-1,
986+
COERCE_DONTCARE);
967987
}
968988
else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
969989
{
@@ -977,14 +997,24 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
977997
* If the target list is of length 1, and the type of the varnode in
978998
* the target list matches the declared return type, this is okay.
979999
* This can happen, for example, where the body of the function is
980-
* 'SELECT func2()', where func2 has the same return type as the
981-
* function that's calling it.
1000+
* 'SELECT func2()', where func2 has the same composite return type
1001+
* as the function that's calling it.
9821002
*/
9831003
if (tlistlen == 1)
9841004
{
985-
restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);
1005+
TargetEntry *tle = (TargetEntry *) linitial(tlist);
1006+
1007+
Assert(!tle->resjunk);
1008+
restype = exprType((Node *) tle->expr);
9861009
if (IsBinaryCoercible(restype, rettype))
1010+
{
1011+
if (insertRelabels && restype != rettype)
1012+
tle->expr = (Expr *) makeRelabelType(tle->expr,
1013+
rettype,
1014+
-1,
1015+
COERCE_DONTCARE);
9871016
return false; /* NOT returning whole tuple */
1017+
}
9881018
}
9891019

9901020
/* Is the rowtype fixed, or determined only at runtime? */
@@ -1043,6 +1073,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
10431073
format_type_be(tletype),
10441074
format_type_be(atttype),
10451075
tuplogcols)));
1076+
if (insertRelabels && tletype != atttype)
1077+
tle->expr = (Expr *) makeRelabelType(tle->expr,
1078+
atttype,
1079+
-1,
1080+
COERCE_DONTCARE);
10461081
}
10471082

10481083
for (;;)
@@ -1070,14 +1105,6 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
10701105
/* Report that we are returning entire tuple result */
10711106
return true;
10721107
}
1073-
else if (IsPolymorphicType(rettype))
1074-
{
1075-
/* This should already have been caught ... */
1076-
ereport(ERROR,
1077-
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
1078-
errmsg("cannot determine result data type"),
1079-
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
1080-
}
10811108
else
10821109
ereport(ERROR,
10831110
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),

src/backend/optimizer/plan/planner.c

+10-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.226 2008/01/01 19:45:50 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.227 2008/03/18 22:04:14 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -253,13 +253,20 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
253253
/*
254254
* Look for IN clauses at the top level of WHERE, and transform them into
255255
* joins. Note that this step only handles IN clauses originally at top
256-
* level of WHERE; if we pull up any subqueries in the next step, their
257-
* INs are processed just before pulling them up.
256+
* level of WHERE; if we pull up any subqueries below, their INs are
257+
* processed just before pulling them up.
258258
*/
259259
if (parse->hasSubLinks)
260260
parse->jointree->quals = pull_up_IN_clauses(root,
261261
parse->jointree->quals);
262262

263+
/*
264+
* Scan the rangetable for set-returning functions, and inline them
265+
* if possible (producing subqueries that might get pulled up next).
266+
* Recursion issues here are handled in the same way as for IN clauses.
267+
*/
268+
inline_set_returning_functions(root);
269+
263270
/*
264271
* Check to see if any subqueries in the rangetable can be merged into
265272
* this query.

src/backend/optimizer/prep/prepjointree.c

+54-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*
66
* NOTE: the intended sequence for invoking these operations is
77
* pull_up_IN_clauses
8+
* inline_set_returning_functions
89
* pull_up_subqueries
910
* do expression preprocessing (including flattening JOIN alias vars)
1011
* reduce_outer_joins
@@ -15,7 +16,7 @@
1516
*
1617
*
1718
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.49 2008/01/01 19:45:50 momjian Exp $
19+
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.50 2008/03/18 22:04:14 tgl Exp $
1920
*
2021
*-------------------------------------------------------------------------
2122
*/
@@ -124,6 +125,52 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node)
124125
return node;
125126
}
126127

128+
/*
129+
* inline_set_returning_functions
130+
* Attempt to "inline" set-returning functions in the FROM clause.
131+
*
132+
* If an RTE_FUNCTION rtable entry invokes a set-returning function that
133+
* contains just a simple SELECT, we can convert the rtable entry to an
134+
* RTE_SUBQUERY entry exposing the SELECT directly. This is especially
135+
* useful if the subquery can then be "pulled up" for further optimization,
136+
* but we do it even if not, to reduce executor overhead.
137+
*
138+
* This has to be done before we have started to do any optimization of
139+
* subqueries, else any such steps wouldn't get applied to subqueries
140+
* obtained via inlining. However, we do it after pull_up_IN_clauses
141+
* so that we can inline any functions used in IN subselects.
142+
*
143+
* Like most of the planner, this feels free to scribble on its input data
144+
* structure.
145+
*/
146+
void
147+
inline_set_returning_functions(PlannerInfo *root)
148+
{
149+
ListCell *rt;
150+
151+
foreach(rt, root->parse->rtable)
152+
{
153+
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
154+
155+
if (rte->rtekind == RTE_FUNCTION)
156+
{
157+
Query *funcquery;
158+
159+
/* Check safety of expansion, and expand if possible */
160+
funcquery = inline_set_returning_function(root, rte->funcexpr);
161+
if (funcquery)
162+
{
163+
/* Successful expansion, replace the rtable entry */
164+
rte->rtekind = RTE_SUBQUERY;
165+
rte->subquery = funcquery;
166+
rte->funcexpr = NULL;
167+
rte->funccoltypes = NIL;
168+
rte->funccoltypmods = NIL;
169+
}
170+
}
171+
}
172+
}
173+
127174
/*
128175
* pull_up_subqueries
129176
* 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,
296343
subroot->query_level = root->query_level;
297344
subroot->planner_cxt = CurrentMemoryContext;
298345
subroot->init_plans = NIL;
346+
subroot->eq_classes = NIL;
299347
subroot->in_info_list = NIL;
300348
subroot->append_rel_list = NIL;
301349

@@ -307,6 +355,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
307355
subquery->jointree->quals = pull_up_IN_clauses(subroot,
308356
subquery->jointree->quals);
309357

358+
/*
359+
* Similarly, inline any set-returning functions in its rangetable.
360+
*/
361+
inline_set_returning_functions(subroot);
362+
310363
/*
311364
* Recursively pull up the subquery's subqueries, so that
312365
* pull_up_subqueries' processing is complete for its jointree and

0 commit comments

Comments
 (0)