1-- 2-- ROWTYPES 3-- 4-- Make both a standalone composite type and a table rowtype 5create type complex as (r float8, i float8); 6create temp table fullname (first text, last text); 7-- Nested composite 8create type quad as (c1 complex, c2 complex); 9-- Some simple tests of I/O conversions and row construction 10select (1.1,2.2)::complex, row((3.3,4.4),(5.5,null))::quad; 11 row | row 12-----------+------------------------ 13 (1.1,2.2) | ("(3.3,4.4)","(5.5,)") 14(1 row) 15 16select row('Joe', 'Blow')::fullname, '(Joe,Blow)'::fullname; 17 row | fullname 18------------+------------ 19 (Joe,Blow) | (Joe,Blow) 20(1 row) 21 22select '(Joe,von Blow)'::fullname, '(Joe,d''Blow)'::fullname; 23 fullname | fullname 24------------------+-------------- 25 (Joe,"von Blow") | (Joe,d'Blow) 26(1 row) 27 28select '(Joe,"von""Blow")'::fullname, E'(Joe,d\\\\Blow)'::fullname; 29 fullname | fullname 30-------------------+----------------- 31 (Joe,"von""Blow") | (Joe,"d\\Blow") 32(1 row) 33 34select '(Joe,"Blow,Jr")'::fullname; 35 fullname 36----------------- 37 (Joe,"Blow,Jr") 38(1 row) 39 40select '(Joe,)'::fullname; -- ok, null 2nd column 41 fullname 42---------- 43 (Joe,) 44(1 row) 45 46select '(Joe)'::fullname; -- bad 47ERROR: malformed record literal: "(Joe)" 48LINE 1: select '(Joe)'::fullname; 49 ^ 50DETAIL: Too few columns. 51select '(Joe,,)'::fullname; -- bad 52ERROR: malformed record literal: "(Joe,,)" 53LINE 1: select '(Joe,,)'::fullname; 54 ^ 55DETAIL: Too many columns. 56create temp table quadtable(f1 int, q quad); 57insert into quadtable values (1, ((3.3,4.4),(5.5,6.6))); 58insert into quadtable values (2, ((null,4.4),(5.5,6.6))); 59select * from quadtable; 60 f1 | q 61----+--------------------------- 62 1 | ("(3.3,4.4)","(5.5,6.6)") 63 2 | ("(,4.4)","(5.5,6.6)") 64(2 rows) 65 66select f1, q.c1 from quadtable; -- fails, q is a table reference 67ERROR: missing FROM-clause entry for table "q" 68LINE 1: select f1, q.c1 from quadtable; 69 ^ 70select f1, (q).c1, (qq.q).c1.i from quadtable qq; 71 f1 | c1 | i 72----+-----------+----- 73 1 | (3.3,4.4) | 4.4 74 2 | (,4.4) | 4.4 75(2 rows) 76 77create temp table people (fn fullname, bd date); 78insert into people values ('(Joe,Blow)', '1984-01-10'); 79select * from people; 80 fn | bd 81------------+------------ 82 (Joe,Blow) | 01-10-1984 83(1 row) 84 85-- at the moment this will not work due to ALTER TABLE inadequacy: 86alter table fullname add column suffix text default ''; 87ERROR: cannot alter table "fullname" because column "people.fn" uses its row type 88-- but this should work: 89alter table fullname add column suffix text default null; 90select * from people; 91 fn | bd 92-------------+------------ 93 (Joe,Blow,) | 01-10-1984 94(1 row) 95 96-- test insertion/updating of subfields 97update people set fn.suffix = 'Jr'; 98select * from people; 99 fn | bd 100---------------+------------ 101 (Joe,Blow,Jr) | 01-10-1984 102(1 row) 103 104insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66); 105select * from quadtable; 106 f1 | q 107----+--------------------------- 108 1 | ("(3.3,4.4)","(5.5,6.6)") 109 2 | ("(,4.4)","(5.5,6.6)") 110 44 | ("(55,)","(,66)") 111(3 rows) 112 113-- The object here is to ensure that toasted references inside 114-- composite values don't cause problems. The large f1 value will 115-- be toasted inside pp, it must still work after being copied to people. 116create temp table pp (f1 text); 117insert into pp values (repeat('abcdefghijkl', 100000)); 118insert into people select ('Jim', f1, null)::fullname, current_date from pp; 119select (fn).first, substr((fn).last, 1, 20), length((fn).last) from people; 120 first | substr | length 121-------+----------------------+--------- 122 Joe | Blow | 4 123 Jim | abcdefghijklabcdefgh | 1200000 124(2 rows) 125 126-- Test row comparison semantics. Prior to PG 8.2 we did this in a totally 127-- non-spec-compliant way. 128select ROW(1,2) < ROW(1,3) as true; 129 true 130------ 131 t 132(1 row) 133 134select ROW(1,2) < ROW(1,1) as false; 135 false 136------- 137 f 138(1 row) 139 140select ROW(1,2) < ROW(1,NULL) as null; 141 null 142------ 143 144(1 row) 145 146select ROW(1,2,3) < ROW(1,3,NULL) as true; -- the NULL is not examined 147 true 148------ 149 t 150(1 row) 151 152select ROW(11,'ABC') < ROW(11,'DEF') as true; 153 true 154------ 155 t 156(1 row) 157 158select ROW(11,'ABC') > ROW(11,'DEF') as false; 159 false 160------- 161 f 162(1 row) 163 164select ROW(12,'ABC') > ROW(11,'DEF') as true; 165 true 166------ 167 t 168(1 row) 169 170-- = and <> have different NULL-behavior than < etc 171select ROW(1,2,3) < ROW(1,NULL,4) as null; 172 null 173------ 174 175(1 row) 176 177select ROW(1,2,3) = ROW(1,NULL,4) as false; 178 false 179------- 180 f 181(1 row) 182 183select ROW(1,2,3) <> ROW(1,NULL,4) as true; 184 true 185------ 186 t 187(1 row) 188 189-- We allow operators beyond the six standard ones, if they have btree 190-- operator classes. 191select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true; 192 true 193------ 194 t 195(1 row) 196 197select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false; 198 false 199------- 200 f 201(1 row) 202 203select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail; 204ERROR: could not determine interpretation of row comparison operator ~~ 205LINE 1: select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail; 206 ^ 207HINT: Row comparison operators must be associated with btree operator families. 208-- Comparisons of ROW() expressions can cope with some type mismatches 209select ROW(1,2) = ROW(1,2::int8); 210 ?column? 211---------- 212 t 213(1 row) 214 215select ROW(1,2) in (ROW(3,4), ROW(1,2)); 216 ?column? 217---------- 218 t 219(1 row) 220 221select ROW(1,2) in (ROW(3,4), ROW(1,2::int8)); 222 ?column? 223---------- 224 t 225(1 row) 226 227-- Check row comparison with a subselect 228select unique1, unique2 from tenk1 229where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3) 230 and unique1 <= 20 231order by 1; 232 unique1 | unique2 233---------+--------- 234 0 | 9998 235 1 | 2838 236(2 rows) 237 238-- Also check row comparison with an indexable condition 239explain (costs off) 240select thousand, tenthous from tenk1 241where (thousand, tenthous) >= (997, 5000) 242order by thousand, tenthous; 243 QUERY PLAN 244----------------------------------------------------------- 245 Index Only Scan using tenk1_thous_tenthous on tenk1 246 Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000)) 247(2 rows) 248 249select thousand, tenthous from tenk1 250where (thousand, tenthous) >= (997, 5000) 251order by thousand, tenthous; 252 thousand | tenthous 253----------+---------- 254 997 | 5997 255 997 | 6997 256 997 | 7997 257 997 | 8997 258 997 | 9997 259 998 | 998 260 998 | 1998 261 998 | 2998 262 998 | 3998 263 998 | 4998 264 998 | 5998 265 998 | 6998 266 998 | 7998 267 998 | 8998 268 998 | 9998 269 999 | 999 270 999 | 1999 271 999 | 2999 272 999 | 3999 273 999 | 4999 274 999 | 5999 275 999 | 6999 276 999 | 7999 277 999 | 8999 278 999 | 9999 279(25 rows) 280 281-- Test case for bug #14010: indexed row comparisons fail with nulls 282create temp table test_table (a text, b text); 283insert into test_table values ('a', 'b'); 284insert into test_table select 'a', null from generate_series(1,1000); 285insert into test_table values ('b', 'a'); 286create index on test_table (a,b); 287set enable_sort = off; 288explain (costs off) 289select a,b from test_table where (a,b) > ('a','a') order by a,b; 290 QUERY PLAN 291-------------------------------------------------------- 292 Index Only Scan using test_table_a_b_idx on test_table 293 Index Cond: (ROW(a, b) > ROW('a'::text, 'a'::text)) 294(2 rows) 295 296select a,b from test_table where (a,b) > ('a','a') order by a,b; 297 a | b 298---+--- 299 a | b 300 b | a 301(2 rows) 302 303reset enable_sort; 304-- Check row comparisons with IN 305select * from int8_tbl i8 where i8 in (row(123,456)); -- fail, type mismatch 306ERROR: cannot compare dissimilar column types bigint and integer at record column 1 307explain (costs off) 308select * from int8_tbl i8 309where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); 310 QUERY PLAN 311----------------------------------------------------------------------------------------------------------------- 312 Seq Scan on int8_tbl i8 313 Filter: (i8.* = ANY (ARRAY[ROW('123'::bigint, '456'::bigint)::int8_tbl, '(4567890123456789,123)'::int8_tbl])) 314(2 rows) 315 316select * from int8_tbl i8 317where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); 318 q1 | q2 319------------------+----- 320 123 | 456 321 4567890123456789 | 123 322(2 rows) 323 324-- Check some corner cases involving empty rowtypes 325select ROW(); 326 row 327----- 328 () 329(1 row) 330 331select ROW() IS NULL; 332 ?column? 333---------- 334 t 335(1 row) 336 337select ROW() = ROW(); 338ERROR: cannot compare rows of zero length 339LINE 1: select ROW() = ROW(); 340 ^ 341-- Check ability to create arrays of anonymous rowtypes 342select array[ row(1,2), row(3,4), row(5,6) ]; 343 array 344--------------------------- 345 {"(1,2)","(3,4)","(5,6)"} 346(1 row) 347 348-- Check ability to compare an anonymous row to elements of an array 349select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]); 350 ?column? 351---------- 352 t 353(1 row) 354 355select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]); 356 ?column? 357---------- 358 f 359(1 row) 360 361-- Check behavior with a non-comparable rowtype 362create type cantcompare as (p point, r float8); 363create temp table cc (f1 cantcompare); 364insert into cc values('("(1,2)",3)'); 365insert into cc values('("(4,5)",6)'); 366select * from cc order by f1; -- fail, but should complain about cantcompare 367ERROR: could not identify an ordering operator for type cantcompare 368LINE 1: select * from cc order by f1; 369 ^ 370HINT: Use an explicit ordering operator or modify the query. 371-- 372-- Test case derived from bug #5716: check multiple uses of a rowtype result 373-- 374BEGIN; 375CREATE TABLE price ( 376 id SERIAL PRIMARY KEY, 377 active BOOLEAN NOT NULL, 378 price NUMERIC 379); 380CREATE TYPE price_input AS ( 381 id INTEGER, 382 price NUMERIC 383); 384CREATE TYPE price_key AS ( 385 id INTEGER 386); 387CREATE FUNCTION price_key_from_table(price) RETURNS price_key AS $$ 388 SELECT $1.id 389$$ LANGUAGE SQL; 390CREATE FUNCTION price_key_from_input(price_input) RETURNS price_key AS $$ 391 SELECT $1.id 392$$ LANGUAGE SQL; 393insert into price values (1,false,42), (10,false,100), (11,true,17.99); 394UPDATE price 395 SET active = true, price = input_prices.price 396 FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices 397 WHERE price_key_from_table(price.*) = price_key_from_input(input_prices.*); 398select * from price; 399 id | active | price 400----+--------+-------- 401 1 | f | 42 402 10 | t | 123.00 403 11 | t | 99.99 404(3 rows) 405 406rollback; 407-- 408-- Test case derived from bug #9085: check * qualification of composite 409-- parameters for SQL functions 410-- 411create temp table compos (f1 int, f2 text); 412create function fcompos1(v compos) returns void as $$ 413insert into compos values (v); -- fail 414$$ language sql; 415ERROR: column "f1" is of type integer but expression is of type compos 416LINE 2: insert into compos values (v); -- fail 417 ^ 418HINT: You will need to rewrite or cast the expression. 419create function fcompos1(v compos) returns void as $$ 420insert into compos values (v.*); 421$$ language sql; 422create function fcompos2(v compos) returns void as $$ 423select fcompos1(v); 424$$ language sql; 425create function fcompos3(v compos) returns void as $$ 426select fcompos1(fcompos3.v.*); 427$$ language sql; 428select fcompos1(row(1,'one')); 429 fcompos1 430---------- 431 432(1 row) 433 434select fcompos2(row(2,'two')); 435 fcompos2 436---------- 437 438(1 row) 439 440select fcompos3(row(3,'three')); 441 fcompos3 442---------- 443 444(1 row) 445 446select * from compos; 447 f1 | f2 448----+------- 449 1 | one 450 2 | two 451 3 | three 452(3 rows) 453 454-- 455-- We allow I/O conversion casts from composite types to strings to be 456-- invoked via cast syntax, but not functional syntax. This is because 457-- the latter is too prone to be invoked unintentionally. 458-- 459select cast (fullname as text) from fullname; 460 fullname 461---------- 462(0 rows) 463 464select fullname::text from fullname; 465 fullname 466---------- 467(0 rows) 468 469select text(fullname) from fullname; -- error 470ERROR: function text(fullname) does not exist 471LINE 1: select text(fullname) from fullname; 472 ^ 473HINT: No function matches the given name and argument types. You might need to add explicit type casts. 474select fullname.text from fullname; -- error 475ERROR: column fullname.text does not exist 476LINE 1: select fullname.text from fullname; 477 ^ 478-- same, but RECORD instead of named composite type: 479select cast (row('Jim', 'Beam') as text); 480 row 481------------ 482 (Jim,Beam) 483(1 row) 484 485select (row('Jim', 'Beam'))::text; 486 row 487------------ 488 (Jim,Beam) 489(1 row) 490 491select text(row('Jim', 'Beam')); -- error 492ERROR: function text(record) does not exist 493LINE 1: select text(row('Jim', 'Beam')); 494 ^ 495HINT: No function matches the given name and argument types. You might need to add explicit type casts. 496select (row('Jim', 'Beam')).text; -- error 497ERROR: could not identify column "text" in record data type 498LINE 1: select (row('Jim', 'Beam')).text; 499 ^ 500-- 501-- Test that composite values are seen to have the correct column names 502-- (bug #11210 and other reports) 503-- 504select row_to_json(i) from int8_tbl i; 505 row_to_json 506------------------------------------------------ 507 {"q1":123,"q2":456} 508 {"q1":123,"q2":4567890123456789} 509 {"q1":4567890123456789,"q2":123} 510 {"q1":4567890123456789,"q2":4567890123456789} 511 {"q1":4567890123456789,"q2":-4567890123456789} 512(5 rows) 513 514select row_to_json(i) from int8_tbl i(x,y); 515 row_to_json 516---------------------------------------------- 517 {"x":123,"y":456} 518 {"x":123,"y":4567890123456789} 519 {"x":4567890123456789,"y":123} 520 {"x":4567890123456789,"y":4567890123456789} 521 {"x":4567890123456789,"y":-4567890123456789} 522(5 rows) 523 524create temp view vv1 as select * from int8_tbl; 525select row_to_json(i) from vv1 i; 526 row_to_json 527------------------------------------------------ 528 {"q1":123,"q2":456} 529 {"q1":123,"q2":4567890123456789} 530 {"q1":4567890123456789,"q2":123} 531 {"q1":4567890123456789,"q2":4567890123456789} 532 {"q1":4567890123456789,"q2":-4567890123456789} 533(5 rows) 534 535select row_to_json(i) from vv1 i(x,y); 536 row_to_json 537---------------------------------------------- 538 {"x":123,"y":456} 539 {"x":123,"y":4567890123456789} 540 {"x":4567890123456789,"y":123} 541 {"x":4567890123456789,"y":4567890123456789} 542 {"x":4567890123456789,"y":-4567890123456789} 543(5 rows) 544 545select row_to_json(ss) from 546 (select q1, q2 from int8_tbl) as ss; 547 row_to_json 548------------------------------------------------ 549 {"q1":123,"q2":456} 550 {"q1":123,"q2":4567890123456789} 551 {"q1":4567890123456789,"q2":123} 552 {"q1":4567890123456789,"q2":4567890123456789} 553 {"q1":4567890123456789,"q2":-4567890123456789} 554(5 rows) 555 556select row_to_json(ss) from 557 (select q1, q2 from int8_tbl offset 0) as ss; 558 row_to_json 559------------------------------------------------ 560 {"q1":123,"q2":456} 561 {"q1":123,"q2":4567890123456789} 562 {"q1":4567890123456789,"q2":123} 563 {"q1":4567890123456789,"q2":4567890123456789} 564 {"q1":4567890123456789,"q2":-4567890123456789} 565(5 rows) 566 567select row_to_json(ss) from 568 (select q1 as a, q2 as b from int8_tbl) as ss; 569 row_to_json 570---------------------------------------------- 571 {"a":123,"b":456} 572 {"a":123,"b":4567890123456789} 573 {"a":4567890123456789,"b":123} 574 {"a":4567890123456789,"b":4567890123456789} 575 {"a":4567890123456789,"b":-4567890123456789} 576(5 rows) 577 578select row_to_json(ss) from 579 (select q1 as a, q2 as b from int8_tbl offset 0) as ss; 580 row_to_json 581---------------------------------------------- 582 {"a":123,"b":456} 583 {"a":123,"b":4567890123456789} 584 {"a":4567890123456789,"b":123} 585 {"a":4567890123456789,"b":4567890123456789} 586 {"a":4567890123456789,"b":-4567890123456789} 587(5 rows) 588 589select row_to_json(ss) from 590 (select q1 as a, q2 as b from int8_tbl) as ss(x,y); 591 row_to_json 592---------------------------------------------- 593 {"x":123,"y":456} 594 {"x":123,"y":4567890123456789} 595 {"x":4567890123456789,"y":123} 596 {"x":4567890123456789,"y":4567890123456789} 597 {"x":4567890123456789,"y":-4567890123456789} 598(5 rows) 599 600select row_to_json(ss) from 601 (select q1 as a, q2 as b from int8_tbl offset 0) as ss(x,y); 602 row_to_json 603---------------------------------------------- 604 {"x":123,"y":456} 605 {"x":123,"y":4567890123456789} 606 {"x":4567890123456789,"y":123} 607 {"x":4567890123456789,"y":4567890123456789} 608 {"x":4567890123456789,"y":-4567890123456789} 609(5 rows) 610 611explain (costs off) 612select row_to_json(q) from 613 (select thousand, tenthous from tenk1 614 where thousand = 42 and tenthous < 2000 offset 0) q; 615 QUERY PLAN 616------------------------------------------------------------- 617 Subquery Scan on q 618 -> Index Only Scan using tenk1_thous_tenthous on tenk1 619 Index Cond: ((thousand = 42) AND (tenthous < 2000)) 620(3 rows) 621 622select row_to_json(q) from 623 (select thousand, tenthous from tenk1 624 where thousand = 42 and tenthous < 2000 offset 0) q; 625 row_to_json 626--------------------------------- 627 {"thousand":42,"tenthous":42} 628 {"thousand":42,"tenthous":1042} 629(2 rows) 630 631select row_to_json(q) from 632 (select thousand as x, tenthous as y from tenk1 633 where thousand = 42 and tenthous < 2000 offset 0) q; 634 row_to_json 635------------------- 636 {"x":42,"y":42} 637 {"x":42,"y":1042} 638(2 rows) 639 640select row_to_json(q) from 641 (select thousand as x, tenthous as y from tenk1 642 where thousand = 42 and tenthous < 2000 offset 0) q(a,b); 643 row_to_json 644------------------- 645 {"a":42,"b":42} 646 {"a":42,"b":1042} 647(2 rows) 648 649create temp table tt1 as select * from int8_tbl limit 2; 650create temp table tt2 () inherits(tt1); 651insert into tt2 values(0,0); 652select row_to_json(r) from (select q2,q1 from tt1 offset 0) r; 653 row_to_json 654---------------------------------- 655 {"q2":456,"q1":123} 656 {"q2":4567890123456789,"q1":123} 657 {"q2":0,"q1":0} 658(3 rows) 659 660-- check no-op rowtype conversions 661create temp table tt3 () inherits(tt2); 662insert into tt3 values(33,44); 663select row_to_json(tt3::tt2::tt1) from tt3; 664 row_to_json 665------------------- 666 {"q1":33,"q2":44} 667(1 row) 668 669-- 670-- IS [NOT] NULL should not recurse into nested composites (bug #14235) 671-- 672explain (verbose, costs off) 673select r, r is null as isnull, r is not null as isnotnull 674from (values (1,row(1,2)), (1,row(null,null)), (1,null), 675 (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); 676 QUERY PLAN 677----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 678 Values Scan on "*VALUES*" 679 Output: ROW("*VALUES*".column1, "*VALUES*".column2), (("*VALUES*".column1 IS NULL) AND ("*VALUES*".column2 IS NOT DISTINCT FROM NULL)), (("*VALUES*".column1 IS NOT NULL) AND ("*VALUES*".column2 IS DISTINCT FROM NULL)) 680(2 rows) 681 682select r, r is null as isnull, r is not null as isnotnull 683from (values (1,row(1,2)), (1,row(null,null)), (1,null), 684 (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); 685 r | isnull | isnotnull 686-------------+--------+----------- 687 (1,"(1,2)") | f | t 688 (1,"(,)") | f | t 689 (1,) | f | f 690 (,"(1,2)") | f | f 691 (,"(,)") | f | f 692 (,) | t | f 693(6 rows) 694 695explain (verbose, costs off) 696with r(a,b) as 697 (values (1,row(1,2)), (1,row(null,null)), (1,null), 698 (null,row(1,2)), (null,row(null,null)), (null,null) ) 699select r, r is null as isnull, r is not null as isnotnull from r; 700 QUERY PLAN 701---------------------------------------------------------- 702 CTE Scan on r 703 Output: r.*, (r.* IS NULL), (r.* IS NOT NULL) 704 CTE r 705 -> Values Scan on "*VALUES*" 706 Output: "*VALUES*".column1, "*VALUES*".column2 707(5 rows) 708 709with r(a,b) as 710 (values (1,row(1,2)), (1,row(null,null)), (1,null), 711 (null,row(1,2)), (null,row(null,null)), (null,null) ) 712select r, r is null as isnull, r is not null as isnotnull from r; 713 r | isnull | isnotnull 714-------------+--------+----------- 715 (1,"(1,2)") | f | t 716 (1,"(,)") | f | t 717 (1,) | f | f 718 (,"(1,2)") | f | f 719 (,"(,)") | f | f 720 (,) | t | f 721(6 rows) 722 723-- 724-- Tests for component access / FieldSelect 725-- 726CREATE TABLE compositetable(a text, b text) WITH OIDS; 727INSERT INTO compositetable(a, b) VALUES('fa', 'fb'); 728-- composite type columns can't directly be accessed (error) 729SELECT d.a FROM (SELECT compositetable AS d FROM compositetable) s; 730ERROR: missing FROM-clause entry for table "d" 731LINE 1: SELECT d.a FROM (SELECT compositetable AS d FROM compositeta... 732 ^ 733-- but can be accessed with proper parens 734SELECT (d).a, (d).b FROM (SELECT compositetable AS d FROM compositetable) s; 735 a | b 736----+---- 737 fa | fb 738(1 row) 739 740-- oids can't be accessed in composite types (error) 741SELECT (d).oid FROM (SELECT compositetable AS d FROM compositetable) s; 742ERROR: column "oid" not found in data type compositetable 743LINE 1: SELECT (d).oid FROM (SELECT compositetable AS d FROM composi... 744 ^ 745-- accessing non-existing column in NULL datum errors out 746SELECT (NULL::compositetable).nonexistant; 747ERROR: column "nonexistant" not found in data type compositetable 748LINE 1: SELECT (NULL::compositetable).nonexistant; 749 ^ 750-- existing column in a NULL composite yield NULL 751SELECT (NULL::compositetable).a; 752 a 753--- 754 755(1 row) 756 757-- oids can't be accessed in composite types (error) 758SELECT (NULL::compositetable).oid; 759ERROR: column "oid" not found in data type compositetable 760LINE 1: SELECT (NULL::compositetable).oid; 761 ^ 762DROP TABLE compositetable; 763