mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-08 08:46:56 +02:00
913bbd88dc
Use the parser's standard type coercion machinery to convert the output column(s) of a SQL function's final SELECT or RETURNING to the type(s) they should have according to the function's declared result type. We'll allow any case where an assignment-level coercion is available. Previously, we failed unless the required coercion was a binary-compatible one (and the documentation ignored this, falsely claiming that the types must match exactly). Notably, the coercion now accounts for typmods, so that cases where a SQL function is declared to return a composite type whose columns are typmod-constrained now behave as one would expect. Arguably this aspect is a bug fix, but the overall behavioral change here seems too large to consider back-patching. A nice side-effect is that functions can now be inlined in a few cases where we previously failed to do so because of type mismatches. Discussion: https://postgr.es/m/18929.1574895430@sss.pgh.pa.us
2309 lines
62 KiB
Plaintext
2309 lines
62 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 function returning a polymorphic type must have at least one polymorphic argument.
|
|
--
|
|
-- 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)
|
|
|
|
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;
|