-- -- insert with DEFAULT in the target_list -- create table inserttest (col1 int4, col2 int4 NOT NULL, col3 text default 'testing'); insert into inserttest (col1, col2, col3) values (DEFAULT, DEFAULT, DEFAULT); ERROR: null value in column "col2" violates not-null constraint DETAIL: Failing row contains (null, null, testing). insert into inserttest (col2, col3) values (3, DEFAULT); insert into inserttest (col1, col2, col3) values (DEFAULT, 5, DEFAULT); insert into inserttest values (DEFAULT, 5, 'test'); insert into inserttest values (DEFAULT, 7); select * from inserttest; col1 | col2 | col3 ------+------+--------- | 3 | testing | 5 | testing | 5 | test | 7 | testing (4 rows) -- -- insert with similar expression / target_list values (all fail) -- insert into inserttest (col1, col2, col3) values (DEFAULT, DEFAULT); ERROR: INSERT has more target columns than expressions LINE 1: insert into inserttest (col1, col2, col3) values (DEFAULT, D... ^ insert into inserttest (col1, col2, col3) values (1, 2); ERROR: INSERT has more target columns than expressions LINE 1: insert into inserttest (col1, col2, col3) values (1, 2); ^ insert into inserttest (col1) values (1, 2); ERROR: INSERT has more expressions than target columns LINE 1: insert into inserttest (col1) values (1, 2); ^ insert into inserttest (col1) values (DEFAULT, DEFAULT); ERROR: INSERT has more expressions than target columns LINE 1: insert into inserttest (col1) values (DEFAULT, DEFAULT); ^ select * from inserttest; col1 | col2 | col3 ------+------+--------- | 3 | testing | 5 | testing | 5 | test | 7 | testing (4 rows) -- -- VALUES test -- insert into inserttest values(10, 20, '40'), (-1, 2, DEFAULT), ((select 2), (select i from (values(3)) as foo (i)), 'values are fun!'); select * from inserttest; col1 | col2 | col3 ------+------+----------------- | 3 | testing | 5 | testing | 5 | test | 7 | testing 10 | 20 | 40 -1 | 2 | testing 2 | 3 | values are fun! (7 rows) -- -- TOASTed value test -- insert into inserttest values(30, 50, repeat('x', 10000)); select col1, col2, char_length(col3) from inserttest; col1 | col2 | char_length ------+------+------------- | 3 | 7 | 5 | 7 | 5 | 4 | 7 | 7 10 | 20 | 2 -1 | 2 | 7 2 | 3 | 15 30 | 50 | 10000 (8 rows) drop table inserttest; -- -- check indirection (field/array assignment), cf bug #14265 -- -- these tests are aware that transformInsertStmt has 3 separate code paths -- create type insert_test_type as (if1 int, if2 text[]); create table inserttest (f1 int, f2 int[], f3 insert_test_type, f4 insert_test_type[]); insert into inserttest (f2[1], f2[2]) values (1,2); insert into inserttest (f2[1], f2[2]) values (3,4), (5,6); insert into inserttest (f2[1], f2[2]) select 7,8; insert into inserttest (f2[1], f2[2]) values (1,default); -- not supported ERROR: cannot set an array element to DEFAULT LINE 1: insert into inserttest (f2[1], f2[2]) values (1,default); ^ insert into inserttest (f3.if1, f3.if2) values (1,array['foo']); insert into inserttest (f3.if1, f3.if2) values (1,'{foo}'), (2,'{bar}'); insert into inserttest (f3.if1, f3.if2) select 3, '{baz,quux}'; insert into inserttest (f3.if1, f3.if2) values (1,default); -- not supported ERROR: cannot set a subfield to DEFAULT LINE 1: insert into inserttest (f3.if1, f3.if2) values (1,default); ^ insert into inserttest (f3.if2[1], f3.if2[2]) values ('foo', 'bar'); insert into inserttest (f3.if2[1], f3.if2[2]) values ('foo', 'bar'), ('baz', 'quux'); insert into inserttest (f3.if2[1], f3.if2[2]) select 'bear', 'beer'; insert into inserttest (f4[1].if2[1], f4[1].if2[2]) values ('foo', 'bar'); insert into inserttest (f4[1].if2[1], f4[1].if2[2]) values ('foo', 'bar'), ('baz', 'quux'); insert into inserttest (f4[1].if2[1], f4[1].if2[2]) select 'bear', 'beer'; select * from inserttest; f1 | f2 | f3 | f4 ----+-------+------------------+------------------------ | {1,2} | | | {3,4} | | | {5,6} | | | {7,8} | | | | (1,{foo}) | | | (1,{foo}) | | | (2,{bar}) | | | (3,"{baz,quux}") | | | (,"{foo,bar}") | | | (,"{foo,bar}") | | | (,"{baz,quux}") | | | (,"{bear,beer}") | | | | {"(,\"{foo,bar}\")"} | | | {"(,\"{foo,bar}\")"} | | | {"(,\"{baz,quux}\")"} | | | {"(,\"{bear,beer}\")"} (16 rows) -- also check reverse-listing create table inserttest2 (f1 bigint, f2 text); create rule irule1 as on insert to inserttest2 do also insert into inserttest (f3.if2[1], f3.if2[2]) values (new.f1,new.f2); create rule irule2 as on insert to inserttest2 do also insert into inserttest (f4[1].if1, f4[1].if2[2]) values (1,'fool'),(new.f1,new.f2); create rule irule3 as on insert to inserttest2 do also insert into inserttest (f4[1].if1, f4[1].if2[2]) select new.f1, new.f2; \d+ inserttest2 Table "public.inserttest2" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description --------+--------+-----------+----------+---------+----------+--------------+------------- f1 | bigint | | | | plain | | f2 | text | | | | extended | | Rules: irule1 AS ON INSERT TO inserttest2 DO INSERT INTO inserttest (f3.if2[1], f3.if2[2]) VALUES (new.f1, new.f2) irule2 AS ON INSERT TO inserttest2 DO INSERT INTO inserttest (f4[1].if1, f4[1].if2[2]) VALUES (1,'fool'::text), (new.f1,new.f2) irule3 AS ON INSERT TO inserttest2 DO INSERT INTO inserttest (f4[1].if1, f4[1].if2[2]) SELECT new.f1, new.f2 drop table inserttest2; drop table inserttest; drop type insert_test_type; -- direct partition inserts should check partition bound constraint create table range_parted ( a text, b int ) partition by range (a, (b+0)); create table part1 partition of range_parted for values from ('a', 1) to ('a', 10); create table part2 partition of range_parted for values from ('a', 10) to ('a', 20); create table part3 partition of range_parted for values from ('b', 1) to ('b', 10); create table part4 partition of range_parted for values from ('b', 10) to ('b', 20); -- fail insert into part1 values ('a', 11); ERROR: new row for relation "part1" violates partition constraint DETAIL: Failing row contains (a, 11). insert into part1 values ('b', 1); ERROR: new row for relation "part1" violates partition constraint DETAIL: Failing row contains (b, 1). -- ok insert into part1 values ('a', 1); -- fail insert into part4 values ('b', 21); ERROR: new row for relation "part4" violates partition constraint DETAIL: Failing row contains (b, 21). insert into part4 values ('a', 10); ERROR: new row for relation "part4" violates partition constraint DETAIL: Failing row contains (a, 10). -- ok insert into part4 values ('b', 10); -- fail (partition key a has a NOT NULL constraint) insert into part1 values (null); ERROR: null value in column "a" violates not-null constraint DETAIL: Failing row contains (null, null). -- fail (expression key (b+0) cannot be null either) insert into part1 values (1); ERROR: new row for relation "part1" violates partition constraint DETAIL: Failing row contains (1, null). create table list_parted ( a text, b int ) partition by list (lower(a)); create table part_aa_bb partition of list_parted FOR VALUES IN ('aa', 'bb'); create table part_cc_dd partition of list_parted FOR VALUES IN ('cc', 'dd'); create table part_null partition of list_parted FOR VALUES IN (null); -- fail insert into part_aa_bb values ('cc', 1); ERROR: new row for relation "part_aa_bb" violates partition constraint DETAIL: Failing row contains (cc, 1). insert into part_aa_bb values ('AAa', 1); ERROR: new row for relation "part_aa_bb" violates partition constraint DETAIL: Failing row contains (AAa, 1). insert into part_aa_bb values (null); ERROR: new row for relation "part_aa_bb" violates partition constraint DETAIL: Failing row contains (null, null). -- ok insert into part_cc_dd values ('cC', 1); insert into part_null values (null, 0); -- check in case of multi-level partitioned table create table part_ee_ff partition of list_parted for values in ('ee', 'ff') partition by range (b); create table part_ee_ff1 partition of part_ee_ff for values from (1) to (10); create table part_ee_ff2 partition of part_ee_ff for values from (10) to (20); -- fail insert into part_ee_ff1 values ('EE', 11); ERROR: new row for relation "part_ee_ff1" violates partition constraint DETAIL: Failing row contains (EE, 11). -- fail (even the parent's, ie, part_ee_ff's partition constraint applies) insert into part_ee_ff1 values ('cc', 1); ERROR: new row for relation "part_ee_ff1" violates partition constraint DETAIL: Failing row contains (cc, 1). -- ok insert into part_ee_ff1 values ('ff', 1); insert into part_ee_ff2 values ('ff', 11); -- Check tuple routing for partitioned tables -- fail insert into range_parted values ('a', 0); ERROR: no partition of relation "range_parted" found for row DETAIL: Failing row contains (a, 0). -- ok insert into range_parted values ('a', 1); insert into range_parted values ('a', 10); -- fail insert into range_parted values ('a', 20); ERROR: no partition of relation "range_parted" found for row DETAIL: Failing row contains (a, 20). -- ok insert into range_parted values ('b', 1); insert into range_parted values ('b', 10); -- fail (partition key (b+0) is null) insert into range_parted values ('a'); ERROR: range partition key of row contains null select tableoid::regclass, * from range_parted; tableoid | a | b ----------+---+---- part1 | a | 1 part1 | a | 1 part2 | a | 10 part3 | b | 1 part4 | b | 10 part4 | b | 10 (6 rows) -- ok insert into list_parted values (null, 1); insert into list_parted (a) values ('aA'); -- fail (partition of part_ee_ff not found in both cases) insert into list_parted values ('EE', 0); ERROR: no partition of relation "part_ee_ff" found for row DETAIL: Failing row contains (EE, 0). insert into part_ee_ff values ('EE', 0); ERROR: no partition of relation "part_ee_ff" found for row DETAIL: Failing row contains (EE, 0). -- ok insert into list_parted values ('EE', 1); insert into part_ee_ff values ('EE', 10); select tableoid::regclass, * from list_parted; tableoid | a | b -------------+----+---- part_aa_bb | aA | part_cc_dd | cC | 1 part_null | | 0 part_null | | 1 part_ee_ff1 | ff | 1 part_ee_ff1 | EE | 1 part_ee_ff2 | ff | 11 part_ee_ff2 | EE | 10 (8 rows) -- cleanup drop table range_parted cascade; NOTICE: drop cascades to 4 other objects DETAIL: drop cascades to table part1 drop cascades to table part2 drop cascades to table part3 drop cascades to table part4 drop table list_parted cascade; NOTICE: drop cascades to 6 other objects DETAIL: drop cascades to table part_aa_bb drop cascades to table part_cc_dd drop cascades to table part_null drop cascades to table part_ee_ff drop cascades to table part_ee_ff1 drop cascades to table part_ee_ff2 -- more tests for certain multi-level partitioning scenarios create table p (a int, b int) partition by range (a, b); create table p1 (b int, a int not null) partition by range (b); create table p11 (like p1); alter table p11 drop a; alter table p11 add a int; alter table p11 drop a; alter table p11 add a int not null; -- attnum for key attribute 'a' is different in p, p1, and p11 select attrelid::regclass, attname, attnum from pg_attribute where attname = 'a' and (attrelid = 'p'::regclass or attrelid = 'p1'::regclass or attrelid = 'p11'::regclass) order by attrelid::regclass::text; attrelid | attname | attnum ----------+---------+-------- p | a | 1 p1 | a | 2 p11 | a | 4 (3 rows) alter table p1 attach partition p11 for values from (2) to (5); alter table p attach partition p1 for values from (1, 2) to (1, 10); -- check that "(1, 2)" is correctly routed to p11. insert into p values (1, 2); select tableoid::regclass, * from p; tableoid | a | b ----------+---+--- p11 | 1 | 2 (1 row) -- cleanup drop table p, p1, p11;