mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-07 11:29:25 +02:00
We can't handle this in the general case due to limitations of the planner's data representations; but we can allow it in many useful cases, by being careful to flatten only when we are pulling a single-row subquery up into a FROM (or, equivalently, inner JOIN) node that will still have at least one remaining relation child. Per discussion of an example from Kyotaro Horiguchi.
2083 lines
53 KiB
Plaintext
2083 lines
53 KiB
Plaintext
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
|
|
name | setting
|
|
----------------------+---------
|
|
enable_bitmapscan | on
|
|
enable_hashagg | on
|
|
enable_hashjoin | on
|
|
enable_indexonlyscan | on
|
|
enable_indexscan | on
|
|
enable_material | on
|
|
enable_mergejoin | on
|
|
enable_nestloop | on
|
|
enable_seqscan | on
|
|
enable_sort | on
|
|
enable_tidscan | on
|
|
(11 rows)
|
|
|
|
CREATE TABLE foo2(fooid int, f2 int);
|
|
INSERT INTO foo2 VALUES(1, 11);
|
|
INSERT INTO foo2 VALUES(2, 22);
|
|
INSERT INTO foo2 VALUES(1, 111);
|
|
CREATE FUNCTION foot(int) returns setof foo2 as 'SELECT * FROM foo2 WHERE fooid = $1 ORDER BY f2;' LANGUAGE SQL;
|
|
-- function with ORDINALITY
|
|
select * from foot(1) with ordinality as z(a,b,ord);
|
|
a | b | ord
|
|
---+-----+-----
|
|
1 | 11 | 1
|
|
1 | 111 | 2
|
|
(2 rows)
|
|
|
|
select * from foot(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 foot(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 foot(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 foot(1) WITH ORDINALITY z(a, b, ord) ON ((v.n = z.ord)));
|
|
(1 row)
|
|
|
|
drop view vw_ord;
|
|
-- multiple functions
|
|
select * from rows from(foot(1),foot(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(foot(1),foot(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(foot(1), foot(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 foo 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 foo;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
2 | 2 | 2
|
|
3 | | 3
|
|
4 | | 4
|
|
5 | | 5
|
|
(5 rows)
|
|
|
|
fetch backward all from foo;
|
|
i | j | o
|
|
---+---+---
|
|
5 | | 5
|
|
4 | | 4
|
|
3 | | 3
|
|
2 | 2 | 2
|
|
1 | 1 | 1
|
|
(5 rows)
|
|
|
|
fetch all from foo;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
2 | 2 | 2
|
|
3 | | 3
|
|
4 | | 4
|
|
5 | | 5
|
|
(5 rows)
|
|
|
|
fetch next from foo;
|
|
i | j | o
|
|
---+---+---
|
|
(0 rows)
|
|
|
|
fetch next from foo;
|
|
i | j | o
|
|
---+---+---
|
|
(0 rows)
|
|
|
|
fetch prior from foo;
|
|
i | j | o
|
|
---+---+---
|
|
5 | | 5
|
|
(1 row)
|
|
|
|
fetch absolute 1 from foo;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
(1 row)
|
|
|
|
fetch next from foo;
|
|
i | j | o
|
|
---+---+---
|
|
2 | 2 | 2
|
|
(1 row)
|
|
|
|
fetch next from foo;
|
|
i | j | o
|
|
---+---+---
|
|
3 | | 3
|
|
(1 row)
|
|
|
|
fetch next from foo;
|
|
i | j | o
|
|
---+---+---
|
|
4 | | 4
|
|
(1 row)
|
|
|
|
fetch prior from foo;
|
|
i | j | o
|
|
---+---+---
|
|
3 | | 3
|
|
(1 row)
|
|
|
|
fetch prior from foo;
|
|
i | j | o
|
|
---+---+---
|
|
2 | 2 | 2
|
|
(1 row)
|
|
|
|
fetch prior from foo;
|
|
i | j | o
|
|
---+---+---
|
|
1 | 1 | 1
|
|
(1 row)
|
|
|
|
commit;
|
|
-- function with implicit LATERAL
|
|
select * from foo2, foot(foo2.fooid) z where foo2.f2 = z.f2;
|
|
fooid | f2 | fooid | f2
|
|
-------+-----+-------+-----
|
|
1 | 11 | 1 | 11
|
|
2 | 22 | 2 | 22
|
|
1 | 111 | 1 | 111
|
|
(3 rows)
|
|
|
|
-- function with implicit LATERAL and explicit ORDINALITY
|
|
select * from foo2, foot(foo2.fooid) with ordinality as z(fooid,f2,ord) where foo2.f2 = z.f2;
|
|
fooid | f2 | fooid | f2 | ord
|
|
-------+-----+-------+-----+-----
|
|
1 | 11 | 1 | 11 | 1
|
|
2 | 22 | 2 | 22 | 1
|
|
1 | 111 | 1 | 111 | 2
|
|
(3 rows)
|
|
|
|
-- function in subselect
|
|
select * from foo2 where f2 in (select f2 from foot(foo2.fooid) z where z.fooid = foo2.fooid) ORDER BY 1,2;
|
|
fooid | f2
|
|
-------+-----
|
|
1 | 11
|
|
1 | 111
|
|
2 | 22
|
|
(3 rows)
|
|
|
|
-- function in subselect
|
|
select * from foo2 where f2 in (select f2 from foot(1) z where z.fooid = foo2.fooid) ORDER BY 1,2;
|
|
fooid | f2
|
|
-------+-----
|
|
1 | 11
|
|
1 | 111
|
|
(2 rows)
|
|
|
|
-- function in subselect
|
|
select * from foo2 where f2 in (select f2 from foot(foo2.fooid) z where z.fooid = 1) ORDER BY 1,2;
|
|
fooid | f2
|
|
-------+-----
|
|
1 | 11
|
|
1 | 111
|
|
(2 rows)
|
|
|
|
-- nested functions
|
|
select foot.fooid, foot.f2 from foot(sin(pi()/2)::int) ORDER BY 1,2;
|
|
fooid | f2
|
|
-------+-----
|
|
1 | 11
|
|
1 | 111
|
|
(2 rows)
|
|
|
|
CREATE TABLE foo (fooid int, foosubid int, fooname text, primary key(fooid,foosubid));
|
|
INSERT INTO foo VALUES(1,1,'Joe');
|
|
INSERT INTO foo VALUES(1,2,'Ed');
|
|
INSERT INTO foo VALUES(2,1,'Mary');
|
|
-- sql, proretset = f, prorettype = b
|
|
CREATE FUNCTION getfoo1(int) RETURNS int AS 'SELECT $1;' LANGUAGE SQL;
|
|
SELECT * FROM getfoo1(1) AS t1;
|
|
t1
|
|
----
|
|
1
|
|
(1 row)
|
|
|
|
SELECT * FROM getfoo1(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo1(1);
|
|
SELECT * FROM vw_getfoo;
|
|
getfoo1
|
|
---------
|
|
1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo1(1) WITH ORDINALITY as t1(v,o);
|
|
SELECT * FROM vw_getfoo;
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- sql, proretset = t, prorettype = b
|
|
CREATE FUNCTION getfoo2(int) RETURNS setof int AS 'SELECT fooid FROM foo WHERE fooid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getfoo2(1) AS t1;
|
|
t1
|
|
----
|
|
1
|
|
1
|
|
(2 rows)
|
|
|
|
SELECT * FROM getfoo2(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
1 | 2
|
|
(2 rows)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo2(1);
|
|
SELECT * FROM vw_getfoo;
|
|
getfoo2
|
|
---------
|
|
1
|
|
1
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo2(1) WITH ORDINALITY AS t1(v,o);
|
|
SELECT * FROM vw_getfoo;
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
1 | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- sql, proretset = t, prorettype = b
|
|
CREATE FUNCTION getfoo3(int) RETURNS setof text AS 'SELECT fooname FROM foo WHERE fooid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getfoo3(1) AS t1;
|
|
t1
|
|
-----
|
|
Joe
|
|
Ed
|
|
(2 rows)
|
|
|
|
SELECT * FROM getfoo3(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
-----+---
|
|
Joe | 1
|
|
Ed | 2
|
|
(2 rows)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo3(1);
|
|
SELECT * FROM vw_getfoo;
|
|
getfoo3
|
|
---------
|
|
Joe
|
|
Ed
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo3(1) WITH ORDINALITY AS t1(v,o);
|
|
SELECT * FROM vw_getfoo;
|
|
v | o
|
|
-----+---
|
|
Joe | 1
|
|
Ed | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- sql, proretset = f, prorettype = c
|
|
CREATE FUNCTION getfoo4(int) RETURNS foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getfoo4(1) AS t1;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
SELECT * FROM getfoo4(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo4(1);
|
|
SELECT * FROM vw_getfoo;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo4(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
SELECT * FROM vw_getfoo;
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- sql, proretset = t, prorettype = c
|
|
CREATE FUNCTION getfoo5(int) RETURNS setof foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getfoo5(1) AS t1;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
SELECT * FROM getfoo5(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_getfoo AS SELECT * FROM getfoo5(1);
|
|
SELECT * FROM vw_getfoo;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo5(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
SELECT * FROM vw_getfoo;
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
1 | 2 | Ed | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- sql, proretset = f, prorettype = record
|
|
CREATE FUNCTION getfoo6(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getfoo6(1) AS t1(fooid int, foosubid int, fooname text);
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
SELECT * FROM ROWS FROM( getfoo6(1) AS (fooid int, foosubid int, fooname text) ) WITH ORDINALITY;
|
|
fooid | foosubid | fooname | ordinality
|
|
-------+----------+---------+------------
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo6(1) AS
|
|
(fooid int, foosubid int, fooname text);
|
|
SELECT * FROM vw_getfoo;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS
|
|
SELECT * FROM ROWS FROM( getfoo6(1) AS (fooid int, foosubid int, fooname text) )
|
|
WITH ORDINALITY;
|
|
SELECT * FROM vw_getfoo;
|
|
fooid | foosubid | fooname | ordinality
|
|
-------+----------+---------+------------
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- sql, proretset = t, prorettype = record
|
|
CREATE FUNCTION getfoo7(int) RETURNS setof record AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
|
|
SELECT * FROM getfoo7(1) AS t1(fooid int, foosubid int, fooname text);
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
SELECT * FROM ROWS FROM( getfoo7(1) AS (fooid int, foosubid int, fooname text) ) WITH ORDINALITY;
|
|
fooid | foosubid | fooname | ordinality
|
|
-------+----------+---------+------------
|
|
1 | 1 | Joe | 1
|
|
1 | 2 | Ed | 2
|
|
(2 rows)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo7(1) AS
|
|
(fooid int, foosubid int, fooname text);
|
|
SELECT * FROM vw_getfoo;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
1 | 2 | Ed
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS
|
|
SELECT * FROM ROWS FROM( getfoo7(1) AS (fooid int, foosubid int, fooname text) )
|
|
WITH ORDINALITY;
|
|
SELECT * FROM vw_getfoo;
|
|
fooid | foosubid | fooname | ordinality
|
|
-------+----------+---------+------------
|
|
1 | 1 | Joe | 1
|
|
1 | 2 | Ed | 2
|
|
(2 rows)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- plpgsql, proretset = f, prorettype = b
|
|
CREATE FUNCTION getfoo8(int) RETURNS int AS 'DECLARE fooint int; BEGIN SELECT fooid into fooint FROM foo WHERE fooid = $1; RETURN fooint; END;' LANGUAGE plpgsql;
|
|
SELECT * FROM getfoo8(1) AS t1;
|
|
t1
|
|
----
|
|
1
|
|
(1 row)
|
|
|
|
SELECT * FROM getfoo8(1) WITH ORDINALITY AS t1(v,o);
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo8(1);
|
|
SELECT * FROM vw_getfoo;
|
|
getfoo8
|
|
---------
|
|
1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo8(1) WITH ORDINALITY AS t1(v,o);
|
|
SELECT * FROM vw_getfoo;
|
|
v | o
|
|
---+---
|
|
1 | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- plpgsql, proretset = f, prorettype = c
|
|
CREATE FUNCTION getfoo9(int) RETURNS foo AS 'DECLARE footup foo%ROWTYPE; BEGIN SELECT * into footup FROM foo WHERE fooid = $1; RETURN footup; END;' LANGUAGE plpgsql;
|
|
SELECT * FROM getfoo9(1) AS t1;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
SELECT * FROM getfoo9(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo9(1);
|
|
SELECT * FROM vw_getfoo;
|
|
fooid | foosubid | fooname
|
|
-------+----------+---------
|
|
1 | 1 | Joe
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo9(1) WITH ORDINALITY AS t1(a,b,c,o);
|
|
SELECT * FROM vw_getfoo;
|
|
a | b | c | o
|
|
---+---+-----+---
|
|
1 | 1 | Joe | 1
|
|
(1 row)
|
|
|
|
DROP VIEW vw_getfoo;
|
|
-- mix 'n match kinds, to exercise expandRTE and related logic
|
|
select * from rows from(getfoo1(1),getfoo2(1),getfoo3(1),getfoo4(1),getfoo5(1),
|
|
getfoo6(1) AS (fooid int, foosubid int, fooname text),
|
|
getfoo7(1) AS (fooid int, foosubid int, fooname text),
|
|
getfoo8(1),getfoo9(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(getfoo9(1),getfoo8(1),
|
|
getfoo7(1) AS (fooid int, foosubid int, fooname text),
|
|
getfoo6(1) AS (fooid int, foosubid int, fooname text),
|
|
getfoo5(1),getfoo4(1),getfoo3(1),getfoo2(1),getfoo1(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_foo as
|
|
select * from rows from(getfoo9(1),
|
|
getfoo7(1) AS (fooid int, foosubid int, fooname text),
|
|
getfoo1(1))
|
|
with ordinality as t1(a,b,c,d,e,f,g,n);
|
|
select * from vw_foo;
|
|
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_foo');
|
|
pg_get_viewdef
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
SELECT t1.a, +
|
|
t1.b, +
|
|
t1.c, +
|
|
t1.d, +
|
|
t1.e, +
|
|
t1.f, +
|
|
t1.g, +
|
|
t1.n +
|
|
FROM ROWS FROM(getfoo9(1), getfoo7(1) AS (fooid integer, foosubid integer, fooname text), getfoo1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n);
|
|
(1 row)
|
|
|
|
drop view vw_foo;
|
|
DROP FUNCTION getfoo1(int);
|
|
DROP FUNCTION getfoo2(int);
|
|
DROP FUNCTION getfoo3(int);
|
|
DROP FUNCTION getfoo4(int);
|
|
DROP FUNCTION getfoo5(int);
|
|
DROP FUNCTION getfoo6(int);
|
|
DROP FUNCTION getfoo7(int);
|
|
DROP FUNCTION getfoo8(int);
|
|
DROP FUNCTION getfoo9(int);
|
|
DROP FUNCTION foot(int);
|
|
DROP TABLE foo2;
|
|
DROP TABLE foo;
|
|
-- Rescan tests --
|
|
CREATE TEMPORARY SEQUENCE foo_rescan_seq1;
|
|
CREATE TEMPORARY SEQUENCE foo_rescan_seq2;
|
|
CREATE TYPE foo_rescan_t AS (i integer, s bigint);
|
|
CREATE FUNCTION foo_sql(int,int) RETURNS setof foo_rescan_t AS 'SELECT i, nextval(''foo_rescan_seq1'') FROM generate_series($1,$2) i;' LANGUAGE SQL;
|
|
-- plpgsql functions use materialize mode
|
|
CREATE FUNCTION foo_mat(int,int) RETURNS setof foo_rescan_t AS 'begin for i in $1..$2 loop return next (i, nextval(''foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( foo_sql(11,13), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_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('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
|
|
setval | setval
|
|
--------+--------
|
|
1 | 1
|
|
(1 row)
|
|
|
|
SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( foo_sql(10+r1,13), foo_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)
|
|
|
|
DROP FUNCTION foo_sql(int,int);
|
|
DROP FUNCTION foo_mat(int,int);
|
|
DROP SEQUENCE foo_rescan_seq1;
|
|
DROP SEQUENCE foo_rescan_seq2;
|
|
--
|
|
-- Test cases involving OUT parameters
|
|
--
|
|
CREATE FUNCTION foo(in f1 int, out f2 int)
|
|
AS 'select $1+1' LANGUAGE sql;
|
|
SELECT foo(42);
|
|
foo
|
|
-----
|
|
43
|
|
(1 row)
|
|
|
|
SELECT * FROM foo(42);
|
|
f2
|
|
----
|
|
43
|
|
(1 row)
|
|
|
|
SELECT * FROM foo(42) AS p(x);
|
|
x
|
|
----
|
|
43
|
|
(1 row)
|
|
|
|
-- explicit spec of return type is OK
|
|
CREATE OR REPLACE FUNCTION foo(in f1 int, out f2 int) RETURNS int
|
|
AS 'select $1+1' LANGUAGE sql;
|
|
-- error, wrong result type
|
|
CREATE OR REPLACE FUNCTION foo(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 foo(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 foo(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 foo(integer) first.
|
|
CREATE OR REPLACE FUNCTION foor(in f1 int, out f2 int, out text)
|
|
AS $$select $1-1, $1::text || 'z'$$ LANGUAGE sql;
|
|
SELECT f1, foor(f1) FROM int4_tbl;
|
|
f1 | foor
|
|
-------------+----------------------------
|
|
0 | (-1,0z)
|
|
123456 | (123455,123456z)
|
|
-123456 | (-123457,-123456z)
|
|
2147483647 | (2147483646,2147483647z)
|
|
-2147483647 | (-2147483648,-2147483647z)
|
|
(5 rows)
|
|
|
|
SELECT * FROM foor(42);
|
|
f2 | column2
|
|
----+---------
|
|
41 | 42z
|
|
(1 row)
|
|
|
|
SELECT * FROM foor(42) AS p(a,b);
|
|
a | b
|
|
----+-----
|
|
41 | 42z
|
|
(1 row)
|
|
|
|
CREATE OR REPLACE FUNCTION foob(in f1 int, inout f2 int, out text)
|
|
AS $$select $2-1, $1::text || 'z'$$ LANGUAGE sql;
|
|
SELECT f1, foob(f1, f1/2) FROM int4_tbl;
|
|
f1 | foob
|
|
-------------+----------------------------
|
|
0 | (-1,0z)
|
|
123456 | (61727,123456z)
|
|
-123456 | (-61729,-123456z)
|
|
2147483647 | (1073741822,2147483647z)
|
|
-2147483647 | (-1073741824,-2147483647z)
|
|
(5 rows)
|
|
|
|
SELECT * FROM foob(42, 99);
|
|
f2 | column2
|
|
----+---------
|
|
98 | 42z
|
|
(1 row)
|
|
|
|
SELECT * FROM foob(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 foo(int);
|
|
DROP FUNCTION foor(in f2 int, out f1 int, out text);
|
|
DROP FUNCTION foob(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 foo()
|
|
RETURNS TABLE(a int)
|
|
AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql;
|
|
SELECT * FROM foo();
|
|
a
|
|
---
|
|
1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
(5 rows)
|
|
|
|
DROP FUNCTION foo();
|
|
CREATE OR REPLACE FUNCTION foo(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 foo(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 foo(int);
|
|
-- case that causes change of typmod knowledge during inlining
|
|
CREATE OR REPLACE FUNCTION foo()
|
|
RETURNS TABLE(a varchar(5))
|
|
AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE;
|
|
SELECT * FROM foo() GROUP BY 1;
|
|
a
|
|
-------
|
|
hello
|
|
(1 row)
|
|
|
|
DROP FUNCTION foo();
|
|
--
|
|
-- 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
|
|
CONTEXT: SQL function "insert_tt2" statement 1
|
|
NOTICE: noticetrigger 12 barme
|
|
CONTEXT: SQL function "insert_tt2" statement 1
|
|
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
|
|
CONTEXT: SQL function "insert_tt2" statement 1
|
|
NOTICE: noticetrigger 14 barlog
|
|
CONTEXT: SQL function "insert_tt2" statement 1
|
|
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 foo1(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 foo1(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 foo1(10000) t limit 1;
|
|
a | t | a
|
|
-------+-------------------+-------
|
|
foo 1 | ("foo 1","bar 1") | foo 1
|
|
(1 row)
|
|
|
|
drop function foo1(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']);
|
|
^
|
|
create temp table foo(f1 int8, f2 int8);
|
|
create function testfoo() returns record as $$
|
|
insert into foo values (1,2) returning *;
|
|
$$ language sql;
|
|
select testfoo();
|
|
testfoo
|
|
---------
|
|
(1,2)
|
|
(1 row)
|
|
|
|
select * from testfoo() as t(f1 int8,f2 int8);
|
|
f1 | f2
|
|
----+----
|
|
1 | 2
|
|
(1 row)
|
|
|
|
select * from testfoo(); -- fail
|
|
ERROR: a column definition list is required for functions returning "record"
|
|
LINE 1: select * from testfoo();
|
|
^
|
|
drop function testfoo();
|
|
create function testfoo() returns setof record as $$
|
|
insert into foo values (1,2), (3,4) returning *;
|
|
$$ language sql;
|
|
select testfoo();
|
|
testfoo
|
|
---------
|
|
(1,2)
|
|
(3,4)
|
|
(2 rows)
|
|
|
|
select * from testfoo() as t(f1 int8,f2 int8);
|
|
f1 | f2
|
|
----+----
|
|
1 | 2
|
|
3 | 4
|
|
(2 rows)
|
|
|
|
select * from testfoo(); -- fail
|
|
ERROR: a column definition list is required for functions returning "record"
|
|
LINE 1: select * from testfoo();
|
|
^
|
|
drop function testfoo();
|
|
--
|
|
-- 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 drop column moredrop;
|
|
select * from usersview;
|
|
userid | seq | email | moredrop | enabled | generate_series | ordinality
|
|
--------+-----+--------+----------+---------+-----------------+------------
|
|
id | 1 | email | | t | 10 | 1
|
|
id2 | 2 | email2 | | 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 | | t | 10 | 1
|
|
id2 | 2 | email2 | | t | 11 | 2
|
|
(2 rows)
|
|
|
|
alter table users alter column seq type numeric;
|
|
select * from usersview; -- expect clean failure
|
|
ERROR: attribute 2 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;
|
|
-- this won't get inlined because of type coercion, but it shouldn't fail
|
|
create or replace function foobar() returns setof text as
|
|
$$ select 'foo'::varchar union all select 'bar'::varchar ; $$
|
|
language sql stable;
|
|
select foobar();
|
|
foobar
|
|
--------
|
|
foo
|
|
bar
|
|
(2 rows)
|
|
|
|
select * from foobar();
|
|
foobar
|
|
--------
|
|
foo
|
|
bar
|
|
(2 rows)
|
|
|
|
drop function foobar();
|
|
-- check handling of a SQL function with multiple OUT params (bug #5777)
|
|
create or replace function foobar(out integer, out numeric) as
|
|
$$ select (1, 2.1) $$ language sql;
|
|
select * from foobar();
|
|
column1 | column2
|
|
---------+---------
|
|
1 | 2.1
|
|
(1 row)
|
|
|
|
create or replace function foobar(out integer, out numeric) as
|
|
$$ select (1, 2) $$ language sql;
|
|
select * from foobar(); -- 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 foobar(out integer, out numeric) as
|
|
$$ select (1, 2.1, 3) $$ language sql;
|
|
select * from foobar(); -- 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 foobar();
|
|
-- check behavior when a function's input sometimes returns a set (bug #8228)
|
|
SELECT *,
|
|
lower(CASE WHEN id = 2 THEN (regexp_matches(str, '^0*([1-9]\d+)$'))[1]
|
|
ELSE str
|
|
END)
|
|
FROM
|
|
(VALUES (1,''), (2,'0000000049404'), (3,'FROM 10000000876')) v(id, str);
|
|
id | str | lower
|
|
----+------------------+------------------
|
|
1 | |
|
|
2 | 0000000049404 | 49404
|
|
3 | FROM 10000000876 | from 10000000876
|
|
(3 rows)
|
|
|
|
-- 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)
|
|
|