Prevent bogus pullup of constant-valued functions returning composite.

Fix an oversight in commit 7266d0997: as it stood, the code failed
when a function-in-FROM returns composite and can be simplified
to a composite constant.

For the moment, just test for composite result and abandon pullup
if we see one.  To make it actually work, we'd have to decompose
the composite constant into per-column constants; which is surely
do-able, but I'm not convinced it's worth the code space.

Per report from Raúl Marín Rodríguez.

Discussion: https://postgr.es/m/CAM6_UM4isP+buRA5sWodO_MUEgutms-KDfnkwGmryc5DGj9XuQ@mail.gmail.com
This commit is contained in:
Tom Lane 2019-09-24 12:11:32 -04:00
parent 6d05086c0a
commit a9ae99d019
3 changed files with 74 additions and 0 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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)