1-- 2-- ROWTYPES 3-- 4 5-- Make both a standalone composite type and a table rowtype 6 7create type complex as (r float8, i float8); 8 9create temp table fullname (first text, last text); 10 11-- Nested composite 12 13create type quad as (c1 complex, c2 complex); 14 15-- Some simple tests of I/O conversions and row construction 16 17select (1.1,2.2)::complex, row((3.3,4.4),(5.5,null))::quad; 18 19select row('Joe', 'Blow')::fullname, '(Joe,Blow)'::fullname; 20 21select '(Joe,von Blow)'::fullname, '(Joe,d''Blow)'::fullname; 22 23select '(Joe,"von""Blow")'::fullname, E'(Joe,d\\\\Blow)'::fullname; 24 25select '(Joe,"Blow,Jr")'::fullname; 26 27select '(Joe,)'::fullname; -- ok, null 2nd column 28select '(Joe)'::fullname; -- bad 29select '(Joe,,)'::fullname; -- bad 30select '[]'::fullname; -- bad 31select ' (Joe,Blow) '::fullname; -- ok, extra whitespace 32select '(Joe,Blow) /'::fullname; -- bad 33 34create temp table quadtable(f1 int, q quad); 35 36insert into quadtable values (1, ((3.3,4.4),(5.5,6.6))); 37insert into quadtable values (2, ((null,4.4),(5.5,6.6))); 38 39select * from quadtable; 40 41select f1, q.c1 from quadtable; -- fails, q is a table reference 42 43select f1, (q).c1, (qq.q).c1.i from quadtable qq; 44 45create temp table people (fn fullname, bd date); 46 47insert into people values ('(Joe,Blow)', '1984-01-10'); 48 49select * from people; 50 51-- at the moment this will not work due to ALTER TABLE inadequacy: 52alter table fullname add column suffix text default ''; 53 54-- but this should work: 55alter table fullname add column suffix text default null; 56 57select * from people; 58 59-- test insertion/updating of subfields 60update people set fn.suffix = 'Jr'; 61 62select * from people; 63 64insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66); 65 66select * from quadtable; 67 68-- The object here is to ensure that toasted references inside 69-- composite values don't cause problems. The large f1 value will 70-- be toasted inside pp, it must still work after being copied to people. 71 72create temp table pp (f1 text); 73insert into pp values (repeat('abcdefghijkl', 100000)); 74 75insert into people select ('Jim', f1, null)::fullname, current_date from pp; 76 77select (fn).first, substr((fn).last, 1, 20), length((fn).last) from people; 78 79-- Test row comparison semantics. Prior to PG 8.2 we did this in a totally 80-- non-spec-compliant way. 81 82select ROW(1,2) < ROW(1,3) as true; 83select ROW(1,2) < ROW(1,1) as false; 84select ROW(1,2) < ROW(1,NULL) as null; 85select ROW(1,2,3) < ROW(1,3,NULL) as true; -- the NULL is not examined 86select ROW(11,'ABC') < ROW(11,'DEF') as true; 87select ROW(11,'ABC') > ROW(11,'DEF') as false; 88select ROW(12,'ABC') > ROW(11,'DEF') as true; 89 90-- = and <> have different NULL-behavior than < etc 91select ROW(1,2,3) < ROW(1,NULL,4) as null; 92select ROW(1,2,3) = ROW(1,NULL,4) as false; 93select ROW(1,2,3) <> ROW(1,NULL,4) as true; 94 95-- We allow operators beyond the six standard ones, if they have btree 96-- operator classes. 97select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true; 98select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false; 99select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail; 100 101-- Comparisons of ROW() expressions can cope with some type mismatches 102select ROW(1,2) = ROW(1,2::int8); 103select ROW(1,2) in (ROW(3,4), ROW(1,2)); 104select ROW(1,2) in (ROW(3,4), ROW(1,2::int8)); 105 106-- Check row comparison with a subselect 107select unique1, unique2 from tenk1 108where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3) 109 and unique1 <= 20 110order by 1; 111 112-- Also check row comparison with an indexable condition 113explain (costs off) 114select thousand, tenthous from tenk1 115where (thousand, tenthous) >= (997, 5000) 116order by thousand, tenthous; 117 118select thousand, tenthous from tenk1 119where (thousand, tenthous) >= (997, 5000) 120order by thousand, tenthous; 121 122explain (costs off) 123select thousand, tenthous, four from tenk1 124where (thousand, tenthous, four) > (998, 5000, 3) 125order by thousand, tenthous; 126 127select thousand, tenthous, four from tenk1 128where (thousand, tenthous, four) > (998, 5000, 3) 129order by thousand, tenthous; 130 131explain (costs off) 132select thousand, tenthous from tenk1 133where (998, 5000) < (thousand, tenthous) 134order by thousand, tenthous; 135 136select thousand, tenthous from tenk1 137where (998, 5000) < (thousand, tenthous) 138order by thousand, tenthous; 139 140explain (costs off) 141select thousand, hundred from tenk1 142where (998, 5000) < (thousand, hundred) 143order by thousand, hundred; 144 145select thousand, hundred from tenk1 146where (998, 5000) < (thousand, hundred) 147order by thousand, hundred; 148 149-- Test case for bug #14010: indexed row comparisons fail with nulls 150create temp table test_table (a text, b text); 151insert into test_table values ('a', 'b'); 152insert into test_table select 'a', null from generate_series(1,1000); 153insert into test_table values ('b', 'a'); 154create index on test_table (a,b); 155set enable_sort = off; 156 157explain (costs off) 158select a,b from test_table where (a,b) > ('a','a') order by a,b; 159 160select a,b from test_table where (a,b) > ('a','a') order by a,b; 161 162reset enable_sort; 163 164-- Check row comparisons with IN 165select * from int8_tbl i8 where i8 in (row(123,456)); -- fail, type mismatch 166 167explain (costs off) 168select * from int8_tbl i8 169where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); 170 171select * from int8_tbl i8 172where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); 173 174-- Check some corner cases involving empty rowtypes 175select ROW(); 176select ROW() IS NULL; 177select ROW() = ROW(); 178 179-- Check ability to create arrays of anonymous rowtypes 180select array[ row(1,2), row(3,4), row(5,6) ]; 181 182-- Check ability to compare an anonymous row to elements of an array 183select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]); 184select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]); 185 186-- Check behavior with a non-comparable rowtype 187create type cantcompare as (p point, r float8); 188create temp table cc (f1 cantcompare); 189insert into cc values('("(1,2)",3)'); 190insert into cc values('("(4,5)",6)'); 191select * from cc order by f1; -- fail, but should complain about cantcompare 192 193-- 194-- Tests for record_{eq,cmp} 195-- 196 197create type testtype1 as (a int, b int); 198 199-- all true 200select row(1, 2)::testtype1 < row(1, 3)::testtype1; 201select row(1, 2)::testtype1 <= row(1, 3)::testtype1; 202select row(1, 2)::testtype1 = row(1, 2)::testtype1; 203select row(1, 2)::testtype1 <> row(1, 3)::testtype1; 204select row(1, 3)::testtype1 >= row(1, 2)::testtype1; 205select row(1, 3)::testtype1 > row(1, 2)::testtype1; 206 207-- all false 208select row(1, -2)::testtype1 < row(1, -3)::testtype1; 209select row(1, -2)::testtype1 <= row(1, -3)::testtype1; 210select row(1, -2)::testtype1 = row(1, -3)::testtype1; 211select row(1, -2)::testtype1 <> row(1, -2)::testtype1; 212select row(1, -3)::testtype1 >= row(1, -2)::testtype1; 213select row(1, -3)::testtype1 > row(1, -2)::testtype1; 214 215-- true, but see *< below 216select row(1, -2)::testtype1 < row(1, 3)::testtype1; 217 218-- mismatches 219create type testtype3 as (a int, b text); 220select row(1, 2)::testtype1 < row(1, 'abc')::testtype3; 221select row(1, 2)::testtype1 <> row(1, 'abc')::testtype3; 222create type testtype5 as (a int); 223select row(1, 2)::testtype1 < row(1)::testtype5; 224select row(1, 2)::testtype1 <> row(1)::testtype5; 225 226-- non-comparable types 227create type testtype6 as (a int, b point); 228select row(1, '(1,2)')::testtype6 < row(1, '(1,3)')::testtype6; 229select row(1, '(1,2)')::testtype6 <> row(1, '(1,3)')::testtype6; 230 231drop type testtype1, testtype3, testtype5, testtype6; 232 233-- 234-- Tests for record_image_{eq,cmp} 235-- 236 237create type testtype1 as (a int, b int); 238 239-- all true 240select row(1, 2)::testtype1 *< row(1, 3)::testtype1; 241select row(1, 2)::testtype1 *<= row(1, 3)::testtype1; 242select row(1, 2)::testtype1 *= row(1, 2)::testtype1; 243select row(1, 2)::testtype1 *<> row(1, 3)::testtype1; 244select row(1, 3)::testtype1 *>= row(1, 2)::testtype1; 245select row(1, 3)::testtype1 *> row(1, 2)::testtype1; 246 247-- all false 248select row(1, -2)::testtype1 *< row(1, -3)::testtype1; 249select row(1, -2)::testtype1 *<= row(1, -3)::testtype1; 250select row(1, -2)::testtype1 *= row(1, -3)::testtype1; 251select row(1, -2)::testtype1 *<> row(1, -2)::testtype1; 252select row(1, -3)::testtype1 *>= row(1, -2)::testtype1; 253select row(1, -3)::testtype1 *> row(1, -2)::testtype1; 254 255-- This returns the "wrong" order because record_image_cmp works on 256-- unsigned datums without knowing about the actual data type. 257select row(1, -2)::testtype1 *< row(1, 3)::testtype1; 258 259-- other types 260create type testtype2 as (a smallint, b bool); -- byval different sizes 261select row(1, true)::testtype2 *< row(2, true)::testtype2; 262select row(-2, true)::testtype2 *< row(-1, true)::testtype2; 263select row(0, false)::testtype2 *< row(0, true)::testtype2; 264select row(0, false)::testtype2 *<> row(0, true)::testtype2; 265 266create type testtype3 as (a int, b text); -- variable length 267select row(1, 'abc')::testtype3 *< row(1, 'abd')::testtype3; 268select row(1, 'abc')::testtype3 *< row(1, 'abcd')::testtype3; 269select row(1, 'abc')::testtype3 *> row(1, 'abd')::testtype3; 270select row(1, 'abc')::testtype3 *<> row(1, 'abd')::testtype3; 271 272create type testtype4 as (a int, b point); -- by ref, fixed length 273select row(1, '(1,2)')::testtype4 *< row(1, '(1,3)')::testtype4; 274select row(1, '(1,2)')::testtype4 *<> row(1, '(1,3)')::testtype4; 275 276-- mismatches 277select row(1, 2)::testtype1 *< row(1, 'abc')::testtype3; 278select row(1, 2)::testtype1 *<> row(1, 'abc')::testtype3; 279create type testtype5 as (a int); 280select row(1, 2)::testtype1 *< row(1)::testtype5; 281select row(1, 2)::testtype1 *<> row(1)::testtype5; 282 283-- non-comparable types 284create type testtype6 as (a int, b point); 285select row(1, '(1,2)')::testtype6 *< row(1, '(1,3)')::testtype6; 286select row(1, '(1,2)')::testtype6 *>= row(1, '(1,3)')::testtype6; 287select row(1, '(1,2)')::testtype6 *<> row(1, '(1,3)')::testtype6; 288 289-- anonymous rowtypes in coldeflists 290select q.a, q.b = row(2), q.c = array[row(3)], q.d = row(row(4)) from 291 unnest(array[row(1, row(2), array[row(3)], row(row(4))), 292 row(2, row(3), array[row(4)], row(row(5)))]) 293 as q(a int, b record, c record[], d record); 294 295drop type testtype1, testtype2, testtype3, testtype4, testtype5, testtype6; 296 297 298-- 299-- Test case derived from bug #5716: check multiple uses of a rowtype result 300-- 301 302BEGIN; 303 304CREATE TABLE price ( 305 id SERIAL PRIMARY KEY, 306 active BOOLEAN NOT NULL, 307 price NUMERIC 308); 309 310CREATE TYPE price_input AS ( 311 id INTEGER, 312 price NUMERIC 313); 314 315CREATE TYPE price_key AS ( 316 id INTEGER 317); 318 319CREATE FUNCTION price_key_from_table(price) RETURNS price_key AS $$ 320 SELECT $1.id 321$$ LANGUAGE SQL; 322 323CREATE FUNCTION price_key_from_input(price_input) RETURNS price_key AS $$ 324 SELECT $1.id 325$$ LANGUAGE SQL; 326 327insert into price values (1,false,42), (10,false,100), (11,true,17.99); 328 329UPDATE price 330 SET active = true, price = input_prices.price 331 FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices 332 WHERE price_key_from_table(price.*) = price_key_from_input(input_prices.*); 333 334select * from price; 335 336rollback; 337 338-- 339-- Test case derived from bug #9085: check * qualification of composite 340-- parameters for SQL functions 341-- 342 343create temp table compos (f1 int, f2 text); 344 345create function fcompos1(v compos) returns void as $$ 346insert into compos values (v); -- fail 347$$ language sql; 348 349create function fcompos1(v compos) returns void as $$ 350insert into compos values (v.*); 351$$ language sql; 352 353create function fcompos2(v compos) returns void as $$ 354select fcompos1(v); 355$$ language sql; 356 357create function fcompos3(v compos) returns void as $$ 358select fcompos1(fcompos3.v.*); 359$$ language sql; 360 361select fcompos1(row(1,'one')); 362select fcompos2(row(2,'two')); 363select fcompos3(row(3,'three')); 364select * from compos; 365 366-- 367-- We allow I/O conversion casts from composite types to strings to be 368-- invoked via cast syntax, but not functional syntax. This is because 369-- the latter is too prone to be invoked unintentionally. 370-- 371select cast (fullname as text) from fullname; 372select fullname::text from fullname; 373select text(fullname) from fullname; -- error 374select fullname.text from fullname; -- error 375-- same, but RECORD instead of named composite type: 376select cast (row('Jim', 'Beam') as text); 377select (row('Jim', 'Beam'))::text; 378select text(row('Jim', 'Beam')); -- error 379select (row('Jim', 'Beam')).text; -- error 380 381-- 382-- Check the equivalence of functional and column notation 383-- 384insert into fullname values ('Joe', 'Blow'); 385 386select f.last from fullname f; 387select last(f) from fullname f; 388 389create function longname(fullname) returns text language sql 390as $$select $1.first || ' ' || $1.last$$; 391 392select f.longname from fullname f; 393select longname(f) from fullname f; 394 395-- Starting in v11, the notational form does matter if there's ambiguity 396alter table fullname add column longname text; 397 398select f.longname from fullname f; 399select longname(f) from fullname f; 400 401-- 402-- Test that composite values are seen to have the correct column names 403-- (bug #11210 and other reports) 404-- 405 406select row_to_json(i) from int8_tbl i; 407select row_to_json(i) from int8_tbl i(x,y); 408 409create temp view vv1 as select * from int8_tbl; 410select row_to_json(i) from vv1 i; 411select row_to_json(i) from vv1 i(x,y); 412 413select row_to_json(ss) from 414 (select q1, q2 from int8_tbl) as ss; 415select row_to_json(ss) from 416 (select q1, q2 from int8_tbl offset 0) as ss; 417select row_to_json(ss) from 418 (select q1 as a, q2 as b from int8_tbl) as ss; 419select row_to_json(ss) from 420 (select q1 as a, q2 as b from int8_tbl offset 0) as ss; 421select row_to_json(ss) from 422 (select q1 as a, q2 as b from int8_tbl) as ss(x,y); 423select row_to_json(ss) from 424 (select q1 as a, q2 as b from int8_tbl offset 0) as ss(x,y); 425 426explain (costs off) 427select row_to_json(q) from 428 (select thousand, tenthous from tenk1 429 where thousand = 42 and tenthous < 2000 offset 0) q; 430select row_to_json(q) from 431 (select thousand, tenthous from tenk1 432 where thousand = 42 and tenthous < 2000 offset 0) q; 433select row_to_json(q) from 434 (select thousand as x, tenthous as y from tenk1 435 where thousand = 42 and tenthous < 2000 offset 0) q; 436select row_to_json(q) from 437 (select thousand as x, tenthous as y from tenk1 438 where thousand = 42 and tenthous < 2000 offset 0) q(a,b); 439 440create temp table tt1 as select * from int8_tbl limit 2; 441create temp table tt2 () inherits(tt1); 442insert into tt2 values(0,0); 443select row_to_json(r) from (select q2,q1 from tt1 offset 0) r; 444 445-- check no-op rowtype conversions 446create temp table tt3 () inherits(tt2); 447insert into tt3 values(33,44); 448select row_to_json(tt3::tt2::tt1) from tt3; 449 450-- 451-- IS [NOT] NULL should not recurse into nested composites (bug #14235) 452-- 453 454explain (verbose, costs off) 455select r, r is null as isnull, r is not null as isnotnull 456from (values (1,row(1,2)), (1,row(null,null)), (1,null), 457 (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); 458 459select r, r is null as isnull, r is not null as isnotnull 460from (values (1,row(1,2)), (1,row(null,null)), (1,null), 461 (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); 462 463explain (verbose, costs off) 464with r(a,b) as materialized 465 (values (1,row(1,2)), (1,row(null,null)), (1,null), 466 (null,row(1,2)), (null,row(null,null)), (null,null) ) 467select r, r is null as isnull, r is not null as isnotnull from r; 468 469with r(a,b) as materialized 470 (values (1,row(1,2)), (1,row(null,null)), (1,null), 471 (null,row(1,2)), (null,row(null,null)), (null,null) ) 472select r, r is null as isnull, r is not null as isnotnull from r; 473 474 475-- 476-- Tests for component access / FieldSelect 477-- 478CREATE TABLE compositetable(a text, b text); 479INSERT INTO compositetable(a, b) VALUES('fa', 'fb'); 480 481-- composite type columns can't directly be accessed (error) 482SELECT d.a FROM (SELECT compositetable AS d FROM compositetable) s; 483-- but can be accessed with proper parens 484SELECT (d).a, (d).b FROM (SELECT compositetable AS d FROM compositetable) s; 485-- system columns can't be accessed in composite types (error) 486SELECT (d).ctid FROM (SELECT compositetable AS d FROM compositetable) s; 487 488-- accessing non-existing column in NULL datum errors out 489SELECT (NULL::compositetable).nonexistant; 490-- existing column in a NULL composite yield NULL 491SELECT (NULL::compositetable).a; 492-- oids can't be accessed in composite types (error) 493SELECT (NULL::compositetable).oid; 494 495DROP TABLE compositetable; 496