mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-07 11:29:25 +02:00
If a function-in-FROM laterally references the output of some sub-SELECT
earlier in the FROM clause, and we are able to flatten that sub-SELECT
into the outer query, the expression(s) copied into the function RTE
missed being processed by eval_const_expressions. This'd lead to trouble
and probable crashes at execution if such expressions contained
named-argument function call syntax or functions with defaulted arguments.
The bug is masked if the query contains any explicit JOIN syntax, which
may help explain why we'd not noticed.
Per bug #17227 from Bernd Dorn. This is an oversight in commit 7266d0997
,
so back-patch to v13 where that came in.
Discussion: https://postgr.es/m/17227-5a28ed1512189fa4@postgresql.org
2448 lines
67 KiB
Plaintext
2448 lines
67 KiB
Plaintext
CREATE TABLE rngfunc2(rngfuncid int, f2 int);
|
|
INSERT INTO rngfunc2 VALUES(1, 11);
|
|
INSERT INTO rngfunc2 VALUES(2, 22);
|
|
INSERT INTO rngfunc2 VALUES(1, 111);
|
|
CREATE FUNCTION rngfunct(int) returns setof rngfunc2 as 'SELECT * FROM rngfunc2 WHERE rngfuncid = $1 ORDER BY f2;' LANGUAGE SQL;
|
|
-- function with ORDINALITY
|
|
select * from rngfunct(1) with ordinality as z(a,b,ord);
|
|
a | b | ord
|
|
---+-----+-----
|
|
1 | 11 | 1
|
|
1 | 111 | 2
|
|
(2 rows)
|
|
|
|
select * from rngfunct(1) with ordinality as z(a,b,ord) where b > 100; -- ordinal 2, not 1
|
|
a | b | ord
|
|
---+-----+-----
|
|
1 | 111 | 2
|
|
(1 row)
|
|
|
|
-- ordinality vs. column names and types
|
|
select a,b,ord from rngfunct(1) with ordinality as z(a,b,ord);
|
|
a | b | ord
|
|
---+-----+-----
|
|
1 | 11 | 1
|
|
1 | 111 | 2
|
|
(2 rows)
|
|
|
|
select a,ord from unnest(array['a','b']) with ordinality as z(a,ord);
|
|
a | ord
|
|
---+-----
|
|
a | 1
|
|
b | 2
|
|
(2 rows)
|
|
|
|
select * from unnest(array['a','b']) with ordinality as z(a,ord);
|
|
a | ord
|
|
---+-----
|
|
a | 1
|
|
b | 2
|
|
(2 rows)
|
|
|
|
select a,ord from unnest(array[1.0::float8]) with ordinality as z(a,ord);
|
|
a | ord
|
|
---+-----
|
|
1 | 1
|
|
(1 row)
|
|
|
|
select * from unnest(array[1.0::float8]) with ordinality as z(a,ord);
|
|
a | ord
|
|
---+-----
|
|
1 | 1
|
|
(1 row)
|
|
|
|
select row_to_json(s.*) from generate_series(11,14) with ordinality s;
|
|
row_to_json
|
|
-------------------------
|
|
{"s":11,"ordinality":1}
|
|
{"s":12,"ordinality":2}
|
|
{"s":13,"ordinality":3}
|
|
{"s":14,"ordinality":4}
|
|
(4 rows)
|
|
|
|
-- ordinality vs. views
|
|
create temporary view vw_ord as select * from (values (1)) v(n) join rngfunct(1) with ordinality as z(a,b,ord) on (n=ord);
|
|
select * from vw_ord;
|
|
n | a | b | ord
|
|
---+---+----+-----
|
|
1 | 1 | 11 | 1
|
|
(1 row)
|
|
|
|
select definition from pg_views where viewname='vw_ord';
|
|
definition
|
|
-------------------------------------------------------------------------
|
|
SELECT v.n, +
|
|
z.a, +
|
|
z.b, +
|
|
z.ord +
|
|
FROM (( VALUES (1)) v(n) +
|
|
JOIN rngfunct(1) WITH ORDINALITY z(a, b, ord) ON ((v.n = z.ord)));
|
|
(1 row)
|
|
|
|
drop view vw_ord;
|
|
-- multiple functions
|
|
select * from rows from(rngfunct(1),rngfunct(2)) with ordinality as z(a,b,c,d,ord);
|
|
a | b | c | d | ord
|
|
---+-----+---+----+-----
|
|
1 | 11 | 2 | 22 | 1
|
|
1 | 111 | | | 2
|
|
(2 rows)
|
|
|
|
create temporary view vw_ord as select * from (values (1)) v(n) join rows from(rngfunct(1),rngfunct(2)) with ordinality as z(a,b,c,d,ord) on (n=ord);
|
|
select * from vw_ord;
|
|
n | a | b | c | d | ord
|
|
---+---+----+---+----+-----
|
|
1 | 1 | 11 | 2 | 22 | 1
|
|
(1 row)
|
|
|
|
select definition from pg_views where viewname='vw_ord';
|
|
definition
|
|
-------------------------------------------------------------------------------------------------------
|
|
SELECT v.n, +
|
|
z.a, +
|
|
z.b, +
|
|
z.c, +
|
|
z.d, +
|
|
z.ord +
|
|
FROM (( VALUES (1)) v(n) +
|
|
JOIN ROWS FROM(rngfunct(1), rngfunct(2)) WITH ORDINALITY z(a, b, c, d, ord) ON ((v.n = z.ord)));
|
|
(1 row)
|
|
|
|
drop view vw_ord;
|
|
-- expansions of unnest()
|
|
select * from unnest(array[10,20],array['foo','bar'],array[1.0]);
|
|
unnest | unnest | unnest
|
|
--------+--------+--------
|
|
10 | foo | 1.0
|
|
20 | bar |
|
|
(2 rows)
|
|
|
|
select * from unnest(array[10,20],array['foo','bar'],array[1.0]) with ordinality as z(a,b,c,ord);
|
|
a | b | c | ord
|
|
----+-----+-----+-----
|
|
10 | foo | 1.0 | 1
|
|
20 | bar | | 2
|
|
(2 rows)
|
|
|
|
select * from rows from(unnest(array[10,20],array['foo','bar'],array[1.0])) with ordinality as z(a,b,c,ord);
|
|
a | b | c | ord
|
|
----+-----+-----+-----
|
|
10 | foo | 1.0 | 1
|
|
20 | bar | | 2
|
|
(2 rows)
|
|
|
|
select * from rows from(unnest(array[10,20],array['foo','bar']), generate_series(101,102)) with ordinality as z(a,b,c,ord);
|
|
a | b | c | ord
|
|
----+-----+-----+-----
|
|
10 | foo | 101 | 1
|
|
20 | bar | 102 | 2
|
|
(2 rows)
|
|
|
|
create temporary view vw_ord as select * from unnest(array[10,20],array['foo','bar'],array[1.0]) as z(a,b,c);
|
|
select * from vw_ord;
|
|
a | b | c
|
|
----+-----+-----
|
|
10 | foo | 1.0
|
|
20 | bar |
|
|
(2 rows)
|
|
|
|
select definition from pg_views where viewname='vw_ord';
|
|
definition
|
|
----------------------------------------------------------------------------------------
|
|
SELECT z.a, +
|
|
z.b, +
|
|
z.c +
|
|
FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c);
|
|
(1 row)
|
|
|
|
drop view vw_ord;
|
|
create temporary view vw_ord as select * from rows from(unnest(array[10,20],array['foo','bar'],array[1.0])) as z(a,b,c);
|
|
select * from vw_ord;
|
|
a | b | c
|
|
----+-----+-----
|
|
10 | foo | 1.0
|
|
20 | bar |
|
|
(2 rows)
|
|
|
|
select definition from pg_views where viewname='vw_ord';
|
|
definition
|
|
----------------------------------------------------------------------------------------
|
|
SELECT z.a, +
|
|
z.b, +
|
|
z.c +
|
|
FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c);
|
|
(1 row)
|
|
|
|
drop view vw_ord;
|
|
create temporary view vw_ord as select * from rows from(unnest(array[10,20],array['foo','bar']), generate_series(1,2)) as z(a,b,c);
|
|
select * from vw_ord;
|
|
a | b | c
|
|
----+-----+---
|
|
10 | foo | 1
|
|
20 | bar | 2
|
|
(2 rows)
|
|
|
|
select definition from pg_views where viewname='vw_ord';
|
|
definition
|
|
----------------------------------------------------------------------------------------------------------------------
|
|
SELECT z.a, +
|
|
z.b, +
|
|
z.c +
|
|
FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c);
|
|
(1 row)
|
|
|
|
drop view vw_ord;
|
|
-- ordinality and multiple functions vs. rewind and reverse scan
|
|
begin;
|
|
declare rf_cur scroll cursor for select * from rows from(generate_series(1,5),generate_series(1,2)) with ordinality as g(i,j,o);
|
|
fetch all from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
2 | 2 | 2
|
|
3 | | 3
|
|
4 | | 4
|
|
5 | | 5
|
|
(5 rows)
|
|
|
|
fetch backward all from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
5 | | 5
|
|
4 | | 4
|
|
3 | | 3
|
|
2 | 2 | 2
|
|
1 | 1 | 1
|
|
(5 rows)
|
|
|
|
fetch all from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
2 | 2 | 2
|
|
3 | | 3
|
|
4 | | 4
|
|
5 | | 5
|
|
(5 rows)
|
|
|
|
fetch next from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
(0 rows)
|
|
|
|
fetch next from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
(0 rows)
|
|
|
|
fetch prior from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
5 | | 5
|
|
(1 row)
|
|
|
|
fetch absolute 1 from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
(1 row)
|
|
|
|
fetch next from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
2 | 2 | 2
|
|
(1 row)
|
|
|
|
fetch next from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
3 | | 3
|
|
(1 row)
|
|
|
|
fetch next from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
4 | | 4
|
|
(1 row)
|
|
|
|
fetch prior from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
3 | | 3
|
|
(1 row)
|
|
|
|
fetch prior from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
2 | 2 | 2
|
|
(1 row)
|
|
|
|
fetch prior from rf_cur;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
(1 row)
|
|
|
|
commit;
|
|
-- function with implicit LATERAL
|
|
select * from rngfunc2, rngfunct(rngfunc2.rngfuncid) z where rngfunc2.f2 = z.f2;
|
|
rngfuncid | f2 | rngfuncid | f2
|
|
-----------+-----+-----------+-----
|
|
1 | 11 | 1 | 11
|
|
2 | 22 | 2 | 22
|
|
1 | 111 | 1 | 111
|
|
(3 rows)
|
|
|
|
-- function with implicit LATERAL and explicit ORDINALITY
|
|
select * from rngfunc2, rngfunct(rngfunc2.rngfuncid) with ordinality as z(rngfuncid,f2,ord) where rngfunc2.f2 = z.f2;
|
|
rngfuncid | f2 | rngfuncid | f2 | ord
|
|
-----------+-----+-----------+-----+-----
|
|
1 | 11 | 1 | 11 | 1
|
|
2 | 22 | 2 | 22 | 1
|
|
1 | 111 | 1 | 111 | 2
|
|
(3 rows)
|
|
|
|
-- function in subselect
|
|
select * from rngfunc2 where f2 in (select f2 from rngfunct(rngfunc2.rngfuncid) z where z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1,2;
|
|
rngfuncid | f2
|
|
-----------+-----
|
|
1 | 11
|
|
1 | 111
|
|
2 | 22
|
|
(3 rows)
|
|
|
|
-- function in subselect
|
|
select * from rngfunc2 where f2 in (select f2 from rngfunct(1) z where z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1,2;
|
|
rngfuncid | f2
|
|
-----------+-----
|
|
1 | 11
|
|
1 | 111
|
|
(2 rows)
|
|
|
|
-- function in subselect
|
|
select * from rngfunc2 where f2 in (select f2 from rngfunct(rngfunc2.rngfuncid) z where z.rngfuncid = 1) ORDER BY 1,2;
|
|
rngfuncid | f2
|
|
-----------+-----
|
|
1 | 11
|
|
1 | 111
|
|
(2 rows)
|
|
|
|
-- nested functions
|
|
select rngfunct.rngfuncid, rngfunct.f2 from rngfunct(sin(pi()/2)::int) ORDER BY 1,2;
|
|
rngfuncid | f2
|
|
-----------+-----
|
|
1 | 11
|
|
1 | 111
|
|
(2 rows)
|
|
|
|
CREATE TABLE rngfunc (rngfuncid int, rngfuncsubid int, rngfuncname text, primary key(rngfuncid,rngfuncsubid));
|
|
INSERT INTO rngfunc VALUES(1,1,'Joe');
|
|
INSERT INTO rngfunc VALUES(1,2,'Ed');
|
|
INSERT INTO rngfunc VALUES(2,1,'Mary');
|
|
-- sql, proretset = f, prorettype = b
|
|
CREATE FUNCTION getrngfunc1(int) RETURNS int AS 'SELECT $1;' LANGUAGE SQL;
|
|
SELECT * FROM getrngfunc1(1) AS t1;
|
|
t1
|
|
----
|
|
1
|
|
(1 row)
|
|
|
|
SELECT * FROM getrngfunc1(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1(1);
|
|
SELECT * FROM vw_getrngfunc;
|
|
getrngfunc1
|
|
-------------
|
|
1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1(1) WITH ORDINALITY as t1(v,o);
|
|
SELECT * FROM vw_getrngfunc;
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- sql, proretset = t, prorettype = b
|
|
CREATE FUNCTION getrngfunc2(int) RETURNS setof int AS 'SELECT rngfuncid FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getrngfunc2(1) AS t1;
|
|
t1
|
|
----
|
|
1
|
|
1
|
|
(2 rows)
|
|
|
|
SELECT * FROM getrngfunc2(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
1 | 2
|
|
(2 rows)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2(1);
|
|
SELECT * FROM vw_getrngfunc;
|
|
getrngfunc2
|
|
-------------
|
|
1
|
|
1
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2(1) WITH ORDINALITY AS t1(v,o);
|
|
SELECT * FROM vw_getrngfunc;
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
1 | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- sql, proretset = t, prorettype = b
|
|
CREATE FUNCTION getrngfunc3(int) RETURNS setof text AS 'SELECT rngfuncname FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getrngfunc3(1) AS t1;
|
|
t1
|
|
-----
|
|
Joe
|
|
Ed
|
|
(2 rows)
|
|
|
|
SELECT * FROM getrngfunc3(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
-----+---
|
|
Joe | 1
|
|
Ed | 2
|
|
(2 rows)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3(1);
|
|
SELECT * FROM vw_getrngfunc;
|
|
getrngfunc3
|
|
-------------
|
|
Joe
|
|
Ed
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3(1) WITH ORDINALITY AS t1(v,o);
|
|
SELECT * FROM vw_getrngfunc;
|
|
v | o
|
|
-----+---
|
|
Joe | 1
|
|
Ed | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- sql, proretset = f, prorettype = c
|
|
CREATE FUNCTION getrngfunc4(int) RETURNS rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getrngfunc4(1) AS t1;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
SELECT * FROM getrngfunc4(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4(1);
|
|
SELECT * FROM vw_getrngfunc;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
SELECT * FROM vw_getrngfunc;
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- sql, proretset = t, prorettype = c
|
|
CREATE FUNCTION getrngfunc5(int) RETURNS setof rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getrngfunc5(1) AS t1;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
SELECT * FROM getrngfunc5(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
1 | 2 | Ed | 2
|
|
(2 rows)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5(1);
|
|
SELECT * FROM vw_getrngfunc;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
SELECT * FROM vw_getrngfunc;
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
1 | 2 | Ed | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- sql, proretset = f, prorettype = record
|
|
CREATE FUNCTION getrngfunc6(int) RETURNS RECORD AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getrngfunc6(1) AS t1(rngfuncid int, rngfuncsubid int, rngfuncname text);
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
SELECT * FROM ROWS FROM( getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY;
|
|
rngfuncid | rngfuncsubid | rngfuncname | ordinality
|
|
-----------+--------------+-------------+------------
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc6(1) AS
|
|
(rngfuncid int, rngfuncsubid int, rngfuncname text);
|
|
SELECT * FROM vw_getrngfunc;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS
|
|
SELECT * FROM ROWS FROM( getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) )
|
|
WITH ORDINALITY;
|
|
SELECT * FROM vw_getrngfunc;
|
|
rngfuncid | rngfuncsubid | rngfuncname | ordinality
|
|
-----------+--------------+-------------+------------
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- sql, proretset = t, prorettype = record
|
|
CREATE FUNCTION getrngfunc7(int) RETURNS setof record AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getrngfunc7(1) AS t1(rngfuncid int, rngfuncsubid int, rngfuncname text);
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
SELECT * FROM ROWS FROM( getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY;
|
|
rngfuncid | rngfuncsubid | rngfuncname | ordinality
|
|
-----------+--------------+-------------+------------
|
|
1 | 1 | Joe | 1
|
|
1 | 2 | Ed | 2
|
|
(2 rows)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc7(1) AS
|
|
(rngfuncid int, rngfuncsubid int, rngfuncname text);
|
|
SELECT * FROM vw_getrngfunc;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS
|
|
SELECT * FROM ROWS FROM( getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) )
|
|
WITH ORDINALITY;
|
|
SELECT * FROM vw_getrngfunc;
|
|
rngfuncid | rngfuncsubid | rngfuncname | ordinality
|
|
-----------+--------------+-------------+------------
|
|
1 | 1 | Joe | 1
|
|
1 | 2 | Ed | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- plpgsql, proretset = f, prorettype = b
|
|
CREATE FUNCTION getrngfunc8(int) RETURNS int AS 'DECLARE rngfuncint int; BEGIN SELECT rngfuncid into rngfuncint FROM rngfunc WHERE rngfuncid = $1; RETURN rngfuncint; END;' LANGUAGE plpgsql;
|
|
SELECT * FROM getrngfunc8(1) AS t1;
|
|
t1
|
|
----
|
|
1
|
|
(1 row)
|
|
|
|
SELECT * FROM getrngfunc8(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8(1);
|
|
SELECT * FROM vw_getrngfunc;
|
|
getrngfunc8
|
|
-------------
|
|
1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8(1) WITH ORDINALITY AS t1(v,o);
|
|
SELECT * FROM vw_getrngfunc;
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- plpgsql, proretset = f, prorettype = c
|
|
CREATE FUNCTION getrngfunc9(int) RETURNS rngfunc AS 'DECLARE rngfunctup rngfunc%ROWTYPE; BEGIN SELECT * into rngfunctup FROM rngfunc WHERE rngfuncid = $1; RETURN rngfunctup; END;' LANGUAGE plpgsql;
|
|
SELECT * FROM getrngfunc9(1) AS t1;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
SELECT * FROM getrngfunc9(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9(1);
|
|
SELECT * FROM vw_getrngfunc;
|
|
rngfuncid | rngfuncsubid | rngfuncname
|
|
-----------+--------------+-------------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
SELECT * FROM vw_getrngfunc;
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getrngfunc;
|
|
-- mix 'n match kinds, to exercise expandRTE and related logic
|
|
select * from rows from(getrngfunc1(1),getrngfunc2(1),getrngfunc3(1),getrngfunc4(1),getrngfunc5(1),
|
|
getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text),
|
|
getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text),
|
|
getrngfunc8(1),getrngfunc9(1))
|
|
with ordinality as t1(a,b,c,d,e,f,g,h,i,j,k,l,m,o,p,q,r,s,t,u);
|
|
a | b | c | d | e | f | g | h | i | j | k | l | m | o | p | q | r | s | t | u
|
|
---+---+-----+---+---+-----+---+---+-----+---+---+-----+---+---+-----+---+---+---+-----+---
|
|
1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | 1 | Joe | 1
|
|
| 1 | Ed | | | | 1 | 2 | Ed | | | | 1 | 2 | Ed | | | | | 2
|
|
(2 rows)
|
|
|
|
select * from rows from(getrngfunc9(1),getrngfunc8(1),
|
|
getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text),
|
|
getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text),
|
|
getrngfunc5(1),getrngfunc4(1),getrngfunc3(1),getrngfunc2(1),getrngfunc1(1))
|
|
with ordinality as t1(a,b,c,d,e,f,g,h,i,j,k,l,m,o,p,q,r,s,t,u);
|
|
a | b | c | d | e | f | g | h | i | j | k | l | m | o | p | q | r | s | t | u
|
|
---+---+-----+---+---+---+-----+---+---+-----+---+---+-----+---+---+-----+-----+---+---+---
|
|
1 | 1 | Joe | 1 | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | Joe | 1 | 1 | 1
|
|
| | | | 1 | 2 | Ed | | | | 1 | 2 | Ed | | | | Ed | 1 | | 2
|
|
(2 rows)
|
|
|
|
create temporary view vw_rngfunc as
|
|
select * from rows from(getrngfunc9(1),
|
|
getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text),
|
|
getrngfunc1(1))
|
|
with ordinality as t1(a,b,c,d,e,f,g,n);
|
|
select * from vw_rngfunc;
|
|
a | b | c | d | e | f | g | n
|
|
---+---+-----+---+---+-----+---+---
|
|
1 | 1 | Joe | 1 | 1 | Joe | 1 | 1
|
|
| | | 1 | 2 | Ed | | 2
|
|
(2 rows)
|
|
|
|
select pg_get_viewdef('vw_rngfunc');
|
|
pg_get_viewdef
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
SELECT t1.a, +
|
|
t1.b, +
|
|
t1.c, +
|
|
t1.d, +
|
|
t1.e, +
|
|
t1.f, +
|
|
t1.g, +
|
|
t1.n +
|
|
FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n);
|
|
(1 row)
|
|
|
|
drop view vw_rngfunc;
|
|
DROP FUNCTION getrngfunc1(int);
|
|
DROP FUNCTION getrngfunc2(int);
|
|
DROP FUNCTION getrngfunc3(int);
|
|
DROP FUNCTION getrngfunc4(int);
|
|
DROP FUNCTION getrngfunc5(int);
|
|
DROP FUNCTION getrngfunc6(int);
|
|
DROP FUNCTION getrngfunc7(int);
|
|
DROP FUNCTION getrngfunc8(int);
|
|
DROP FUNCTION getrngfunc9(int);
|
|
DROP FUNCTION rngfunct(int);
|
|
DROP TABLE rngfunc2;
|
|
DROP TABLE rngfunc;
|
|
-- Rescan tests --
|
|
CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq1;
|
|
CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq2;
|
|
CREATE TYPE rngfunc_rescan_t AS (i integer, s bigint);
|
|
CREATE FUNCTION rngfunc_sql(int,int) RETURNS setof rngfunc_rescan_t AS 'SELECT i, nextval(''rngfunc_rescan_seq1'') FROM generate_series($1,$2) i;' LANGUAGE SQL;
|
|
-- plpgsql functions use materialize mode
|
|
CREATE FUNCTION rngfunc_mat(int,int) RETURNS setof rngfunc_rescan_t AS 'begin for i in $1..$2 loop return next (i, nextval(''rngfunc_rescan_seq2'')); end loop; end;' LANGUAGE plpgsql;
|
|
--invokes ExecReScanFunctionScan - all these cases should materialize the function only once
|
|
-- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function
|
|
-- is on the inner path of a nestloop join
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_sql(11,13) ON (r+i)<100;
|
|
r | i | s
|
|
---+----+---
|
|
1 | 11 | 1
|
|
1 | 12 | 2
|
|
1 | 13 | 3
|
|
2 | 11 | 1
|
|
2 | 12 | 2
|
|
2 | 13 | 3
|
|
3 | 11 | 1
|
|
3 | 12 | 2
|
|
3 | 13 | 3
|
|
(9 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
|
|
r | i | s | o
|
|
---+----+---+---
|
|
1 | 11 | 1 | 1
|
|
1 | 12 | 2 | 2
|
|
1 | 13 | 3 | 3
|
|
2 | 11 | 1 | 1
|
|
2 | 12 | 2 | 2
|
|
2 | 13 | 3 | 3
|
|
3 | 11 | 1 | 1
|
|
3 | 12 | 2 | 2
|
|
3 | 13 | 3 | 3
|
|
(9 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) ON (r+i)<100;
|
|
r | i | s
|
|
---+----+---
|
|
1 | 11 | 1
|
|
1 | 12 | 2
|
|
1 | 13 | 3
|
|
2 | 11 | 1
|
|
2 | 12 | 2
|
|
2 | 13 | 3
|
|
3 | 11 | 1
|
|
3 | 12 | 2
|
|
3 | 13 | 3
|
|
(9 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
|
|
r | i | s | o
|
|
---+----+---+---
|
|
1 | 11 | 1 | 1
|
|
1 | 12 | 2 | 2
|
|
1 | 13 | 3 | 3
|
|
2 | 11 | 1 | 1
|
|
2 | 12 | 2 | 2
|
|
2 | 13 | 3 | 3
|
|
3 | 11 | 1 | 1
|
|
3 | 12 | 2 | 2
|
|
3 | 13 | 3 | 3
|
|
(9 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( rngfunc_sql(11,13), rngfunc_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100;
|
|
r | i1 | s1 | i2 | s2 | o
|
|
---+----+----+----+----+---
|
|
1 | 11 | 1 | 11 | 1 | 1
|
|
1 | 12 | 2 | 12 | 2 | 2
|
|
1 | 13 | 3 | 13 | 3 | 3
|
|
2 | 11 | 1 | 11 | 1 | 1
|
|
2 | 12 | 2 | 12 | 2 | 2
|
|
2 | 13 | 3 | 13 | 3 | 3
|
|
3 | 11 | 1 | 11 | 1 | 1
|
|
3 | 12 | 2 | 12 | 2 | 2
|
|
3 | 13 | 3 | 13 | 3 | 3
|
|
(9 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) f(i) ON (r+i)<100;
|
|
r | i
|
|
---+----
|
|
1 | 11
|
|
1 | 12
|
|
1 | 13
|
|
2 | 11
|
|
2 | 12
|
|
2 | 13
|
|
3 | 11
|
|
3 | 12
|
|
3 | 13
|
|
(9 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) WITH ORDINALITY AS f(i,o) ON (r+i)<100;
|
|
r | i | o
|
|
---+----+---
|
|
1 | 11 | 1
|
|
1 | 12 | 2
|
|
1 | 13 | 3
|
|
2 | 11 | 1
|
|
2 | 12 | 2
|
|
2 | 13 | 3
|
|
3 | 11 | 1
|
|
3 | 12 | 2
|
|
3 | 13 | 3
|
|
(9 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) f(i) ON (r+i)<100;
|
|
r | i
|
|
---+----
|
|
1 | 10
|
|
1 | 20
|
|
1 | 30
|
|
2 | 10
|
|
2 | 20
|
|
2 | 30
|
|
3 | 10
|
|
3 | 20
|
|
3 | 30
|
|
(9 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH ORDINALITY AS f(i,o) ON (r+i)<100;
|
|
r | i | o
|
|
---+----+---
|
|
1 | 10 | 1
|
|
1 | 20 | 2
|
|
1 | 30 | 3
|
|
2 | 10 | 1
|
|
2 | 20 | 2
|
|
2 | 30 | 3
|
|
3 | 10 | 1
|
|
3 | 20 | 2
|
|
3 | 30 | 3
|
|
(9 rows)
|
|
|
|
--invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL)
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(10+r,13);
|
|
r | i | s
|
|
---+----+---
|
|
1 | 11 | 1
|
|
1 | 12 | 2
|
|
1 | 13 | 3
|
|
2 | 12 | 4
|
|
2 | 13 | 5
|
|
3 | 13 | 6
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(10+r,13) WITH ORDINALITY AS f(i,s,o);
|
|
r | i | s | o
|
|
---+----+---+---
|
|
1 | 11 | 1 | 1
|
|
1 | 12 | 2 | 2
|
|
1 | 13 | 3 | 3
|
|
2 | 12 | 4 | 1
|
|
2 | 13 | 5 | 2
|
|
3 | 13 | 6 | 1
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(11,10+r);
|
|
r | i | s
|
|
---+----+---
|
|
1 | 11 | 1
|
|
2 | 11 | 2
|
|
2 | 12 | 3
|
|
3 | 11 | 4
|
|
3 | 12 | 5
|
|
3 | 13 | 6
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(11,10+r) WITH ORDINALITY AS f(i,s,o);
|
|
r | i | s | o
|
|
---+----+---+---
|
|
1 | 11 | 1 | 1
|
|
2 | 11 | 2 | 1
|
|
2 | 12 | 3 | 2
|
|
3 | 11 | 4 | 1
|
|
3 | 12 | 5 | 2
|
|
3 | 13 | 6 | 3
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_sql(r1,r2);
|
|
r1 | r2 | i | s
|
|
----+----+----+----
|
|
11 | 12 | 11 | 1
|
|
11 | 12 | 12 | 2
|
|
13 | 15 | 13 | 3
|
|
13 | 15 | 14 | 4
|
|
13 | 15 | 15 | 5
|
|
16 | 20 | 16 | 6
|
|
16 | 20 | 17 | 7
|
|
16 | 20 | 18 | 8
|
|
16 | 20 | 19 | 9
|
|
16 | 20 | 20 | 10
|
|
(10 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_sql(r1,r2) WITH ORDINALITY AS f(i,s,o);
|
|
r1 | r2 | i | s | o
|
|
----+----+----+----+---
|
|
11 | 12 | 11 | 1 | 1
|
|
11 | 12 | 12 | 2 | 2
|
|
13 | 15 | 13 | 3 | 1
|
|
13 | 15 | 14 | 4 | 2
|
|
13 | 15 | 15 | 5 | 3
|
|
16 | 20 | 16 | 6 | 1
|
|
16 | 20 | 17 | 7 | 2
|
|
16 | 20 | 18 | 8 | 3
|
|
16 | 20 | 19 | 9 | 4
|
|
16 | 20 | 20 | 10 | 5
|
|
(10 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13);
|
|
r | i | s
|
|
---+----+---
|
|
1 | 11 | 1
|
|
1 | 12 | 2
|
|
1 | 13 | 3
|
|
2 | 12 | 4
|
|
2 | 13 | 5
|
|
3 | 13 | 6
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13) WITH ORDINALITY AS f(i,s,o);
|
|
r | i | s | o
|
|
---+----+---+---
|
|
1 | 11 | 1 | 1
|
|
1 | 12 | 2 | 2
|
|
1 | 13 | 3 | 3
|
|
2 | 12 | 4 | 1
|
|
2 | 13 | 5 | 2
|
|
3 | 13 | 6 | 1
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r);
|
|
r | i | s
|
|
---+----+---
|
|
1 | 11 | 1
|
|
2 | 11 | 2
|
|
2 | 12 | 3
|
|
3 | 11 | 4
|
|
3 | 12 | 5
|
|
3 | 13 | 6
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r) WITH ORDINALITY AS f(i,s,o);
|
|
r | i | s | o
|
|
---+----+---+---
|
|
1 | 11 | 1 | 1
|
|
2 | 11 | 2 | 1
|
|
2 | 12 | 3 | 2
|
|
3 | 11 | 4 | 1
|
|
3 | 12 | 5 | 2
|
|
3 | 13 | 6 | 3
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2);
|
|
r1 | r2 | i | s
|
|
----+----+----+----
|
|
11 | 12 | 11 | 1
|
|
11 | 12 | 12 | 2
|
|
13 | 15 | 13 | 3
|
|
13 | 15 | 14 | 4
|
|
13 | 15 | 15 | 5
|
|
16 | 20 | 16 | 6
|
|
16 | 20 | 17 | 7
|
|
16 | 20 | 18 | 8
|
|
16 | 20 | 19 | 9
|
|
16 | 20 | 20 | 10
|
|
(10 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2) WITH ORDINALITY AS f(i,s,o);
|
|
r1 | r2 | i | s | o
|
|
----+----+----+----+---
|
|
11 | 12 | 11 | 1 | 1
|
|
11 | 12 | 12 | 2 | 2
|
|
13 | 15 | 13 | 3 | 1
|
|
13 | 15 | 14 | 4 | 2
|
|
13 | 15 | 15 | 5 | 3
|
|
16 | 20 | 16 | 6 | 1
|
|
16 | 20 | 17 | 7 | 2
|
|
16 | 20 | 18 | 8 | 3
|
|
16 | 20 | 19 | 9 | 4
|
|
16 | 20 | 20 | 10 | 5
|
|
(10 rows)
|
|
|
|
-- selective rescan of multiple functions:
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(11,11), rngfunc_mat(10+r,13) );
|
|
r | i | s | i | s
|
|
---+----+---+----+---
|
|
1 | 11 | 1 | 11 | 1
|
|
1 | | | 12 | 2
|
|
1 | | | 13 | 3
|
|
2 | 11 | 1 | 12 | 4
|
|
2 | | | 13 | 5
|
|
3 | 11 | 1 | 13 | 6
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(11,11) );
|
|
r | i | s | i | s
|
|
---+----+---+----+---
|
|
1 | 11 | 1 | 11 | 1
|
|
1 | 12 | 2 | |
|
|
1 | 13 | 3 | |
|
|
2 | 12 | 4 | 11 | 1
|
|
2 | 13 | 5 | |
|
|
3 | 13 | 6 | 11 | 1
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(10+r,13) );
|
|
r | i | s | i | s
|
|
---+----+---+----+---
|
|
1 | 11 | 1 | 11 | 1
|
|
1 | 12 | 2 | 12 | 2
|
|
1 | 13 | 3 | 13 | 3
|
|
2 | 12 | 4 | 12 | 4
|
|
2 | 13 | 5 | 13 | 5
|
|
3 | 13 | 6 | 13 | 6
|
|
(6 rows)
|
|
|
|
SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( rngfunc_sql(10+r1,13), rngfunc_mat(10+r2,13) );
|
|
r1 | r2 | i | s | i | s
|
|
----+----+----+----+----+---
|
|
1 | 1 | 11 | 1 | 11 | 1
|
|
1 | 1 | 12 | 2 | 12 | 2
|
|
1 | 1 | 13 | 3 | 13 | 3
|
|
1 | 2 | 11 | 4 | 12 | 4
|
|
1 | 2 | 12 | 5 | 13 | 5
|
|
1 | 2 | 13 | 6 | |
|
|
1 | 3 | 11 | 7 | 13 | 6
|
|
1 | 3 | 12 | 8 | |
|
|
1 | 3 | 13 | 9 | |
|
|
2 | 2 | 12 | 10 | 12 | 7
|
|
2 | 2 | 13 | 11 | 13 | 8
|
|
2 | 3 | 12 | 12 | 13 | 9
|
|
2 | 3 | 13 | 13 | |
|
|
(13 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) f(i);
|
|
r | i
|
|
---+----
|
|
1 | 11
|
|
1 | 12
|
|
1 | 13
|
|
1 | 14
|
|
1 | 15
|
|
1 | 16
|
|
1 | 17
|
|
1 | 18
|
|
1 | 19
|
|
2 | 12
|
|
2 | 13
|
|
2 | 14
|
|
2 | 15
|
|
2 | 16
|
|
2 | 17
|
|
2 | 18
|
|
3 | 13
|
|
3 | 14
|
|
3 | 15
|
|
3 | 16
|
|
3 | 17
|
|
(21 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) WITH ORDINALITY AS f(i,o);
|
|
r | i | o
|
|
---+----+---
|
|
1 | 11 | 1
|
|
1 | 12 | 2
|
|
1 | 13 | 3
|
|
1 | 14 | 4
|
|
1 | 15 | 5
|
|
1 | 16 | 6
|
|
1 | 17 | 7
|
|
1 | 18 | 8
|
|
1 | 19 | 9
|
|
2 | 12 | 1
|
|
2 | 13 | 2
|
|
2 | 14 | 3
|
|
2 | 15 | 4
|
|
2 | 16 | 5
|
|
2 | 17 | 6
|
|
2 | 18 | 7
|
|
3 | 13 | 1
|
|
3 | 14 | 2
|
|
3 | 15 | 3
|
|
3 | 16 | 4
|
|
3 | 17 | 5
|
|
(21 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), unnest(array[r*10,r*20,r*30]) f(i);
|
|
r | i
|
|
---+----
|
|
1 | 10
|
|
1 | 20
|
|
1 | 30
|
|
2 | 20
|
|
2 | 40
|
|
2 | 60
|
|
3 | 30
|
|
3 | 60
|
|
3 | 90
|
|
(9 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), unnest(array[r*10,r*20,r*30]) WITH ORDINALITY AS f(i,o);
|
|
r | i | o
|
|
---+----+---
|
|
1 | 10 | 1
|
|
1 | 20 | 2
|
|
1 | 30 | 3
|
|
2 | 20 | 1
|
|
2 | 40 | 2
|
|
2 | 60 | 3
|
|
3 | 30 | 1
|
|
3 | 60 | 2
|
|
3 | 90 | 3
|
|
(9 rows)
|
|
|
|
-- deep nesting
|
|
SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
|
|
LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2)
|
|
LEFT JOIN generate_series(21,23) f(i) ON ((r2+i)<100) OFFSET 0) s1;
|
|
r1 | r1 | r2 | i
|
|
----+----+----+----
|
|
1 | 1 | 10 | 21
|
|
1 | 1 | 10 | 22
|
|
1 | 1 | 10 | 23
|
|
1 | 1 | 20 | 21
|
|
1 | 1 | 20 | 22
|
|
1 | 1 | 20 | 23
|
|
1 | 1 | 30 | 21
|
|
1 | 1 | 30 | 22
|
|
1 | 1 | 30 | 23
|
|
2 | 2 | 10 | 21
|
|
2 | 2 | 10 | 22
|
|
2 | 2 | 10 | 23
|
|
2 | 2 | 20 | 21
|
|
2 | 2 | 20 | 22
|
|
2 | 2 | 20 | 23
|
|
2 | 2 | 30 | 21
|
|
2 | 2 | 30 | 22
|
|
2 | 2 | 30 | 23
|
|
3 | 3 | 10 | 21
|
|
3 | 3 | 10 | 22
|
|
3 | 3 | 10 | 23
|
|
3 | 3 | 20 | 21
|
|
3 | 3 | 20 | 22
|
|
3 | 3 | 20 | 23
|
|
3 | 3 | 30 | 21
|
|
3 | 3 | 30 | 22
|
|
3 | 3 | 30 | 23
|
|
(27 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
|
|
LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2)
|
|
LEFT JOIN generate_series(20+r1,23) f(i) ON ((r2+i)<100) OFFSET 0) s1;
|
|
r1 | r1 | r2 | i
|
|
----+----+----+----
|
|
1 | 1 | 10 | 21
|
|
1 | 1 | 10 | 22
|
|
1 | 1 | 10 | 23
|
|
1 | 1 | 20 | 21
|
|
1 | 1 | 20 | 22
|
|
1 | 1 | 20 | 23
|
|
1 | 1 | 30 | 21
|
|
1 | 1 | 30 | 22
|
|
1 | 1 | 30 | 23
|
|
2 | 2 | 10 | 22
|
|
2 | 2 | 10 | 23
|
|
2 | 2 | 20 | 22
|
|
2 | 2 | 20 | 23
|
|
2 | 2 | 30 | 22
|
|
2 | 2 | 30 | 23
|
|
3 | 3 | 10 | 23
|
|
3 | 3 | 20 | 23
|
|
3 | 3 | 30 | 23
|
|
(18 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
|
|
LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2)
|
|
LEFT JOIN generate_series(r2,r2+3) f(i) ON ((r2+i)<100) OFFSET 0) s1;
|
|
r1 | r1 | r2 | i
|
|
----+----+----+----
|
|
1 | 1 | 10 | 10
|
|
1 | 1 | 10 | 11
|
|
1 | 1 | 10 | 12
|
|
1 | 1 | 10 | 13
|
|
1 | 1 | 20 | 20
|
|
1 | 1 | 20 | 21
|
|
1 | 1 | 20 | 22
|
|
1 | 1 | 20 | 23
|
|
1 | 1 | 30 | 30
|
|
1 | 1 | 30 | 31
|
|
1 | 1 | 30 | 32
|
|
1 | 1 | 30 | 33
|
|
2 | 2 | 10 | 10
|
|
2 | 2 | 10 | 11
|
|
2 | 2 | 10 | 12
|
|
2 | 2 | 10 | 13
|
|
2 | 2 | 20 | 20
|
|
2 | 2 | 20 | 21
|
|
2 | 2 | 20 | 22
|
|
2 | 2 | 20 | 23
|
|
2 | 2 | 30 | 30
|
|
2 | 2 | 30 | 31
|
|
2 | 2 | 30 | 32
|
|
2 | 2 | 30 | 33
|
|
3 | 3 | 10 | 10
|
|
3 | 3 | 10 | 11
|
|
3 | 3 | 10 | 12
|
|
3 | 3 | 10 | 13
|
|
3 | 3 | 20 | 20
|
|
3 | 3 | 20 | 21
|
|
3 | 3 | 20 | 22
|
|
3 | 3 | 20 | 23
|
|
3 | 3 | 30 | 30
|
|
3 | 3 | 30 | 31
|
|
3 | 3 | 30 | 32
|
|
3 | 3 | 30 | 33
|
|
(36 rows)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
|
|
LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2)
|
|
LEFT JOIN generate_series(r1,2+r2/5) f(i) ON ((r2+i)<100) OFFSET 0) s1;
|
|
r1 | r1 | r2 | i
|
|
----+----+----+---
|
|
1 | 1 | 10 | 1
|
|
1 | 1 | 10 | 2
|
|
1 | 1 | 10 | 3
|
|
1 | 1 | 10 | 4
|
|
1 | 1 | 20 | 1
|
|
1 | 1 | 20 | 2
|
|
1 | 1 | 20 | 3
|
|
1 | 1 | 20 | 4
|
|
1 | 1 | 20 | 5
|
|
1 | 1 | 20 | 6
|
|
1 | 1 | 30 | 1
|
|
1 | 1 | 30 | 2
|
|
1 | 1 | 30 | 3
|
|
1 | 1 | 30 | 4
|
|
1 | 1 | 30 | 5
|
|
1 | 1 | 30 | 6
|
|
1 | 1 | 30 | 7
|
|
1 | 1 | 30 | 8
|
|
2 | 2 | 10 | 2
|
|
2 | 2 | 10 | 3
|
|
2 | 2 | 10 | 4
|
|
2 | 2 | 20 | 2
|
|
2 | 2 | 20 | 3
|
|
2 | 2 | 20 | 4
|
|
2 | 2 | 20 | 5
|
|
2 | 2 | 20 | 6
|
|
2 | 2 | 30 | 2
|
|
2 | 2 | 30 | 3
|
|
2 | 2 | 30 | 4
|
|
2 | 2 | 30 | 5
|
|
2 | 2 | 30 | 6
|
|
2 | 2 | 30 | 7
|
|
2 | 2 | 30 | 8
|
|
3 | 3 | 10 | 3
|
|
3 | 3 | 10 | 4
|
|
3 | 3 | 20 | 3
|
|
3 | 3 | 20 | 4
|
|
3 | 3 | 20 | 5
|
|
3 | 3 | 20 | 6
|
|
3 | 3 | 30 | 3
|
|
3 | 3 | 30 | 4
|
|
3 | 3 | 30 | 5
|
|
3 | 3 | 30 | 6
|
|
3 | 3 | 30 | 7
|
|
3 | 3 | 30 | 8
|
|
(45 rows)
|
|
|
|
-- check handling of FULL JOIN with multiple lateral references (bug #15741)
|
|
SELECT *
|
|
FROM (VALUES (1),(2)) v1(r1)
|
|
LEFT JOIN LATERAL (
|
|
SELECT *
|
|
FROM generate_series(1, v1.r1) AS gs1
|
|
LEFT JOIN LATERAL (
|
|
SELECT *
|
|
FROM generate_series(1, gs1) AS gs2
|
|
LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE
|
|
) AS ss1 ON TRUE
|
|
FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE
|
|
) AS ss0 ON TRUE;
|
|
r1 | gs1 | gs2 | gs3 | gs4
|
|
----+-----+-----+-----+-----
|
|
1 | | | | 1
|
|
1 | 1 | 1 | 1 |
|
|
2 | | | | 1
|
|
2 | | | | 2
|
|
2 | 1 | 1 | 1 |
|
|
2 | 2 | 1 | 1 |
|
|
2 | 2 | 2 | 1 |
|
|
2 | 2 | 2 | 2 |
|
|
(8 rows)
|
|
|
|
DROP FUNCTION rngfunc_sql(int,int);
|
|
DROP FUNCTION rngfunc_mat(int,int);
|
|
DROP SEQUENCE rngfunc_rescan_seq1;
|
|
DROP SEQUENCE rngfunc_rescan_seq2;
|
|
--
|
|
-- Test cases involving OUT parameters
|
|
--
|
|
CREATE FUNCTION rngfunc(in f1 int, out f2 int)
|
|
AS 'select $1+1' LANGUAGE sql;
|
|
SELECT rngfunc(42);
|
|
rngfunc
|
|
---------
|
|
43
|
|
(1 row)
|
|
|
|
SELECT * FROM rngfunc(42);
|
|
f2
|
|
----
|
|
43
|
|
(1 row)
|
|
|
|
SELECT * FROM rngfunc(42) AS p(x);
|
|
x
|
|
----
|
|
43
|
|
(1 row)
|
|
|
|
-- explicit spec of return type is OK
|
|
CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int) RETURNS int
|
|
AS 'select $1+1' LANGUAGE sql;
|
|
-- error, wrong result type
|
|
CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int) RETURNS float
|
|
AS 'select $1+1' LANGUAGE sql;
|
|
ERROR: function result type must be integer because of OUT parameters
|
|
-- with multiple OUT params you must get a RECORD result
|
|
CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text) RETURNS int
|
|
AS 'select $1+1' LANGUAGE sql;
|
|
ERROR: function result type must be record because of OUT parameters
|
|
CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text)
|
|
RETURNS record
|
|
AS 'select $1+1' LANGUAGE sql;
|
|
ERROR: cannot change return type of existing function
|
|
HINT: Use DROP FUNCTION rngfunc(integer) first.
|
|
CREATE OR REPLACE FUNCTION rngfuncr(in f1 int, out f2 int, out text)
|
|
AS $$select $1-1, $1::text || 'z'$$ LANGUAGE sql;
|
|
SELECT f1, rngfuncr(f1) FROM int4_tbl;
|
|
f1 | rngfuncr
|
|
-------------+----------------------------
|
|
0 | (-1,0z)
|
|
123456 | (123455,123456z)
|
|
-123456 | (-123457,-123456z)
|
|
2147483647 | (2147483646,2147483647z)
|
|
-2147483647 | (-2147483648,-2147483647z)
|
|
(5 rows)
|
|
|
|
SELECT * FROM rngfuncr(42);
|
|
f2 | column2
|
|
----+---------
|
|
41 | 42z
|
|
(1 row)
|
|
|
|
SELECT * FROM rngfuncr(42) AS p(a,b);
|
|
a | b
|
|
----+-----
|
|
41 | 42z
|
|
(1 row)
|
|
|
|
CREATE OR REPLACE FUNCTION rngfuncb(in f1 int, inout f2 int, out text)
|
|
AS $$select $2-1, $1::text || 'z'$$ LANGUAGE sql;
|
|
SELECT f1, rngfuncb(f1, f1/2) FROM int4_tbl;
|
|
f1 | rngfuncb
|
|
-------------+----------------------------
|
|
0 | (-1,0z)
|
|
123456 | (61727,123456z)
|
|
-123456 | (-61729,-123456z)
|
|
2147483647 | (1073741822,2147483647z)
|
|
-2147483647 | (-1073741824,-2147483647z)
|
|
(5 rows)
|
|
|
|
SELECT * FROM rngfuncb(42, 99);
|
|
f2 | column2
|
|
----+---------
|
|
98 | 42z
|
|
(1 row)
|
|
|
|
SELECT * FROM rngfuncb(42, 99) AS p(a,b);
|
|
a | b
|
|
----+-----
|
|
98 | 42z
|
|
(1 row)
|
|
|
|
-- Can reference function with or without OUT params for DROP, etc
|
|
DROP FUNCTION rngfunc(int);
|
|
DROP FUNCTION rngfuncr(in f2 int, out f1 int, out text);
|
|
DROP FUNCTION rngfuncb(in f1 int, inout f2 int);
|
|
--
|
|
-- For my next trick, polymorphic OUT parameters
|
|
--
|
|
CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray)
|
|
AS 'select $1, array[$1,$1]' LANGUAGE sql;
|
|
SELECT dup(22);
|
|
dup
|
|
----------------
|
|
(22,"{22,22}")
|
|
(1 row)
|
|
|
|
SELECT dup('xyz'); -- fails
|
|
ERROR: could not determine polymorphic type because input has type unknown
|
|
SELECT dup('xyz'::text);
|
|
dup
|
|
-------------------
|
|
(xyz,"{xyz,xyz}")
|
|
(1 row)
|
|
|
|
SELECT * FROM dup('xyz'::text);
|
|
f2 | f3
|
|
-----+-----------
|
|
xyz | {xyz,xyz}
|
|
(1 row)
|
|
|
|
-- fails, as we are attempting to rename first argument
|
|
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
|
|
AS 'select $1, array[$1,$1]' LANGUAGE sql;
|
|
ERROR: cannot change name of input parameter "f1"
|
|
HINT: Use DROP FUNCTION dup(anyelement) first.
|
|
DROP FUNCTION dup(anyelement);
|
|
-- equivalent behavior, though different name exposed for input arg
|
|
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
|
|
AS 'select $1, array[$1,$1]' LANGUAGE sql;
|
|
SELECT dup(22);
|
|
dup
|
|
----------------
|
|
(22,"{22,22}")
|
|
(1 row)
|
|
|
|
DROP FUNCTION dup(anyelement);
|
|
-- fails, no way to deduce outputs
|
|
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
|
|
AS 'select $1, array[$1,$1]' LANGUAGE sql;
|
|
ERROR: cannot determine result data type
|
|
DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
|
|
CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
|
|
AS 'select $1, $2' LANGUAGE sql;
|
|
SELECT dup(22, array[44]);
|
|
dup
|
|
-----------
|
|
(22,{44})
|
|
(1 row)
|
|
|
|
SELECT dup(4.5, array[44]);
|
|
dup
|
|
------------
|
|
(4.5,{44})
|
|
(1 row)
|
|
|
|
SELECT dup(22, array[44::bigint]);
|
|
dup
|
|
-----------
|
|
(22,{44})
|
|
(1 row)
|
|
|
|
SELECT *, pg_typeof(f3), pg_typeof(f4) FROM dup(22, array[44::bigint]);
|
|
f3 | f4 | pg_typeof | pg_typeof
|
|
----+------+-----------+-----------
|
|
22 | {44} | bigint | bigint[]
|
|
(1 row)
|
|
|
|
DROP FUNCTION dup(f1 anycompatible, f2 anycompatiblearray);
|
|
CREATE FUNCTION dup (f1 anycompatiblerange, f2 out anycompatible, f3 out anycompatiblearray, f4 out anycompatiblerange)
|
|
AS 'select lower($1), array[lower($1), upper($1)], $1' LANGUAGE sql;
|
|
SELECT dup(int4range(4,7));
|
|
dup
|
|
---------------------
|
|
(4,"{4,7}","[4,7)")
|
|
(1 row)
|
|
|
|
SELECT dup(numrange(4,7));
|
|
dup
|
|
---------------------
|
|
(4,"{4,7}","[4,7)")
|
|
(1 row)
|
|
|
|
SELECT dup(textrange('aaa', 'bbb'));
|
|
dup
|
|
-------------------------------
|
|
(aaa,"{aaa,bbb}","[aaa,bbb)")
|
|
(1 row)
|
|
|
|
DROP FUNCTION dup(f1 anycompatiblerange);
|
|
-- fails, no way to deduce outputs
|
|
CREATE FUNCTION bad (f1 anyarray, out f2 anycompatible, out f3 anycompatiblearray)
|
|
AS 'select $1, array[$1,$1]' LANGUAGE sql;
|
|
ERROR: cannot determine result data type
|
|
DETAIL: A result of type anycompatible requires at least one input of type anycompatible, anycompatiblearray, anycompatiblenonarray, anycompatiblerange, or anycompatiblemultirange.
|
|
--
|
|
-- table functions
|
|
--
|
|
CREATE OR REPLACE FUNCTION rngfunc()
|
|
RETURNS TABLE(a int)
|
|
AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql;
|
|
SELECT * FROM rngfunc();
|
|
a
|
|
---
|
|
1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
(5 rows)
|
|
|
|
DROP FUNCTION rngfunc();
|
|
CREATE OR REPLACE FUNCTION rngfunc(int)
|
|
RETURNS TABLE(a int, b int)
|
|
AS $$ SELECT a, b
|
|
FROM generate_series(1,$1) a(a),
|
|
generate_series(1,$1) b(b) $$ LANGUAGE sql;
|
|
SELECT * FROM rngfunc(3);
|
|
a | b
|
|
---+---
|
|
1 | 1
|
|
1 | 2
|
|
1 | 3
|
|
2 | 1
|
|
2 | 2
|
|
2 | 3
|
|
3 | 1
|
|
3 | 2
|
|
3 | 3
|
|
(9 rows)
|
|
|
|
DROP FUNCTION rngfunc(int);
|
|
-- case that causes change of typmod knowledge during inlining
|
|
CREATE OR REPLACE FUNCTION rngfunc()
|
|
RETURNS TABLE(a varchar(5))
|
|
AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE;
|
|
SELECT * FROM rngfunc() GROUP BY 1;
|
|
a
|
|
-------
|
|
hello
|
|
(1 row)
|
|
|
|
DROP FUNCTION rngfunc();
|
|
--
|
|
-- some tests on SQL functions with RETURNING
|
|
--
|
|
create temp table tt(f1 serial, data text);
|
|
create function insert_tt(text) returns int as
|
|
$$ insert into tt(data) values($1) returning f1 $$
|
|
language sql;
|
|
select insert_tt('foo');
|
|
insert_tt
|
|
-----------
|
|
1
|
|
(1 row)
|
|
|
|
select insert_tt('bar');
|
|
insert_tt
|
|
-----------
|
|
2
|
|
(1 row)
|
|
|
|
select * from tt;
|
|
f1 | data
|
|
----+------
|
|
1 | foo
|
|
2 | bar
|
|
(2 rows)
|
|
|
|
-- insert will execute to completion even if function needs just 1 row
|
|
create or replace function insert_tt(text) returns int as
|
|
$$ insert into tt(data) values($1),($1||$1) returning f1 $$
|
|
language sql;
|
|
select insert_tt('fool');
|
|
insert_tt
|
|
-----------
|
|
3
|
|
(1 row)
|
|
|
|
select * from tt;
|
|
f1 | data
|
|
----+----------
|
|
1 | foo
|
|
2 | bar
|
|
3 | fool
|
|
4 | foolfool
|
|
(4 rows)
|
|
|
|
-- setof does what's expected
|
|
create or replace function insert_tt2(text,text) returns setof int as
|
|
$$ insert into tt(data) values($1),($2) returning f1 $$
|
|
language sql;
|
|
select insert_tt2('foolish','barrish');
|
|
insert_tt2
|
|
------------
|
|
5
|
|
6
|
|
(2 rows)
|
|
|
|
select * from insert_tt2('baz','quux');
|
|
insert_tt2
|
|
------------
|
|
7
|
|
8
|
|
(2 rows)
|
|
|
|
select * from tt;
|
|
f1 | data
|
|
----+----------
|
|
1 | foo
|
|
2 | bar
|
|
3 | fool
|
|
4 | foolfool
|
|
5 | foolish
|
|
6 | barrish
|
|
7 | baz
|
|
8 | quux
|
|
(8 rows)
|
|
|
|
-- limit doesn't prevent execution to completion
|
|
select insert_tt2('foolish','barrish') limit 1;
|
|
insert_tt2
|
|
------------
|
|
9
|
|
(1 row)
|
|
|
|
select * from tt;
|
|
f1 | data
|
|
----+----------
|
|
1 | foo
|
|
2 | bar
|
|
3 | fool
|
|
4 | foolfool
|
|
5 | foolish
|
|
6 | barrish
|
|
7 | baz
|
|
8 | quux
|
|
9 | foolish
|
|
10 | barrish
|
|
(10 rows)
|
|
|
|
-- triggers will fire, too
|
|
create function noticetrigger() returns trigger as $$
|
|
begin
|
|
raise notice 'noticetrigger % %', new.f1, new.data;
|
|
return null;
|
|
end $$ language plpgsql;
|
|
create trigger tnoticetrigger after insert on tt for each row
|
|
execute procedure noticetrigger();
|
|
select insert_tt2('foolme','barme') limit 1;
|
|
NOTICE: noticetrigger 11 foolme
|
|
NOTICE: noticetrigger 12 barme
|
|
insert_tt2
|
|
------------
|
|
11
|
|
(1 row)
|
|
|
|
select * from tt;
|
|
f1 | data
|
|
----+----------
|
|
1 | foo
|
|
2 | bar
|
|
3 | fool
|
|
4 | foolfool
|
|
5 | foolish
|
|
6 | barrish
|
|
7 | baz
|
|
8 | quux
|
|
9 | foolish
|
|
10 | barrish
|
|
11 | foolme
|
|
12 | barme
|
|
(12 rows)
|
|
|
|
-- and rules work
|
|
create temp table tt_log(f1 int, data text);
|
|
create rule insert_tt_rule as on insert to tt do also
|
|
insert into tt_log values(new.*);
|
|
select insert_tt2('foollog','barlog') limit 1;
|
|
NOTICE: noticetrigger 13 foollog
|
|
NOTICE: noticetrigger 14 barlog
|
|
insert_tt2
|
|
------------
|
|
13
|
|
(1 row)
|
|
|
|
select * from tt;
|
|
f1 | data
|
|
----+----------
|
|
1 | foo
|
|
2 | bar
|
|
3 | fool
|
|
4 | foolfool
|
|
5 | foolish
|
|
6 | barrish
|
|
7 | baz
|
|
8 | quux
|
|
9 | foolish
|
|
10 | barrish
|
|
11 | foolme
|
|
12 | barme
|
|
13 | foollog
|
|
14 | barlog
|
|
(14 rows)
|
|
|
|
-- note that nextval() gets executed a second time in the rule expansion,
|
|
-- which is expected.
|
|
select * from tt_log;
|
|
f1 | data
|
|
----+---------
|
|
15 | foollog
|
|
16 | barlog
|
|
(2 rows)
|
|
|
|
-- test case for a whole-row-variable bug
|
|
create function rngfunc1(n integer, out a text, out b text)
|
|
returns setof record
|
|
language sql
|
|
as $$ select 'foo ' || i, 'bar ' || i from generate_series(1,$1) i $$;
|
|
set work_mem='64kB';
|
|
select t.a, t, t.a from rngfunc1(10000) t limit 1;
|
|
a | t | a
|
|
-------+-------------------+-------
|
|
foo 1 | ("foo 1","bar 1") | foo 1
|
|
(1 row)
|
|
|
|
reset work_mem;
|
|
select t.a, t, t.a from rngfunc1(10000) t limit 1;
|
|
a | t | a
|
|
-------+-------------------+-------
|
|
foo 1 | ("foo 1","bar 1") | foo 1
|
|
(1 row)
|
|
|
|
drop function rngfunc1(n integer);
|
|
-- test use of SQL functions returning record
|
|
-- this is supported in some cases where the query doesn't specify
|
|
-- the actual record type ...
|
|
create function array_to_set(anyarray) returns setof record as $$
|
|
select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i
|
|
$$ language sql strict immutable;
|
|
select array_to_set(array['one', 'two']);
|
|
array_to_set
|
|
--------------
|
|
(1,one)
|
|
(2,two)
|
|
(2 rows)
|
|
|
|
select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text);
|
|
f1 | f2
|
|
----+-----
|
|
1 | one
|
|
2 | two
|
|
(2 rows)
|
|
|
|
select * from array_to_set(array['one', 'two']); -- fail
|
|
ERROR: a column definition list is required for functions returning "record"
|
|
LINE 1: select * from array_to_set(array['one', 'two']);
|
|
^
|
|
-- after-the-fact coercion of the columns is now possible, too
|
|
select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text);
|
|
f1 | f2
|
|
------+-----
|
|
1.00 | one
|
|
2.00 | two
|
|
(2 rows)
|
|
|
|
-- and if it doesn't work, you get a compile-time not run-time error
|
|
select * from array_to_set(array['one', 'two']) as t(f1 point,f2 text);
|
|
ERROR: return type mismatch in function declared to return record
|
|
DETAIL: Final statement returns integer instead of point at column 1.
|
|
CONTEXT: SQL function "array_to_set" during startup
|
|
-- with "strict", this function can't be inlined in FROM
|
|
explain (verbose, costs off)
|
|
select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text);
|
|
QUERY PLAN
|
|
----------------------------------------------------
|
|
Function Scan on public.array_to_set t
|
|
Output: f1, f2
|
|
Function Call: array_to_set('{one,two}'::text[])
|
|
(3 rows)
|
|
|
|
-- but without, it can be:
|
|
create or replace function array_to_set(anyarray) returns setof record as $$
|
|
select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i
|
|
$$ language sql immutable;
|
|
select array_to_set(array['one', 'two']);
|
|
array_to_set
|
|
--------------
|
|
(1,one)
|
|
(2,two)
|
|
(2 rows)
|
|
|
|
select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text);
|
|
f1 | f2
|
|
----+-----
|
|
1 | one
|
|
2 | two
|
|
(2 rows)
|
|
|
|
select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text);
|
|
f1 | f2
|
|
------+-----
|
|
1.00 | one
|
|
2.00 | two
|
|
(2 rows)
|
|
|
|
select * from array_to_set(array['one', 'two']) as t(f1 point,f2 text);
|
|
ERROR: return type mismatch in function declared to return record
|
|
DETAIL: Final statement returns integer instead of point at column 1.
|
|
CONTEXT: SQL function "array_to_set" during inlining
|
|
explain (verbose, costs off)
|
|
select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text);
|
|
QUERY PLAN
|
|
--------------------------------------------------------------
|
|
Function Scan on pg_catalog.generate_subscripts i
|
|
Output: i.i, ('{one,two}'::text[])[i.i]
|
|
Function Call: generate_subscripts('{one,two}'::text[], 1)
|
|
(3 rows)
|
|
|
|
create temp table rngfunc(f1 int8, f2 int8);
|
|
create function testrngfunc() returns record as $$
|
|
insert into rngfunc values (1,2) returning *;
|
|
$$ language sql;
|
|
select testrngfunc();
|
|
testrngfunc
|
|
-------------
|
|
(1,2)
|
|
(1 row)
|
|
|
|
select * from testrngfunc() as t(f1 int8,f2 int8);
|
|
f1 | f2
|
|
----+----
|
|
1 | 2
|
|
(1 row)
|
|
|
|
select * from testrngfunc(); -- fail
|
|
ERROR: a column definition list is required for functions returning "record"
|
|
LINE 1: select * from testrngfunc();
|
|
^
|
|
drop function testrngfunc();
|
|
create function testrngfunc() returns setof record as $$
|
|
insert into rngfunc values (1,2), (3,4) returning *;
|
|
$$ language sql;
|
|
select testrngfunc();
|
|
testrngfunc
|
|
-------------
|
|
(1,2)
|
|
(3,4)
|
|
(2 rows)
|
|
|
|
select * from testrngfunc() as t(f1 int8,f2 int8);
|
|
f1 | f2
|
|
----+----
|
|
1 | 2
|
|
3 | 4
|
|
(2 rows)
|
|
|
|
select * from testrngfunc(); -- fail
|
|
ERROR: a column definition list is required for functions returning "record"
|
|
LINE 1: select * from testrngfunc();
|
|
^
|
|
drop function testrngfunc();
|
|
-- Check that typmod imposed by a composite type is honored
|
|
create type rngfunc_type as (f1 numeric(35,6), f2 numeric(35,2));
|
|
create function testrngfunc() returns rngfunc_type as $$
|
|
select 7.136178319899999964, 7.136178319899999964;
|
|
$$ language sql immutable;
|
|
explain (verbose, costs off)
|
|
select testrngfunc();
|
|
QUERY PLAN
|
|
-------------------------------------------
|
|
Result
|
|
Output: '(7.136178,7.14)'::rngfunc_type
|
|
(2 rows)
|
|
|
|
select testrngfunc();
|
|
testrngfunc
|
|
-----------------
|
|
(7.136178,7.14)
|
|
(1 row)
|
|
|
|
explain (verbose, costs off)
|
|
select * from testrngfunc();
|
|
QUERY PLAN
|
|
--------------------------------------------------
|
|
Function Scan on testrngfunc
|
|
Output: f1, f2
|
|
Function Call: '(7.136178,7.14)'::rngfunc_type
|
|
(3 rows)
|
|
|
|
select * from testrngfunc();
|
|
f1 | f2
|
|
----------+------
|
|
7.136178 | 7.14
|
|
(1 row)
|
|
|
|
create or replace function testrngfunc() returns rngfunc_type as $$
|
|
select 7.136178319899999964, 7.136178319899999964;
|
|
$$ language sql volatile;
|
|
explain (verbose, costs off)
|
|
select testrngfunc();
|
|
QUERY PLAN
|
|
-------------------------
|
|
Result
|
|
Output: testrngfunc()
|
|
(2 rows)
|
|
|
|
select testrngfunc();
|
|
testrngfunc
|
|
-----------------
|
|
(7.136178,7.14)
|
|
(1 row)
|
|
|
|
explain (verbose, costs off)
|
|
select * from testrngfunc();
|
|
QUERY PLAN
|
|
-------------------------------------
|
|
Function Scan on public.testrngfunc
|
|
Output: f1, f2
|
|
Function Call: testrngfunc()
|
|
(3 rows)
|
|
|
|
select * from testrngfunc();
|
|
f1 | f2
|
|
----------+------
|
|
7.136178 | 7.14
|
|
(1 row)
|
|
|
|
drop function testrngfunc();
|
|
create function testrngfunc() returns setof rngfunc_type as $$
|
|
select 7.136178319899999964, 7.136178319899999964;
|
|
$$ language sql immutable;
|
|
explain (verbose, costs off)
|
|
select testrngfunc();
|
|
QUERY PLAN
|
|
-------------------------
|
|
ProjectSet
|
|
Output: testrngfunc()
|
|
-> Result
|
|
(3 rows)
|
|
|
|
select testrngfunc();
|
|
testrngfunc
|
|
-----------------
|
|
(7.136178,7.14)
|
|
(1 row)
|
|
|
|
explain (verbose, costs off)
|
|
select * from testrngfunc();
|
|
QUERY PLAN
|
|
--------------------------------------------------------
|
|
Result
|
|
Output: 7.136178::numeric(35,6), 7.14::numeric(35,2)
|
|
(2 rows)
|
|
|
|
select * from testrngfunc();
|
|
f1 | f2
|
|
----------+------
|
|
7.136178 | 7.14
|
|
(1 row)
|
|
|
|
create or replace function testrngfunc() returns setof rngfunc_type as $$
|
|
select 7.136178319899999964, 7.136178319899999964;
|
|
$$ language sql volatile;
|
|
explain (verbose, costs off)
|
|
select testrngfunc();
|
|
QUERY PLAN
|
|
-------------------------
|
|
ProjectSet
|
|
Output: testrngfunc()
|
|
-> Result
|
|
(3 rows)
|
|
|
|
select testrngfunc();
|
|
testrngfunc
|
|
-----------------
|
|
(7.136178,7.14)
|
|
(1 row)
|
|
|
|
explain (verbose, costs off)
|
|
select * from testrngfunc();
|
|
QUERY PLAN
|
|
-------------------------------------
|
|
Function Scan on public.testrngfunc
|
|
Output: f1, f2
|
|
Function Call: testrngfunc()
|
|
(3 rows)
|
|
|
|
select * from testrngfunc();
|
|
f1 | f2
|
|
----------+------
|
|
7.136178 | 7.14
|
|
(1 row)
|
|
|
|
create or replace function testrngfunc() returns setof rngfunc_type as $$
|
|
select 1, 2 union select 3, 4 order by 1;
|
|
$$ language sql immutable;
|
|
explain (verbose, costs off)
|
|
select testrngfunc();
|
|
QUERY PLAN
|
|
-------------------------
|
|
ProjectSet
|
|
Output: testrngfunc()
|
|
-> Result
|
|
(3 rows)
|
|
|
|
select testrngfunc();
|
|
testrngfunc
|
|
-----------------
|
|
(1.000000,2.00)
|
|
(3.000000,4.00)
|
|
(2 rows)
|
|
|
|
explain (verbose, costs off)
|
|
select * from testrngfunc();
|
|
QUERY PLAN
|
|
----------------------------------------------------------
|
|
Subquery Scan on "*SELECT*"
|
|
Output: "*SELECT*"."?column?", "*SELECT*"."?column?_1"
|
|
-> Unique
|
|
Output: (1), (2)
|
|
-> Sort
|
|
Output: (1), (2)
|
|
Sort Key: (1), (2)
|
|
-> Append
|
|
-> Result
|
|
Output: 1, 2
|
|
-> Result
|
|
Output: 3, 4
|
|
(12 rows)
|
|
|
|
select * from testrngfunc();
|
|
f1 | f2
|
|
----------+------
|
|
1.000000 | 2.00
|
|
3.000000 | 4.00
|
|
(2 rows)
|
|
|
|
-- Check a couple of error cases while we're here
|
|
select * from testrngfunc() as t(f1 int8,f2 int8); -- fail, composite result
|
|
ERROR: a column definition list is redundant for a function returning a named composite type
|
|
LINE 1: select * from testrngfunc() as t(f1 int8,f2 int8);
|
|
^
|
|
select * from pg_get_keywords() as t(f1 int8,f2 int8); -- fail, OUT params
|
|
ERROR: a column definition list is redundant for a function with OUT parameters
|
|
LINE 1: select * from pg_get_keywords() as t(f1 int8,f2 int8);
|
|
^
|
|
select * from sin(3) as t(f1 int8,f2 int8); -- fail, scalar result type
|
|
ERROR: a column definition list is only allowed for functions returning "record"
|
|
LINE 1: select * from sin(3) as t(f1 int8,f2 int8);
|
|
^
|
|
drop type rngfunc_type cascade;
|
|
NOTICE: drop cascades to function testrngfunc()
|
|
--
|
|
-- Check some cases involving added/dropped columns in a rowtype result
|
|
--
|
|
create temp table users (userid text, seq int, email text, todrop bool, moredrop int, enabled bool);
|
|
insert into users values ('id',1,'email',true,11,true);
|
|
insert into users values ('id2',2,'email2',true,12,true);
|
|
alter table users drop column todrop;
|
|
create or replace function get_first_user() returns users as
|
|
$$ SELECT * FROM users ORDER BY userid LIMIT 1; $$
|
|
language sql stable;
|
|
SELECT get_first_user();
|
|
get_first_user
|
|
-------------------
|
|
(id,1,email,11,t)
|
|
(1 row)
|
|
|
|
SELECT * FROM get_first_user();
|
|
userid | seq | email | moredrop | enabled
|
|
--------+-----+-------+----------+---------
|
|
id | 1 | email | 11 | t
|
|
(1 row)
|
|
|
|
create or replace function get_users() returns setof users as
|
|
$$ SELECT * FROM users ORDER BY userid; $$
|
|
language sql stable;
|
|
SELECT get_users();
|
|
get_users
|
|
---------------------
|
|
(id,1,email,11,t)
|
|
(id2,2,email2,12,t)
|
|
(2 rows)
|
|
|
|
SELECT * FROM get_users();
|
|
userid | seq | email | moredrop | enabled
|
|
--------+-----+--------+----------+---------
|
|
id | 1 | email | 11 | t
|
|
id2 | 2 | email2 | 12 | t
|
|
(2 rows)
|
|
|
|
SELECT * FROM get_users() WITH ORDINALITY; -- make sure ordinality copes
|
|
userid | seq | email | moredrop | enabled | ordinality
|
|
--------+-----+--------+----------+---------+------------
|
|
id | 1 | email | 11 | t | 1
|
|
id2 | 2 | email2 | 12 | t | 2
|
|
(2 rows)
|
|
|
|
-- multiple functions vs. dropped columns
|
|
SELECT * FROM ROWS FROM(generate_series(10,11), get_users()) WITH ORDINALITY;
|
|
generate_series | userid | seq | email | moredrop | enabled | ordinality
|
|
-----------------+--------+-----+--------+----------+---------+------------
|
|
10 | id | 1 | email | 11 | t | 1
|
|
11 | id2 | 2 | email2 | 12 | t | 2
|
|
(2 rows)
|
|
|
|
SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
|
|
userid | seq | email | moredrop | enabled | generate_series | ordinality
|
|
--------+-----+--------+----------+---------+-----------------+------------
|
|
id | 1 | email | 11 | t | 10 | 1
|
|
id2 | 2 | email2 | 12 | t | 11 | 2
|
|
(2 rows)
|
|
|
|
-- check that we can cope with post-parsing changes in rowtypes
|
|
create temp view usersview as
|
|
SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
|
|
select * from usersview;
|
|
userid | seq | email | moredrop | enabled | generate_series | ordinality
|
|
--------+-----+--------+----------+---------+-----------------+------------
|
|
id | 1 | email | 11 | t | 10 | 1
|
|
id2 | 2 | email2 | 12 | t | 11 | 2
|
|
(2 rows)
|
|
|
|
alter table users add column junk text;
|
|
select * from usersview;
|
|
userid | seq | email | moredrop | enabled | generate_series | ordinality
|
|
--------+-----+--------+----------+---------+-----------------+------------
|
|
id | 1 | email | 11 | t | 10 | 1
|
|
id2 | 2 | email2 | 12 | t | 11 | 2
|
|
(2 rows)
|
|
|
|
begin;
|
|
alter table users drop column moredrop;
|
|
select * from usersview; -- expect clean failure
|
|
ERROR: attribute 5 of type record has been dropped
|
|
rollback;
|
|
alter table users alter column seq type numeric;
|
|
select * from usersview; -- expect clean failure
|
|
ERROR: attribute 2 of type record has wrong type
|
|
DETAIL: Table has type numeric, but query expects integer.
|
|
drop view usersview;
|
|
drop function get_first_user();
|
|
drop function get_users();
|
|
drop table users;
|
|
-- check behavior with type coercion required for a set-op
|
|
create or replace function rngfuncbar() returns setof text as
|
|
$$ select 'foo'::varchar union all select 'bar'::varchar ; $$
|
|
language sql stable;
|
|
select rngfuncbar();
|
|
rngfuncbar
|
|
------------
|
|
foo
|
|
bar
|
|
(2 rows)
|
|
|
|
select * from rngfuncbar();
|
|
rngfuncbar
|
|
------------
|
|
foo
|
|
bar
|
|
(2 rows)
|
|
|
|
-- this function is now inlinable, too:
|
|
explain (verbose, costs off) select * from rngfuncbar();
|
|
QUERY PLAN
|
|
------------------------------------------------
|
|
Result
|
|
Output: ('foo'::character varying)
|
|
-> Append
|
|
-> Result
|
|
Output: 'foo'::character varying
|
|
-> Result
|
|
Output: 'bar'::character varying
|
|
(7 rows)
|
|
|
|
drop function rngfuncbar();
|
|
-- check handling of a SQL function with multiple OUT params (bug #5777)
|
|
create or replace function rngfuncbar(out integer, out numeric) as
|
|
$$ select (1, 2.1) $$ language sql;
|
|
select * from rngfuncbar();
|
|
column1 | column2
|
|
---------+---------
|
|
1 | 2.1
|
|
(1 row)
|
|
|
|
create or replace function rngfuncbar(out integer, out numeric) as
|
|
$$ select (1, 2) $$ language sql;
|
|
select * from rngfuncbar(); -- fail
|
|
ERROR: function return row and query-specified return row do not match
|
|
DETAIL: Returned type integer at ordinal position 2, but query expects numeric.
|
|
create or replace function rngfuncbar(out integer, out numeric) as
|
|
$$ select (1, 2.1, 3) $$ language sql;
|
|
select * from rngfuncbar(); -- fail
|
|
ERROR: function return row and query-specified return row do not match
|
|
DETAIL: Returned row contains 3 attributes, but query expects 2.
|
|
drop function rngfuncbar();
|
|
-- check whole-row-Var handling in nested lateral functions (bug #11703)
|
|
create function extractq2(t int8_tbl) returns int8 as $$
|
|
select t.q2
|
|
$$ language sql immutable;
|
|
explain (verbose, costs off)
|
|
select x from int8_tbl, extractq2(int8_tbl) f(x);
|
|
QUERY PLAN
|
|
------------------------------------------
|
|
Nested Loop
|
|
Output: f.x
|
|
-> Seq Scan on public.int8_tbl
|
|
Output: int8_tbl.q1, int8_tbl.q2
|
|
-> Function Scan on f
|
|
Output: f.x
|
|
Function Call: int8_tbl.q2
|
|
(7 rows)
|
|
|
|
select x from int8_tbl, extractq2(int8_tbl) f(x);
|
|
x
|
|
-------------------
|
|
456
|
|
4567890123456789
|
|
123
|
|
4567890123456789
|
|
-4567890123456789
|
|
(5 rows)
|
|
|
|
create function extractq2_2(t int8_tbl) returns table(ret1 int8) as $$
|
|
select extractq2(t) offset 0
|
|
$$ language sql immutable;
|
|
explain (verbose, costs off)
|
|
select x from int8_tbl, extractq2_2(int8_tbl) f(x);
|
|
QUERY PLAN
|
|
-----------------------------------
|
|
Nested Loop
|
|
Output: ((int8_tbl.*).q2)
|
|
-> Seq Scan on public.int8_tbl
|
|
Output: int8_tbl.*
|
|
-> Result
|
|
Output: (int8_tbl.*).q2
|
|
(6 rows)
|
|
|
|
select x from int8_tbl, extractq2_2(int8_tbl) f(x);
|
|
x
|
|
-------------------
|
|
456
|
|
4567890123456789
|
|
123
|
|
4567890123456789
|
|
-4567890123456789
|
|
(5 rows)
|
|
|
|
-- without the "offset 0", this function gets optimized quite differently
|
|
create function extractq2_2_opt(t int8_tbl) returns table(ret1 int8) as $$
|
|
select extractq2(t)
|
|
$$ language sql immutable;
|
|
explain (verbose, costs off)
|
|
select x from int8_tbl, extractq2_2_opt(int8_tbl) f(x);
|
|
QUERY PLAN
|
|
-----------------------------
|
|
Seq Scan on public.int8_tbl
|
|
Output: int8_tbl.q2
|
|
(2 rows)
|
|
|
|
select x from int8_tbl, extractq2_2_opt(int8_tbl) f(x);
|
|
x
|
|
-------------------
|
|
456
|
|
4567890123456789
|
|
123
|
|
4567890123456789
|
|
-4567890123456789
|
|
(5 rows)
|
|
|
|
-- check handling of nulls in SRF results (bug #7808)
|
|
create type rngfunc2 as (a integer, b text);
|
|
select *, row_to_json(u) from unnest(array[(1,'foo')::rngfunc2, null::rngfunc2]) u;
|
|
a | b | row_to_json
|
|
---+-----+---------------------
|
|
1 | foo | {"a":1,"b":"foo"}
|
|
| | {"a":null,"b":null}
|
|
(2 rows)
|
|
|
|
select *, row_to_json(u) from unnest(array[null::rngfunc2, null::rngfunc2]) u;
|
|
a | b | row_to_json
|
|
---+---+---------------------
|
|
| | {"a":null,"b":null}
|
|
| | {"a":null,"b":null}
|
|
(2 rows)
|
|
|
|
select *, row_to_json(u) from unnest(array[null::rngfunc2, (1,'foo')::rngfunc2, null::rngfunc2]) u;
|
|
a | b | row_to_json
|
|
---+-----+---------------------
|
|
| | {"a":null,"b":null}
|
|
1 | foo | {"a":1,"b":"foo"}
|
|
| | {"a":null,"b":null}
|
|
(3 rows)
|
|
|
|
select *, row_to_json(u) from unnest(array[]::rngfunc2[]) u;
|
|
a | b | row_to_json
|
|
---+---+-------------
|
|
(0 rows)
|
|
|
|
drop type rngfunc2;
|
|
-- check handling of functions pulled up into function RTEs (bug #17227)
|
|
explain (verbose, costs off)
|
|
select * from
|
|
(select jsonb_path_query_array(module->'lectures', '$[*]') as lecture
|
|
from unnest(array['{"lectures": [{"id": "1"}]}'::jsonb])
|
|
as unnested_modules(module)) as ss,
|
|
jsonb_to_recordset(ss.lecture) as j (id text);
|
|
QUERY PLAN
|
|
--------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
Nested Loop
|
|
Output: jsonb_path_query_array((unnested_modules.module -> 'lectures'::text), '$[*]'::jsonpath, '{}'::jsonb, false), j.id
|
|
-> Function Scan on pg_catalog.unnest unnested_modules
|
|
Output: unnested_modules.module
|
|
Function Call: unnest('{"{\"lectures\": [{\"id\": \"1\"}]}"}'::jsonb[])
|
|
-> Function Scan on pg_catalog.jsonb_to_recordset j
|
|
Output: j.id
|
|
Function Call: jsonb_to_recordset(jsonb_path_query_array((unnested_modules.module -> 'lectures'::text), '$[*]'::jsonpath, '{}'::jsonb, false))
|
|
(8 rows)
|
|
|
|
select * from
|
|
(select jsonb_path_query_array(module->'lectures', '$[*]') as lecture
|
|
from unnest(array['{"lectures": [{"id": "1"}]}'::jsonb])
|
|
as unnested_modules(module)) as ss,
|
|
jsonb_to_recordset(ss.lecture) as j (id text);
|
|
lecture | id
|
|
---------------+----
|
|
[{"id": "1"}] | 1
|
|
(1 row)
|
|
|