Prevent bogus pullup of constant-valued functions returning composite.
authorTom Lane
Tue, 24 Sep 2019 16:11:32 +0000 (12:11 -0400)
committerTom Lane
Tue, 24 Sep 2019 16:11:32 +0000 (12:11 -0400)
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

src/backend/optimizer/prep/prepjointree.c
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index d13cb5822725664e601502d60b0a7537b91a90a4..f489f140e3711a0acc1843df69e92585e63617c9 100644 (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,
index 9b407bc48549cbb311d3de456a2111b016040871..b58d560163b8a64b9179f85825cf65b5ac2aa45c 100644 (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)
index 145accca86fb6565cb73f31f108e842d5cefd201..57481d041170b58c4b09b068d1be3d4a9efb0d61 100644 (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)