postgresql/src/test/regress/expected/rowtypes.out

660 lines
18 KiB
Plaintext
Raw Normal View History

--
-- ROWTYPES
--
-- Make both a standalone composite type and a table rowtype
create type complex as (r float8, i float8);
create temp table fullname (first text, last text);
-- Nested composite
create type quad as (c1 complex, c2 complex);
-- Some simple tests of I/O conversions and row construction
select (1.1,2.2)::complex, row((3.3,4.4),(5.5,null))::quad;
row | row
-----------+------------------------
(1.1,2.2) | ("(3.3,4.4)","(5.5,)")
(1 row)
select row('Joe', 'Blow')::fullname, '(Joe,Blow)'::fullname;
row | fullname
------------+------------
(Joe,Blow) | (Joe,Blow)
(1 row)
select '(Joe,von Blow)'::fullname, '(Joe,d''Blow)'::fullname;
fullname | fullname
------------------+--------------
(Joe,"von Blow") | (Joe,d'Blow)
(1 row)
select '(Joe,"von""Blow")'::fullname, E'(Joe,d\\\\Blow)'::fullname;
fullname | fullname
-------------------+-----------------
(Joe,"von""Blow") | (Joe,"d\\Blow")
(1 row)
select '(Joe,"Blow,Jr")'::fullname;
fullname
-----------------
(Joe,"Blow,Jr")
(1 row)
select '(Joe,)'::fullname; -- ok, null 2nd column
fullname
----------
(Joe,)
(1 row)
select '(Joe)'::fullname; -- bad
ERROR: malformed record literal: "(Joe)"
LINE 1: select '(Joe)'::fullname;
^
DETAIL: Too few columns.
select '(Joe,,)'::fullname; -- bad
ERROR: malformed record literal: "(Joe,,)"
LINE 1: select '(Joe,,)'::fullname;
^
DETAIL: Too many columns.
create temp table quadtable(f1 int, q quad);
insert into quadtable values (1, ((3.3,4.4),(5.5,6.6)));
insert into quadtable values (2, ((null,4.4),(5.5,6.6)));
select * from quadtable;
f1 | q
----+---------------------------
1 | ("(3.3,4.4)","(5.5,6.6)")
2 | ("(,4.4)","(5.5,6.6)")
(2 rows)
select f1, q.c1 from quadtable; -- fails, q is a table reference
ERROR: missing FROM-clause entry for table "q"
LINE 1: select f1, q.c1 from quadtable;
^
select f1, (q).c1, (qq.q).c1.i from quadtable qq;
f1 | c1 | i
----+-----------+-----
1 | (3.3,4.4) | 4.4
2 | (,4.4) | 4.4
(2 rows)
create temp table people (fn fullname, bd date);
insert into people values ('(Joe,Blow)', '1984-01-10');
select * from people;
fn | bd
------------+------------
(Joe,Blow) | 01-10-1984
(1 row)
-- at the moment this will not work due to ALTER TABLE inadequacy:
alter table fullname add column suffix text default '';
ERROR: cannot alter table "fullname" because column "people.fn" uses its row type
-- but this should work:
alter table fullname add column suffix text default null;
select * from people;
fn | bd
-------------+------------
(Joe,Blow,) | 01-10-1984
(1 row)
-- test insertion/updating of subfields
update people set fn.suffix = 'Jr';
select * from people;
fn | bd
---------------+------------
(Joe,Blow,Jr) | 01-10-1984
(1 row)
insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66);
select * from quadtable;
f1 | q
----+---------------------------
1 | ("(3.3,4.4)","(5.5,6.6)")
2 | ("(,4.4)","(5.5,6.6)")
44 | ("(55,)","(,66)")
(3 rows)
-- The object here is to ensure that toasted references inside
-- composite values don't cause problems. The large f1 value will
-- be toasted inside pp, it must still work after being copied to people.
create temp table pp (f1 text);
insert into pp values (repeat('abcdefghijkl', 100000));
insert into people select ('Jim', f1, null)::fullname, current_date from pp;
select (fn).first, substr((fn).last, 1, 20), length((fn).last) from people;
first | substr | length
-------+----------------------+---------
Joe | Blow | 4
Jim | abcdefghijklabcdefgh | 1200000
(2 rows)
-- Test row comparison semantics. Prior to PG 8.2 we did this in a totally
-- non-spec-compliant way.
select ROW(1,2) < ROW(1,3) as true;
true
------
t
(1 row)
select ROW(1,2) < ROW(1,1) as false;
false
-------
f
(1 row)
select ROW(1,2) < ROW(1,NULL) as null;
null
------
(1 row)
select ROW(1,2,3) < ROW(1,3,NULL) as true; -- the NULL is not examined
true
------
t
(1 row)
select ROW(11,'ABC') < ROW(11,'DEF') as true;
true
------
t
(1 row)
select ROW(11,'ABC') > ROW(11,'DEF') as false;
false
-------
f
(1 row)
select ROW(12,'ABC') > ROW(11,'DEF') as true;
true
------
t
(1 row)
-- = and <> have different NULL-behavior than < etc
select ROW(1,2,3) < ROW(1,NULL,4) as null;
null
------
(1 row)
select ROW(1,2,3) = ROW(1,NULL,4) as false;
false
-------
f
(1 row)
select ROW(1,2,3) <> ROW(1,NULL,4) as true;
true
------
t
(1 row)
-- We allow operators beyond the six standard ones, if they have btree
-- operator classes.
select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true;
true
------
t
(1 row)
select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false;
false
-------
f
(1 row)
select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail;
ERROR: could not determine interpretation of row comparison operator ~~
LINE 1: select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail;
^
HINT: Row comparison operators must be associated with btree operator families.
-- Comparisons of ROW() expressions can cope with some type mismatches
select ROW(1,2) = ROW(1,2::int8);
?column?
----------
t
(1 row)
select ROW(1,2) in (ROW(3,4), ROW(1,2));
?column?
----------
t
(1 row)
select ROW(1,2) in (ROW(3,4), ROW(1,2::int8));
?column?
----------
t
(1 row)
-- Check row comparison with a subselect
select unique1, unique2 from tenk1
where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3)
and unique1 <= 20
order by 1;
unique1 | unique2
---------+---------
0 | 9998
1 | 2838
(2 rows)
-- Also check row comparison with an indexable condition
explain (costs off)
select thousand, tenthous from tenk1
where (thousand, tenthous) >= (997, 5000)
order by thousand, tenthous;
QUERY PLAN
-----------------------------------------------------------
Index Only Scan using tenk1_thous_tenthous on tenk1
Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
(2 rows)
select thousand, tenthous from tenk1
where (thousand, tenthous) >= (997, 5000)
order by thousand, tenthous;
thousand | tenthous
----------+----------
997 | 5997
997 | 6997
997 | 7997
997 | 8997
997 | 9997
998 | 998
998 | 1998
998 | 2998
998 | 3998
998 | 4998
998 | 5998
998 | 6998
998 | 7998
998 | 8998
998 | 9998
999 | 999
999 | 1999
999 | 2999
999 | 3999
999 | 4999
999 | 5999
999 | 6999
999 | 7999
999 | 8999
999 | 9999
(25 rows)
-- Test case for bug #14010: indexed row comparisons fail with nulls
create temp table test_table (a text, b text);
insert into test_table values ('a', 'b');
insert into test_table select 'a', null from generate_series(1,1000);
insert into test_table values ('b', 'a');
create index on test_table (a,b);
set enable_sort = off;
explain (costs off)
select a,b from test_table where (a,b) > ('a','a') order by a,b;
QUERY PLAN
--------------------------------------------------------
Index Only Scan using test_table_a_b_idx on test_table
Index Cond: (ROW(a, b) > ROW('a'::text, 'a'::text))
(2 rows)
select a,b from test_table where (a,b) > ('a','a') order by a,b;
a | b
---+---
a | b
b | a
(2 rows)
reset enable_sort;
-- Check row comparisons with IN
select * from int8_tbl i8 where i8 in (row(123,456)); -- fail, type mismatch
ERROR: cannot compare dissimilar column types bigint and integer at record column 1
explain (costs off)
select * from int8_tbl i8
where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Seq Scan on int8_tbl i8
Filter: (i8.* = ANY (ARRAY[ROW('123'::bigint, '456'::bigint)::int8_tbl, '(4567890123456789,123)'::int8_tbl]))
(2 rows)
select * from int8_tbl i8
where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
q1 | q2
------------------+-----
123 | 456
4567890123456789 | 123
(2 rows)
-- Check some corner cases involving empty rowtypes
select ROW();
row
-----
()
(1 row)
select ROW() IS NULL;
?column?
----------
t
(1 row)
select ROW() = ROW();
ERROR: cannot compare rows of zero length
LINE 1: select ROW() = ROW();
^
-- Check ability to create arrays of anonymous rowtypes
select array[ row(1,2), row(3,4), row(5,6) ];
array
---------------------------
{"(1,2)","(3,4)","(5,6)"}
(1 row)
-- Check ability to compare an anonymous row to elements of an array
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]);
?column?
----------
t
(1 row)
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]);
?column?
----------
f
(1 row)
-- Check behavior with a non-comparable rowtype
create type cantcompare as (p point, r float8);
create temp table cc (f1 cantcompare);
insert into cc values('("(1,2)",3)');
insert into cc values('("(4,5)",6)');
select * from cc order by f1; -- fail, but should complain about cantcompare
ERROR: could not identify an ordering operator for type cantcompare
LINE 1: select * from cc order by f1;
^
HINT: Use an explicit ordering operator or modify the query.
--
-- Test case derived from bug #5716: check multiple uses of a rowtype result
--
BEGIN;
CREATE TABLE price (
id SERIAL PRIMARY KEY,
active BOOLEAN NOT NULL,
price NUMERIC
);
CREATE TYPE price_input AS (
id INTEGER,
price NUMERIC
);
CREATE TYPE price_key AS (
id INTEGER
);
CREATE FUNCTION price_key_from_table(price) RETURNS price_key AS $$
SELECT $1.id
$$ LANGUAGE SQL;
CREATE FUNCTION price_key_from_input(price_input) RETURNS price_key AS $$
SELECT $1.id
$$ LANGUAGE SQL;
insert into price values (1,false,42), (10,false,100), (11,true,17.99);
UPDATE price
SET active = true, price = input_prices.price
FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices
WHERE price_key_from_table(price.*) = price_key_from_input(input_prices.*);
select * from price;
id | active | price
----+--------+--------
1 | f | 42
10 | t | 123.00
11 | t | 99.99
(3 rows)
rollback;
--
-- Test case derived from bug #9085: check * qualification of composite
-- parameters for SQL functions
--
create temp table compos (f1 int, f2 text);
create function fcompos1(v compos) returns void as $$
insert into compos values (v); -- fail
$$ language sql;
ERROR: column "f1" is of type integer but expression is of type compos
LINE 2: insert into compos values (v); -- fail
^
HINT: You will need to rewrite or cast the expression.
create function fcompos1(v compos) returns void as $$
insert into compos values (v.*);
$$ language sql;
create function fcompos2(v compos) returns void as $$
select fcompos1(v);
$$ language sql;
create function fcompos3(v compos) returns void as $$
select fcompos1(fcompos3.v.*);
$$ language sql;
select fcompos1(row(1,'one'));
fcompos1
----------
(1 row)
select fcompos2(row(2,'two'));
fcompos2
----------
(1 row)
select fcompos3(row(3,'three'));
fcompos3
----------
(1 row)
select * from compos;
f1 | f2
----+-------
1 | one
2 | two
3 | three
(3 rows)
--
-- We allow I/O conversion casts from composite types to strings to be
-- invoked via cast syntax, but not functional syntax. This is because
-- the latter is too prone to be invoked unintentionally.
--
select cast (fullname as text) from fullname;
fullname
----------
(0 rows)
select fullname::text from fullname;
fullname
----------
(0 rows)
select text(fullname) from fullname; -- error
ERROR: function text(fullname) does not exist
LINE 1: select text(fullname) from fullname;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select fullname.text from fullname; -- error
ERROR: column fullname.text does not exist
LINE 1: select fullname.text from fullname;
^
-- same, but RECORD instead of named composite type:
select cast (row('Jim', 'Beam') as text);
row
------------
(Jim,Beam)
(1 row)
select (row('Jim', 'Beam'))::text;
row
------------
(Jim,Beam)
(1 row)
select text(row('Jim', 'Beam')); -- error
ERROR: function text(record) does not exist
LINE 1: select text(row('Jim', 'Beam'));
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select (row('Jim', 'Beam')).text; -- error
ERROR: could not identify column "text" in record data type
LINE 1: select (row('Jim', 'Beam')).text;
^
Ensure that RowExprs and whole-row Vars produce the expected column names. At one time it wasn't terribly important what column names were associated with the fields of a composite Datum, but since the introduction of operations like row_to_json(), it's important that looking up the rowtype ID embedded in the Datum returns the column names that users would expect. That did not work terribly well before this patch: you could get the column names of the underlying table, or column aliases from any level of the query, depending on minor details of the plan tree. You could even get totally empty field names, which is disastrous for cases like row_to_json(). To fix this for whole-row Vars, look to the RTE referenced by the Var, and make sure its column aliases are applied to the rowtype associated with the result Datums. This is a tad scary because we might have to return a transient RECORD type even though the Var is declared as having some named rowtype. In principle it should be all right because the record type will still be physically compatible with the named rowtype; but I had to weaken one Assert in ExecEvalConvertRowtype, and there might be third-party code containing similar assumptions. Similarly, RowExprs have to be willing to override the column names coming from a named composite result type and produce a RECORD when the column aliases visible at the site of the RowExpr differ from the underlying table's column names. In passing, revert the decision made in commit 398f70ec070fe601 to add an alias-list argument to ExecTypeFromExprList: better to provide that functionality in a separate function. This also reverts most of the code changes in d68581483564ec0f, which we don't need because we're no longer depending on the tupdesc found in the child plan node's result slot to be blessed. Back-patch to 9.4, but not earlier, since this solution changes the results in some cases that users might not have realized were buggy. We'll apply a more restricted form of this patch in older branches.
2014-11-10 21:21:09 +01:00
--
-- Test that composite values are seen to have the correct column names
-- (bug #11210 and other reports)
--
select row_to_json(i) from int8_tbl i;
row_to_json
------------------------------------------------
{"q1":123,"q2":456}
{"q1":123,"q2":4567890123456789}
{"q1":4567890123456789,"q2":123}
{"q1":4567890123456789,"q2":4567890123456789}
{"q1":4567890123456789,"q2":-4567890123456789}
(5 rows)
select row_to_json(i) from int8_tbl i(x,y);
row_to_json
----------------------------------------------
{"x":123,"y":456}
{"x":123,"y":4567890123456789}
{"x":4567890123456789,"y":123}
{"x":4567890123456789,"y":4567890123456789}
{"x":4567890123456789,"y":-4567890123456789}
(5 rows)
create temp view vv1 as select * from int8_tbl;
select row_to_json(i) from vv1 i;
row_to_json
------------------------------------------------
{"q1":123,"q2":456}
{"q1":123,"q2":4567890123456789}
{"q1":4567890123456789,"q2":123}
{"q1":4567890123456789,"q2":4567890123456789}
{"q1":4567890123456789,"q2":-4567890123456789}
(5 rows)
select row_to_json(i) from vv1 i(x,y);
row_to_json
----------------------------------------------
{"x":123,"y":456}
{"x":123,"y":4567890123456789}
{"x":4567890123456789,"y":123}
{"x":4567890123456789,"y":4567890123456789}
{"x":4567890123456789,"y":-4567890123456789}
(5 rows)
select row_to_json(ss) from
(select q1, q2 from int8_tbl) as ss;
row_to_json
------------------------------------------------
{"q1":123,"q2":456}
{"q1":123,"q2":4567890123456789}
{"q1":4567890123456789,"q2":123}
{"q1":4567890123456789,"q2":4567890123456789}
{"q1":4567890123456789,"q2":-4567890123456789}
(5 rows)
select row_to_json(ss) from
(select q1, q2 from int8_tbl offset 0) as ss;
row_to_json
------------------------------------------------
{"q1":123,"q2":456}
{"q1":123,"q2":4567890123456789}
{"q1":4567890123456789,"q2":123}
{"q1":4567890123456789,"q2":4567890123456789}
{"q1":4567890123456789,"q2":-4567890123456789}
(5 rows)
select row_to_json(ss) from
(select q1 as a, q2 as b from int8_tbl) as ss;
row_to_json
----------------------------------------------
{"a":123,"b":456}
{"a":123,"b":4567890123456789}
{"a":4567890123456789,"b":123}
{"a":4567890123456789,"b":4567890123456789}
{"a":4567890123456789,"b":-4567890123456789}
(5 rows)
select row_to_json(ss) from
(select q1 as a, q2 as b from int8_tbl offset 0) as ss;
row_to_json
----------------------------------------------
{"a":123,"b":456}
{"a":123,"b":4567890123456789}
{"a":4567890123456789,"b":123}
{"a":4567890123456789,"b":4567890123456789}
{"a":4567890123456789,"b":-4567890123456789}
(5 rows)
select row_to_json(ss) from
(select q1 as a, q2 as b from int8_tbl) as ss(x,y);
row_to_json
----------------------------------------------
{"x":123,"y":456}
{"x":123,"y":4567890123456789}
{"x":4567890123456789,"y":123}
{"x":4567890123456789,"y":4567890123456789}
{"x":4567890123456789,"y":-4567890123456789}
(5 rows)
select row_to_json(ss) from
(select q1 as a, q2 as b from int8_tbl offset 0) as ss(x,y);
row_to_json
----------------------------------------------
{"x":123,"y":456}
{"x":123,"y":4567890123456789}
{"x":4567890123456789,"y":123}
{"x":4567890123456789,"y":4567890123456789}
{"x":4567890123456789,"y":-4567890123456789}
(5 rows)
explain (costs off)
select row_to_json(q) from
(select thousand, tenthous from tenk1
where thousand = 42 and tenthous < 2000 offset 0) q;
QUERY PLAN
-------------------------------------------------------------
Subquery Scan on q
-> Index Only Scan using tenk1_thous_tenthous on tenk1
Index Cond: ((thousand = 42) AND (tenthous < 2000))
(3 rows)
select row_to_json(q) from
(select thousand, tenthous from tenk1
where thousand = 42 and tenthous < 2000 offset 0) q;
row_to_json
---------------------------------
{"thousand":42,"tenthous":42}
{"thousand":42,"tenthous":1042}
(2 rows)
select row_to_json(q) from
(select thousand as x, tenthous as y from tenk1
where thousand = 42 and tenthous < 2000 offset 0) q;
row_to_json
-------------------
{"x":42,"y":42}
{"x":42,"y":1042}
(2 rows)
select row_to_json(q) from
(select thousand as x, tenthous as y from tenk1
where thousand = 42 and tenthous < 2000 offset 0) q(a,b);
row_to_json
-------------------
{"a":42,"b":42}
{"a":42,"b":1042}
(2 rows)
create temp table tt1 as select * from int8_tbl limit 2;
create temp table tt2 () inherits(tt1);
insert into tt2 values(0,0);
select row_to_json(r) from (select q2,q1 from tt1 offset 0) r;
row_to_json
----------------------------------
{"q2":456,"q1":123}
{"q2":4567890123456789,"q1":123}
{"q2":0,"q1":0}
(3 rows)