diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index d13cb582272..f489f140e37 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -26,6 +26,7 @@ #include "postgres.h" #include "catalog/pg_type.h" +#include "funcapi.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" @@ -1683,6 +1684,9 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode, { Query *parse = root->parse; RangeTblFunction *rtf; + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; pullup_replace_vars_context rvcontext; /* Fail if the RTE has ORDINALITY - we don't implement that here. */ @@ -1696,6 +1700,20 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode, if (!IsA(rtf->funcexpr, Const)) return jtnode; + /* + * If the function's result is not a scalar, we punt. In principle we + * could break the composite constant value apart into per-column + * constants, but for now it seems not worth the work. + */ + if (rtf->funccolcount != 1) + return jtnode; /* definitely composite */ + + functypclass = get_expr_result_type(rtf->funcexpr, + &funcrettype, + &tupdesc); + if (functypclass != TYPEFUNC_SCALAR) + return jtnode; /* must be a one-column composite type */ + /* Create context for applying pullup_replace_vars */ rvcontext.root = root; rvcontext.targetlist = list_make1(makeTargetEntry((Expr *) rtf->funcexpr, diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 9b407bc4854..b58d560163b 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3365,6 +3365,43 @@ where nt3.id = 1 and ss2.b3; (9 rows) drop function f_immutable_int4(int); +-- test inlining when function returns composite +create function mki8(bigint, bigint) returns int8_tbl as +$$select row($1,$2)::int8_tbl$$ language sql; +create function mki4(int) returns int4_tbl as +$$select row($1)::int4_tbl$$ language sql; +explain (verbose, costs off) +select * from mki8(1,2); + QUERY PLAN +------------------------------------ + Function Scan on mki8 + Output: q1, q2 + Function Call: '(1,2)'::int8_tbl +(3 rows) + +select * from mki8(1,2); + q1 | q2 +----+---- + 1 | 2 +(1 row) + +explain (verbose, costs off) +select * from mki4(42); + QUERY PLAN +----------------------------------- + Function Scan on mki4 + Output: f1 + Function Call: '(42)'::int4_tbl +(3 rows) + +select * from mki4(42); + f1 +---- + 42 +(1 row) + +drop function mki8(bigint, bigint); +drop function mki4(int); -- -- test extraction of restriction OR clauses from join OR clause -- (we used to only do this for indexable clauses) diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 145accca86f..57481d04117 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -1072,6 +1072,25 @@ where nt3.id = 1 and ss2.b3; drop function f_immutable_int4(int); +-- test inlining when function returns composite + +create function mki8(bigint, bigint) returns int8_tbl as +$$select row($1,$2)::int8_tbl$$ language sql; + +create function mki4(int) returns int4_tbl as +$$select row($1)::int4_tbl$$ language sql; + +explain (verbose, costs off) +select * from mki8(1,2); +select * from mki8(1,2); + +explain (verbose, costs off) +select * from mki4(42); +select * from mki4(42); + +drop function mki8(bigint, bigint); +drop function mki4(int); + -- -- test extraction of restriction OR clauses from join OR clause -- (we used to only do this for indexable clauses)