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