mirror of
https://github.com/postgres/postgres.git
synced 2026-04-28 17:49:35 -04:00
When we reimplemented SRFs in commit 69f4b9c85, our initial choice was
to allow the behavior to vary from historical practice in cases where a
SRF call appeared within a conditional-execution construct (currently,
only CASE or COALESCE). But that was controversial to begin with, and
subsequent discussion has resulted in a consensus that it's better to
throw an error instead of executing the query differently from before,
so long as we can provide a reasonably clear error message and a way to
rewrite the query.
Hence, add a parser mechanism to allow detection of such cases during
parse analysis. The mechanism just requires storing, in the ParseState,
a pointer to the set-returning FuncExpr or OpExpr most recently emitted
by parse analysis. Then the parsing functions for CASE and COALESCE can
detect the presence of a SRF in their arguments by noting whether this
pointer changes while analyzing their arguments. Furthermore, if it does,
it provides a suitable error cursor location for the complaint. (This
means that if there's more than one SRF in the arguments, the error will
point at the last one to be analyzed not the first. While connoisseurs of
parsing behavior might find that odd, it's unlikely the average user would
ever notice.)
While at it, we can also provide more specific error messages than before
about some pre-existing restrictions, such as no-SRFs-within-aggregates.
Also, reject at parse time cases where a NULLIF or IS DISTINCT FROM
construct would need to return a set. We've never supported that, but the
restriction is depended on in more subtle ways now, so it seems wise to
detect it at the start.
Also, provide some documentation about how to rewrite a SRF-within-CASE
query using a custom wrapper SRF.
It turns out that the information_schema.user_mapping_options view
contained an instance of exactly the behavior we're now forbidding; but
rewriting it makes it more clear and safer too.
initdb forced because of user_mapping_options change.
Patch by me, with error message suggestions from Alvaro Herrera and
Andres Freund, pursuant to a complaint from Regina Obe.
Discussion: https://postgr.es/m/000001d2d5de$d8d66170$8a832450$@pcorp.us
161 lines
6.9 KiB
SQL
161 lines
6.9 KiB
SQL
--
|
|
-- tsrf - targetlist set returning function tests
|
|
--
|
|
|
|
-- simple srf
|
|
SELECT generate_series(1, 3);
|
|
|
|
-- parallel iteration
|
|
SELECT generate_series(1, 3), generate_series(3,5);
|
|
|
|
-- parallel iteration, different number of rows
|
|
SELECT generate_series(1, 2), generate_series(1,4);
|
|
|
|
-- srf, with SRF argument
|
|
SELECT generate_series(1, generate_series(1, 3));
|
|
|
|
-- but we've traditionally rejected the same in FROM
|
|
SELECT * FROM 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');
|
|
|
|
-- SRF output order of sorting is maintained, if SRF is not referenced
|
|
SELECT few.id, generate_series(1,3) g FROM few ORDER BY id DESC;
|
|
|
|
-- but SRFs can be referenced in sort
|
|
SELECT few.id, generate_series(1,3) g FROM few ORDER BY id, g DESC;
|
|
SELECT few.id, generate_series(1,3) g FROM few ORDER BY id, generate_series(1,3) DESC;
|
|
|
|
-- it's weird to have ORDER BYs that increase the number of results
|
|
SELECT few.id FROM few ORDER BY id, generate_series(1,3) DESC;
|
|
|
|
-- SRFs are computed after aggregation
|
|
SET enable_hashagg TO 0; -- stable output order
|
|
SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa;
|
|
-- unless referenced in GROUP BY clause
|
|
SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, unnest('{1,1,3}'::int[]);
|
|
SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, 5;
|
|
RESET enable_hashagg;
|
|
|
|
-- check HAVING works when GROUP BY does [not] reference SRF output
|
|
SELECT dataa, generate_series(1,1), count(*) FROM few GROUP BY 1 HAVING count(*) > 1;
|
|
SELECT dataa, generate_series(1,1), count(*) FROM few GROUP BY 1, 2 HAVING count(*) > 1;
|
|
|
|
-- it's weird to have GROUP BYs that increase the number of results
|
|
SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa ORDER BY 2;
|
|
SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa, unnest('{1,1,3}'::int[]) ORDER BY 2;
|
|
|
|
-- SRFs are not allowed if they'd need to be conditionally executed
|
|
SELECT q1, case when q1 > 0 then generate_series(1,3) else 0 end FROM int8_tbl;
|
|
SELECT q1, coalesce(generate_series(1,3), 0) FROM int8_tbl;
|
|
|
|
-- SRFs are not allowed in aggregate arguments
|
|
SELECT min(generate_series(1, 3)) FROM few;
|
|
|
|
-- SRFs are not allowed in window function arguments, either
|
|
SELECT min(generate_series(1, 3)) OVER() FROM few;
|
|
|
|
-- SRFs are normally computed after window functions
|
|
SELECT id,lag(id) OVER(), count(*) OVER(), generate_series(1,3) FROM few;
|
|
-- unless referencing SRFs
|
|
SELECT SUM(count(*)) OVER(PARTITION BY generate_series(1,3) ORDER BY generate_series(1,3)), generate_series(1,3) g FROM few GROUP BY g;
|
|
|
|
-- sorting + grouping
|
|
SELECT few.dataa, count(*), min(id), max(id), generate_series(1,3) FROM few GROUP BY few.dataa ORDER BY 5, 1;
|
|
|
|
-- grouping sets are a bit special, they produce NULLs in columns not actually NULL
|
|
set enable_hashagg = false;
|
|
SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab);
|
|
SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab) ORDER BY dataa;
|
|
SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab) ORDER BY g;
|
|
SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g);
|
|
SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY dataa;
|
|
SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY g;
|
|
reset enable_hashagg;
|
|
|
|
-- data modification
|
|
CREATE TABLE fewmore AS SELECT generate_series(1,3) AS data;
|
|
INSERT INTO fewmore VALUES(generate_series(4,5));
|
|
SELECT * FROM fewmore;
|
|
|
|
-- SRFs are not allowed in UPDATE (they once were, but it was nonsense)
|
|
UPDATE fewmore SET data = generate_series(4,9);
|
|
|
|
-- SRFs are not allowed in RETURNING
|
|
INSERT INTO fewmore VALUES(1) RETURNING generate_series(1,3);
|
|
|
|
-- nor standalone VALUES (but surely this is a bug?)
|
|
VALUES(1, generate_series(1,2));
|
|
|
|
-- We allow tSRFs that are not at top level
|
|
SELECT int4mul(generate_series(1,2), 10);
|
|
|
|
-- but SRFs in function RTEs must be at top level (annoying restriction)
|
|
SELECT * FROM int4mul(generate_series(1,2), 10);
|
|
|
|
-- DISTINCT ON is evaluated before tSRF evaluation if SRF is not
|
|
-- referenced either in ORDER BY or in the DISTINCT ON list. The ORDER
|
|
-- BY reference can be implicitly generated, if there's no other ORDER BY.
|
|
|
|
-- implicit reference (via implicit ORDER) to all columns
|
|
SELECT DISTINCT ON (a) a, b, generate_series(1,3) g
|
|
FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b);
|
|
|
|
-- unreferenced in DISTINCT ON or ORDER BY
|
|
SELECT DISTINCT ON (a) a, b, generate_series(1,3) g
|
|
FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b)
|
|
ORDER BY a, b DESC;
|
|
|
|
-- referenced in ORDER BY
|
|
SELECT DISTINCT ON (a) a, b, generate_series(1,3) g
|
|
FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b)
|
|
ORDER BY a, b DESC, g DESC;
|
|
|
|
-- referenced in ORDER BY and DISTINCT ON
|
|
SELECT DISTINCT ON (a, b, g) a, b, generate_series(1,3) g
|
|
FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b)
|
|
ORDER BY a, b DESC, g DESC;
|
|
|
|
-- only SRF mentioned in DISTINCT ON
|
|
SELECT DISTINCT ON (g) a, b, generate_series(1,3) g
|
|
FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b);
|
|
|
|
-- LIMIT / OFFSET is evaluated after SRF evaluation
|
|
SELECT a, generate_series(1,2) FROM (VALUES(1),(2),(3)) r(a) LIMIT 2 OFFSET 2;
|
|
-- SRFs are not allowed in LIMIT.
|
|
SELECT 1 LIMIT generate_series(1,3);
|
|
|
|
-- tSRF in correlated subquery, referencing table outside
|
|
SELECT (SELECT generate_series(1,3) LIMIT 1 OFFSET few.id) FROM few;
|
|
-- tSRF in correlated subquery, referencing SRF outside
|
|
SELECT (SELECT generate_series(1,3) LIMIT 1 OFFSET g.i) FROM generate_series(0,3) g(i);
|
|
|
|
-- Operators can return sets too
|
|
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;
|