Commit c82d4e65 authored by Tom Lane's avatar Tom Lane

Fix mishandling of tSRFs at different nesting levels.

Given a targetlist like "srf(x), f(srf(x))", split_pathtarget_at_srfs()
decided that it needed two levels of ProjectSet nodes, failing to notice
that the two SRF calls are textually equal().  Because of that, setrefs.c
would convert the upper ProjectSet's tlist to "Var1, f(Var1)" (where Var1
represents a reference to the srf(x) output of the lower ProjectSet).
This triggered an assertion in nodeProjectSet.c complaining that it found
no SRFs to evaluate, as reported by Erik Rijkers.

What we want in such a case is to evaluate srf(x) only once and use a plain
Result node to compute "Var1, f(Var1)"; that gives results similar to what
previous versions produced, whereas allowing srf(x) to be evaluated again
in an upper ProjectSet would square the number of rows emitted.

Furthermore, even if the SRF calls aren't textually identical, we want them
to be evaluated in lockstep, because that's what happened in the old
implementation.  But split_pathtarget_at_srfs() got this completely wrong,
using two levels of ProjectSet for a case like "srf(x), f(srf(y))".

Hence, rewrite split_pathtarget_at_srfs() from the ground up so that it
groups SRFs according to the depth of nesting of SRFs in their arguments.
This is pretty much how we envisioned that working originally, but I blew
it when it came to implementation.

In passing, optimize the case of target == input_target, which I noticed
is not only possible but quite common.

Discussion: https://postgr.es/m/dcbd2853c05d22088766553d60dc78c6@xs4all.nl
parent ecb814b5
This diff is collapsed.
......@@ -853,7 +853,7 @@ select * from int4_tbl o where (f1, f1) in
-> Result
Output: i.f1, ((generate_series(1, 2)) / 10)
-> ProjectSet
Output: i.f1, generate_series(1, 2)
Output: generate_series(1, 2), i.f1
-> HashAggregate
Output: i.f1
Group Key: i.f1
......
......@@ -53,6 +53,29 @@ SELECT generate_series(generate_series(1,3), generate_series(2, 4));
4
(6 rows)
-- check proper nesting of SRFs in different expressions
explain (verbose, costs off)
SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4);
QUERY PLAN
--------------------------------------------------------------------------------
ProjectSet
Output: generate_series(1, (generate_series(1, 3))), (generate_series(2, 4))
-> ProjectSet
Output: generate_series(1, 3), generate_series(2, 4)
-> Result
(5 rows)
SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4);
generate_series | generate_series
-----------------+-----------------
1 | 2
1 | 3
2 | 3
1 | 4
2 | 4
3 | 4
(6 rows)
CREATE TABLE few(id int, dataa text, datab text);
INSERT INTO few VALUES(1, 'a', 'foo'),(2, 'a', 'bar'),(3, 'b', 'bar');
-- SRF output order of sorting is maintained, if SRF is not referenced
......@@ -518,6 +541,69 @@ SELECT |@|ARRAY[1,2,3];
3
(3 rows)
-- Some fun cases involving duplicate SRF calls
explain (verbose, costs off)
select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1;
QUERY PLAN
------------------------------------------------------------------
Result
Output: (generate_series(1, 3)), ((generate_series(1, 3)) + 1)
-> ProjectSet
Output: generate_series(1, 3)
-> Result
(5 rows)
select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1;
x | xp1
---+-----
1 | 2
2 | 3
3 | 4
(3 rows)
explain (verbose, costs off)
select generate_series(1,3)+1 order by generate_series(1,3);
QUERY PLAN
------------------------------------------------------------------------
Sort
Output: (((generate_series(1, 3)) + 1)), (generate_series(1, 3))
Sort Key: (generate_series(1, 3))
-> Result
Output: ((generate_series(1, 3)) + 1), (generate_series(1, 3))
-> ProjectSet
Output: generate_series(1, 3)
-> Result
(8 rows)
select generate_series(1,3)+1 order by generate_series(1,3);
?column?
----------
2
3
4
(3 rows)
-- Check that SRFs of same nesting level run in lockstep
explain (verbose, costs off)
select generate_series(1,3) as x, generate_series(3,6) + 1 as y;
QUERY PLAN
------------------------------------------------------------------
Result
Output: (generate_series(1, 3)), ((generate_series(3, 6)) + 1)
-> ProjectSet
Output: generate_series(1, 3), generate_series(3, 6)
-> Result
(5 rows)
select generate_series(1,3) as x, generate_series(3,6) + 1 as y;
x | y
---+---
1 | 4
2 | 5
3 | 6
| 7
(4 rows)
-- Clean up
DROP TABLE few;
DROP TABLE fewmore;
......@@ -17,6 +17,11 @@ SELECT generate_series(1, generate_series(1, 3));
-- srf, with two SRF arguments
SELECT generate_series(generate_series(1,3), generate_series(2, 4));
-- check proper nesting of SRFs in different expressions
explain (verbose, costs off)
SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4);
SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4);
CREATE TABLE few(id int, dataa text, datab text);
INSERT INTO few VALUES(1, 'a', 'foo'),(2, 'a', 'bar'),(3, 'b', 'bar');
......@@ -129,6 +134,19 @@ SELECT (SELECT generate_series(1,3) LIMIT 1 OFFSET g.i) FROM generate_series(0,3
CREATE OPERATOR |@| (PROCEDURE = unnest, RIGHTARG = ANYARRAY);
SELECT |@|ARRAY[1,2,3];
-- Some fun cases involving duplicate SRF calls
explain (verbose, costs off)
select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1;
select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1;
explain (verbose, costs off)
select generate_series(1,3)+1 order by generate_series(1,3);
select generate_series(1,3)+1 order by generate_series(1,3);
-- Check that SRFs of same nesting level run in lockstep
explain (verbose, costs off)
select generate_series(1,3) as x, generate_series(3,6) + 1 as y;
select generate_series(1,3) as x, generate_series(3,6) + 1 as y;
-- Clean up
DROP TABLE few;
DROP TABLE fewmore;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment