-- Clean up in case a prior regression run failed SET client_min_messages TO'warning'; DROP ROLE IFEXISTS regress_alter_table_user1;
RESET client_min_messages;
-- -- rename - check on both non-temp and temp tables -- CREATETABLE attmp (regtable int); CREATE TEMP TABLE attmp (attmptable int);
ALTERTABLE attmp RENAMETO attmp_new;
SELECT * FROM attmp; SELECT * FROM attmp_new;
ALTERTABLE attmp RENAMETO attmp_new2;
SELECT * FROM attmp; -- should fail SELECT * FROM attmp_new; SELECT * FROM attmp_new2;
DROPTABLE attmp_new; DROPTABLE attmp_new2;
-- check rename of partitioned tables and indexes also CREATETABLE part_attmp (a intprimarykey) partition by range (a); CREATETABLE part_attmp1 PARTITION OF part_attmp FORVALUESFROM (0) TO (100); ALTERINDEX part_attmp_pkey RENAMETO part_attmp_index; ALTERINDEX part_attmp1_pkey RENAMETO part_attmp1_index; ALTERTABLE part_attmp RENAMETO part_at2tmp; ALTERTABLE part_attmp1 RENAMETO part_at2tmp1; SET ROLE regress_alter_table_user1; ALTERINDEX part_attmp_index RENAMETO fail; ALTERINDEX part_attmp1_index RENAMETO fail; ALTERTABLE part_at2tmp RENAMETO fail; ALTERTABLE part_at2tmp1 RENAMETO fail;
RESET ROLE; DROPTABLE part_at2tmp;
-- -- check renaming to a table's array type's autogenerated name -- (the array type's name should get out of the way) -- CREATETABLE attmp_array (id int); CREATETABLE attmp_array2 (id int); SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; SELECT typname FROM pg_type WHERE oid = 'attmp_array2[]'::regtype; ALTERTABLE attmp_array2 RENAMETO _attmp_array; SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; DROPTABLE _attmp_array; DROPTABLE attmp_array;
-- renaming to table's own array type's name is an interesting corner case CREATETABLE attmp_array (id int); SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; ALTERTABLE attmp_array RENAMETO _attmp_array; SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; DROPTABLE _attmp_array;
-- ALTER TABLE ... RENAME on non-table relations -- renaming indexes (FIXME: this should probably test the index's functionality) ALTERINDEXIFEXISTS __onek_unique1 RENAMETO attmp_onek_unique1; ALTERINDEXIFEXISTS __attmp_onek_unique1 RENAMETO onek_unique1;
SET ROLE regress_alter_table_user1; ALTER VIEW attmp_view_new RENAMETO fail; -- permission denied
RESET ROLE;
-- hack to ensure we get an indexscan here set enable_seqscan to off; set enable_bitmapscan to off; -- 5 values, sorted SELECT unique1 FROM tenk1 WHERE unique1 < 5;
reset enable_seqscan;
reset enable_bitmapscan;
DROP VIEW attmp_view_new; -- toast-like relation name altertable stud_emp renameto pg_toast_stud_emp; altertable pg_toast_stud_emp renameto stud_emp;
-- renaming index should rename constraint as well ALTERTABLE onek ADDCONSTRAINT onek_unique1_constraint UNIQUE (unique1); ALTERINDEX onek_unique1_constraint RENAMETO onek_unique1_constraint_foo; ALTERTABLE onek DROPCONSTRAINT onek_unique1_constraint_foo;
-- renaming constraint should rename index as well ALTERTABLE onek ADDCONSTRAINT onek_unique1_constraint UNIQUE (unique1); DROPINDEX onek_unique1_constraint; -- to see whether it's there ALTERTABLE onek RENAMECONSTRAINT onek_unique1_constraint TO onek_unique1_constraint_foo; DROPINDEX onek_unique1_constraint_foo; -- to see whether it's there ALTERTABLE onek DROPCONSTRAINT onek_unique1_constraint_foo;
-- renaming constraints vs. inheritance CREATETABLE constraint_rename_test (a intCONSTRAINT con1 CHECK (a > 0), b int, c int);
\d constraint_rename_test CREATETABLE constraint_rename_test2 (a intCONSTRAINT con1 CHECK (a > 0), d int) INHERITS (constraint_rename_test);
\d constraint_rename_test2 ALTERTABLE constraint_rename_test2 RENAMECONSTRAINT con1 TO con1foo; -- fail ALTERTABLE ONLY constraint_rename_test RENAMECONSTRAINT con1 TO con1foo; -- fail ALTERTABLE constraint_rename_test RENAMECONSTRAINT con1 TO con1foo; -- ok
\d constraint_rename_test
\d constraint_rename_test2 ALTERTABLE constraint_rename_test ADDCONSTRAINT con2 CHECK (b > 0) NO INHERIT; ALTERTABLE ONLY constraint_rename_test RENAMECONSTRAINT con2 TO con2foo; -- ok ALTERTABLE constraint_rename_test RENAMECONSTRAINT con2foo TO con2bar; -- ok
\d constraint_rename_test
\d constraint_rename_test2 ALTERTABLE constraint_rename_test ADDCONSTRAINT con3 PRIMARYKEY (a); ALTERTABLE constraint_rename_test RENAMECONSTRAINT con3 TO con3foo; -- ok
\d constraint_rename_test
\d constraint_rename_test2 DROPTABLE constraint_rename_test2; DROPTABLE constraint_rename_test; ALTERTABLEIFEXISTS constraint_not_exist RENAMECONSTRAINT con3 TO con3foo; -- ok ALTERTABLEIFEXISTS constraint_rename_test ADDCONSTRAINT con4 UNIQUE (a);
-- renaming constraints with cache reset of target relation CREATETABLE constraint_rename_cache (a int, CONSTRAINT chk_a CHECK (a > 0), PRIMARYKEY (a)); ALTERTABLE constraint_rename_cache RENAMECONSTRAINT chk_a TO chk_a_new; ALTERTABLE constraint_rename_cache RENAMECONSTRAINT constraint_rename_cache_pkey TO constraint_rename_pkey_new; CREATETABLE like_constraint_rename_cache
(LIKE constraint_rename_cache INCLUDING ALL);
\d like_constraint_rename_cache DROPTABLE constraint_rename_cache; DROPTABLE like_constraint_rename_cache;
-- Try (and fail) to add constraint due to invalid source columns ALTERTABLE attmp3 addconstraint attmpconstr foreignkey(c) references attmp2 match full;
-- Try (and fail) to add constraint due to invalid destination columns explicitly given ALTERTABLE attmp3 addconstraint attmpconstr foreignkey(a) references attmp2(b) match full;
-- Try (and fail) to add constraint due to invalid data ALTERTABLE attmp3 addconstraint attmpconstr foreignkey (a) references attmp2 match full;
-- Delete failing row DELETEFROM attmp3 where a=5;
-- Try NOT VALID and then VALIDATE CONSTRAINT, but fails. Delete failure then re-validate ALTERTABLE attmp3 addconstraint attmpconstr foreignkey (a) references attmp2 match full NOT VALID; ALTERTABLE attmp3 validate constraint attmpconstr;
-- Delete failing row DELETEFROM attmp3 where a=5;
-- Try (and succeed) and repeat to show it works on already valid constraint ALTERTABLE attmp3 validate constraint attmpconstr; ALTERTABLE attmp3 validate constraint attmpconstr;
-- An already validated constraint must not be revalidated CREATE FUNCTION boo(int) RETURNS int IMMUTABLE STRICT LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'boo: %', $1; RETURN $1; END; $$; INSERTINTO attmp7 VALUES (8, 18); ALTERTABLE attmp7 ADDCONSTRAINT identity CHECK (b = boo(b)); ALTERTABLE attmp3 ADDCONSTRAINT IDENTITY check (b = boo(b)) NOT VALID; ALTERTABLE attmp3 VALIDATE CONSTRAINT identity;
-- A NO INHERIT constraint should not be looked for in children during VALIDATE CONSTRAINT createtable parent_noinh_convalid (a int); createtable child_noinh_convalid () inherits (parent_noinh_convalid); insertinto parent_noinh_convalid values (1); insertinto child_noinh_convalid values (1); altertable parent_noinh_convalid addconstraint check_a_is_2 check (a = 2) no inherit not valid; -- fail, because of the row in parent altertable parent_noinh_convalid validate constraint check_a_is_2; deletefrom only parent_noinh_convalid; -- ok (parent itself contains no violating rows) altertable parent_noinh_convalid validate constraint check_a_is_2; select convalidated from pg_constraint where conrelid = 'parent_noinh_convalid'::regclass and conname = 'check_a_is_2'; -- cleanup droptable parent_noinh_convalid, child_noinh_convalid;
-- Try (and fail) to create constraint from attmp5(a) to attmp4(a) - unique constraint on -- attmp4 is a,b
ALTERTABLE attmp5 addconstraint attmpconstr foreignkey(a) references attmp4(a) match full;
DROPTABLE attmp7;
DROPTABLE attmp6;
DROPTABLE attmp5;
DROPTABLE attmp4;
DROPTABLE attmp3;
DROPTABLE attmp2;
-- NOT VALID with plan invalidation -- ensure we don't use a constraint for -- exclusion until validated set constraint_exclusion TO'partition'; createtable nv_parent (d date, check (false) no inherit not valid); -- not valid constraint added at creation time should automatically become valid
\d nv_parent
createtable nv_child_2010 () inherits (nv_parent); createtable nv_child_2011 () inherits (nv_parent); altertable nv_child_2010 addcheck (d between'2010-01-01'::date and'2010-12-31'::date) not valid; altertable nv_child_2011 addcheck (d between'2011-01-01'::date and'2011-12-31'::date) not valid; explain (costs off) select * from nv_parent where d between'2011-08-01'and'2011-08-31'; createtable nv_child_2009 (check (d between'2009-01-01'::date and'2009-12-31'::date)) inherits (nv_parent); explain (costs off) select * from nv_parent where d between'2011-08-01'::date and'2011-08-31'::date; explain (costs off) select * from nv_parent where d between'2009-08-01'::date and'2009-08-31'::date; -- after validation, the constraint should be used altertable nv_child_2011 VALIDATE CONSTRAINT nv_child_2011_d_check; explain (costs off) select * from nv_parent where d between'2009-08-01'::date and'2009-08-31'::date;
-- add an inherited NOT VALID constraint altertable nv_parent addcheck (d between'2001-01-01'::date and'2099-12-31'::date) not valid;
\d nv_child_2009 -- we leave nv_parent and children around to help test pg_dump logic
-- Foreign key adding test with mixed types
-- Note: these tables are TEMP to avoid name conflicts when this test -- is run in parallel with foreign_key.sql.
CREATE TEMP TABLE PKTABLE (ptest1 intPRIMARYKEY); INSERTINTO PKTABLE VALUES(42); CREATE TEMP TABLE FKTABLE (ftest1 inet); -- This next should fail, because int=inet does not exist ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1) references pktable; -- This should also fail for the same reason, but here we -- give the column name ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1) references pktable(ptest1); DROPTABLE FKTABLE; -- This should succeed, even though they are different types, -- because int=int8 exists and is a member of the integer opfamily CREATE TEMP TABLE FKTABLE (ftest1 int8); ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1) references pktable; -- Check it actually works INSERTINTO FKTABLE VALUES(42); -- should succeed INSERTINTO FKTABLE VALUES(43); -- should fail DROPTABLE FKTABLE; -- This should fail, because we'd have to cast numeric to int which is -- not an implicit coercion (or use numeric=numeric, but that's not part -- of the integer opfamily) CREATE TEMP TABLE FKTABLE (ftest1 numeric); ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1) references pktable; DROPTABLE FKTABLE; DROPTABLE PKTABLE; -- On the other hand, this should work because int implicitly promotes to -- numeric, and we allow promotion on the FK side CREATE TEMP TABLE PKTABLE (ptest1 numericPRIMARYKEY); INSERTINTO PKTABLE VALUES(42); CREATE TEMP TABLE FKTABLE (ftest1 int); ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1) references pktable; -- Check it actually works INSERTINTO FKTABLE VALUES(42); -- should succeed INSERTINTO FKTABLE VALUES(43); -- should fail DROPTABLE FKTABLE; DROPTABLE PKTABLE;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARYKEY(ptest1, ptest2)); -- This should fail, because we just chose really odd types CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1, ftest2) references pktable; DROPTABLE FKTABLE; -- Again, so should this... CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1, ftest2) references pktable(ptest1, ptest2); DROPTABLE FKTABLE; -- This fails because we mixed up the column ordering CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet); ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest1, ftest2) references pktable(ptest2, ptest1); -- As does this... ALTERTABLE FKTABLE ADDFOREIGNKEY(ftest2, ftest1) references pktable(ptest1, ptest2); DROPTABLE FKTABLE; DROPTABLE PKTABLE;
-- Test that ALTER CONSTRAINT updates trigger deferrability properly
ALTERTABLE FKTABLE ADDCONSTRAINT fknd FOREIGNKEY(ftest1) REFERENCES pktable ONDELETECASCADEONUPDATE NO ACTION NOT DEFERRABLE; ALTERTABLE FKTABLE ADDCONSTRAINT fkdd FOREIGNKEY(ftest1) REFERENCES pktable ONDELETECASCADEONUPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; ALTERTABLE FKTABLE ADDCONSTRAINT fkdi FOREIGNKEY(ftest1) REFERENCES pktable ONDELETECASCADEONUPDATE NO ACTION DEFERRABLE INITIALLY IMMEDIATE;
ALTERTABLE FKTABLE ADDCONSTRAINT fknd2 FOREIGNKEY(ftest1) REFERENCES pktable ONDELETECASCADEONUPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; ALTERTABLE FKTABLE ALTERCONSTRAINT fknd2 NOT DEFERRABLE; ALTERTABLE FKTABLE ADDCONSTRAINT fkdd2 FOREIGNKEY(ftest1) REFERENCES pktable ONDELETECASCADEONUPDATE NO ACTION NOT DEFERRABLE; ALTERTABLE FKTABLE ALTERCONSTRAINT fkdd2 DEFERRABLE INITIALLY DEFERRED; ALTERTABLE FKTABLE ADDCONSTRAINT fkdi2 FOREIGNKEY(ftest1) REFERENCES pktable ONDELETECASCADEONUPDATE NO ACTION NOT DEFERRABLE; ALTERTABLE FKTABLE ALTERCONSTRAINT fkdi2 DEFERRABLE INITIALLY IMMEDIATE;
SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint WHERE tgrelid = 'pktable'::regclass ORDERBY1,2,3; SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint WHERE tgrelid = 'fktable'::regclass ORDERBY1,2,3;
-- temp tables should go away by themselves, need not drop them.
-- test check constraint adding
createtable atacc1 ( test int ); -- add a check constraint altertable atacc1 addconstraint atacc_test1 check (test>3); -- should fail insertinto atacc1 (test) values (2); -- should succeed insertinto atacc1 (test) values (4); droptable atacc1;
-- let's do one where the check fails when added createtable atacc1 ( test int ); -- insert a soon to be failing row insertinto atacc1 (test) values (2); -- add a check constraint (fails) altertable atacc1 addconstraint atacc_test1 check (test>3); insertinto atacc1 (test) values (4); droptable atacc1;
-- let's do one where the check fails because the column doesn't exist createtable atacc1 ( test int ); -- add a check constraint (fails) altertable atacc1 addconstraint atacc_test1 check (test1>3); droptable atacc1;
-- something a little more complicated createtable atacc1 ( test int, test2 int, test3 int); -- add a check constraint (fails) altertable atacc1 addconstraint atacc_test1 check (test+test2<test3*4); -- should fail insertinto atacc1 (test,test2,test3) values (4,4,2); -- should succeed insertinto atacc1 (test,test2,test3) values (4,4,5); droptable atacc1;
-- lets do some naming tests createtable atacc1 (test intcheck (test>3), test2 int); altertable atacc1 addcheck (test2>test); -- should fail for $2 insertinto atacc1 (test2, test) values (3, 4); droptable atacc1;
-- inheritance related tests createtable atacc1 (test int); createtable atacc2 (test2 int); createtable atacc3 (test3 int) inherits (atacc1, atacc2); altertable atacc2 addconstraint foo check (test2>0); -- fail and then succeed on atacc2 insertinto atacc2 (test2) values (-3); insertinto atacc2 (test2) values (3); -- fail and then succeed on atacc3 insertinto atacc3 (test2) values (-3); insertinto atacc3 (test2) values (3); droptable atacc3; droptable atacc2; droptable atacc1;
-- same things with one created with INHERIT createtable atacc1 (test int); createtable atacc2 (test2 int); createtable atacc3 (test3 int) inherits (atacc1, atacc2); altertable atacc3 no inherit atacc2; -- fail altertable atacc3 no inherit atacc2; -- make sure it really isn't a child insertinto atacc3 (test2) values (3); select test2 from atacc2; -- fail due to missing constraint altertable atacc2 addconstraint foo check (test2>0); altertable atacc3 inherit atacc2; -- fail due to missing column altertable atacc3 rename test2 to testx; altertable atacc3 inherit atacc2; -- fail due to mismatched data type altertable atacc3 add test2 bool; altertable atacc3 inherit atacc2; altertable atacc3 drop test2; -- succeed altertable atacc3 add test2 int; update atacc3 set test2 = 4where test2 isnull; altertable atacc3 addconstraint foo check (test2>0); altertable atacc3 inherit atacc2; -- fail due to duplicates and circular inheritance altertable atacc3 inherit atacc2; altertable atacc2 inherit atacc3; altertable atacc2 inherit atacc2; -- test that we really are a child now (should see 4 not 3 and cascade should go through) select test2 from atacc2; droptable atacc2 cascade; droptable atacc1;
-- adding only to a parent is allowed as of 9.2
createtable atacc1 (test int); createtable atacc2 (test2 int) inherits (atacc1); -- ok: altertable atacc1 addconstraint foo check (test>0) no inherit; -- check constraint is not there on child insertinto atacc2 (test) values (-3); -- check constraint is there on parent insertinto atacc1 (test) values (-3); insertinto atacc1 (test) values (3); -- fail, violating row: altertable atacc2 addconstraint foo check (test>0) no inherit; droptable atacc2; droptable atacc1;
-- test unique constraint adding
createtable atacc1 ( test int ) ; -- add a unique constraint altertable atacc1 addconstraint atacc_test1 unique (test); -- insert first value insertinto atacc1 (test) values (2); -- should fail insertinto atacc1 (test) values (2); -- should succeed insertinto atacc1 (test) values (4); -- try to create duplicates via alter table using - should fail altertable atacc1 altercolumn test type integerusing0; droptable atacc1;
-- let's do one where the unique constraint fails when added createtable atacc1 ( test int ); -- insert soon to be failing rows insertinto atacc1 (test) values (2); insertinto atacc1 (test) values (2); -- add a unique constraint (fails) altertable atacc1 addconstraint atacc_test1 unique (test); insertinto atacc1 (test) values (3); droptable atacc1;
-- let's do one where the unique constraint fails -- because the column doesn't exist createtable atacc1 ( test int ); -- add a unique constraint (fails) altertable atacc1 addconstraint atacc_test1 unique (test1); droptable atacc1;
-- something a little more complicated createtable atacc1 ( test int, test2 int); -- add a unique constraint altertable atacc1 addconstraint atacc_test1 unique (test, test2); -- insert initial value insertinto atacc1 (test,test2) values (4,4); -- should fail insertinto atacc1 (test,test2) values (4,4); -- should all succeed insertinto atacc1 (test,test2) values (4,5); insertinto atacc1 (test,test2) values (5,4); insertinto atacc1 (test,test2) values (5,5); droptable atacc1;
-- lets do some naming tests createtable atacc1 (test int, test2 int, unique(test)); altertable atacc1 addunique (test2); -- should fail for @@ second one @@ insertinto atacc1 (test2, test) values (3, 3); insertinto atacc1 (test2, test) values (2, 3); droptable atacc1;
-- test primary key constraint adding
createtable atacc1 ( id serial, test int) ; -- add a primary key constraint altertable atacc1 addconstraint atacc_test1 primarykey (test); -- insert first value insertinto atacc1 (test) values (2); -- should fail insertinto atacc1 (test) values (2); -- should succeed insertinto atacc1 (test) values (4); -- inserting NULL should fail insertinto atacc1 (test) values(NULL); -- try adding a second primary key (should fail) altertable atacc1 addconstraint atacc_oid1 primarykey(id); -- drop first primary key constraint altertable atacc1 dropconstraint atacc_test1 restrict; -- try adding a primary key on oid (should succeed) altertable atacc1 addconstraint atacc_oid1 primarykey(id); droptable atacc1;
-- let's do one where the primary key constraint fails when added createtable atacc1 ( test int ); -- insert soon to be failing rows insertinto atacc1 (test) values (2); insertinto atacc1 (test) values (2); -- add a primary key (fails) altertable atacc1 addconstraint atacc_test1 primarykey (test); insertinto atacc1 (test) values (3); droptable atacc1;
-- let's do another one where the primary key constraint fails when added createtable atacc1 ( test int ); -- insert soon to be failing row insertinto atacc1 (test) values (NULL); -- add a primary key (fails) altertable atacc1 addconstraint atacc_test1 primarykey (test); insertinto atacc1 (test) values (3); droptable atacc1;
-- let's do one where the primary key constraint fails -- because the column doesn't exist createtable atacc1 ( test int ); -- add a primary key constraint (fails) altertable atacc1 addconstraint atacc_test1 primarykey (test1); droptable atacc1;
-- adding a new column as primary key to a non-empty table. -- should fail unless the column has a non-null default value. createtable atacc1 ( test int ); insertinto atacc1 (test) values (0); -- add a primary key column without a default (fails). altertable atacc1 addcolumn test2 intprimarykey; -- now add a primary key column with a default (succeeds). altertable atacc1 addcolumn test2 intdefault0primarykey; droptable atacc1;
-- this combination used to have order-of-execution problems (bug #15580) createtable atacc1 (a int); insertinto atacc1 values(1); altertable atacc1 addcolumn b float8notnulldefault random(), addprimarykey(a); droptable atacc1;
-- additionally, we've seen issues with foreign key validation not being -- properly delayed until after a table rewrite. Check that works ok. createtable atacc1 (a intprimarykey); altertable atacc1 addconstraint atacc1_fkey foreignkey (a) references atacc1 (a) not valid; altertable atacc1 validate constraint atacc1_fkey, alter a type bigint; droptable atacc1;
-- we've also seen issues with check constraints being validated at the wrong -- time when there's a pending table rewrite. createtable atacc1 (a bigint, b int); insertinto atacc1 values(1,1); altertable atacc1 addconstraint atacc1_chk check(b = 1) not valid; altertable atacc1 validate constraint atacc1_chk, alter a type int; droptable atacc1;
-- same as above, but ensure the constraint violation is detected createtable atacc1 (a bigint, b int); insertinto atacc1 values(1,2); altertable atacc1 addconstraint atacc1_chk check(b = 1) not valid; altertable atacc1 validate constraint atacc1_chk, alter a type int; droptable atacc1;
-- something a little more complicated createtable atacc1 ( test int, test2 int); -- add a primary key constraint altertable atacc1 addconstraint atacc_test1 primarykey (test, test2); -- try adding a second primary key - should fail altertable atacc1 addconstraint atacc_test2 primarykey (test); -- insert initial value insertinto atacc1 (test,test2) values (4,4); -- should fail insertinto atacc1 (test,test2) values (4,4); insertinto atacc1 (test,test2) values (NULL,3); insertinto atacc1 (test,test2) values (3, NULL); insertinto atacc1 (test,test2) values (NULL,NULL); -- should all succeed insertinto atacc1 (test,test2) values (4,5); insertinto atacc1 (test,test2) values (5,4); insertinto atacc1 (test,test2) values (5,5); droptable atacc1;
-- lets do some naming tests createtable atacc1 (test int, test2 int, primarykey(test)); -- only first should succeed insertinto atacc1 (test2, test) values (3, 3); insertinto atacc1 (test2, test) values (2, 3); insertinto atacc1 (test2, test) values (1, NULL); droptable atacc1;
-- alter table / alter column [set/drop] not null tests -- try altering system catalogs, should fail altertable pg_class altercolumn relname dropnotnull; altertable pg_class alter relname setnotnull;
-- try altering non-existent table, should fail altertable non_existent altercolumn bar setnotnull; altertable non_existent altercolumn bar dropnotnull;
-- test setting columns to null and not null and vice versa -- test checking for null values and primary key createtable atacc1 (test intnotnull); altertable atacc1 addconstraint"atacc1_pkey"primarykey (test);
\d atacc1 altertable atacc1 altercolumn test dropnotnull;
\d atacc1 altertable atacc1 dropconstraint"atacc1_pkey"; altertable atacc1 altercolumn test dropnotnull;
\d atacc1 insertinto atacc1 values (null); altertable atacc1 alter test setnotnull; deletefrom atacc1; altertable atacc1 alter test setnotnull;
-- try altering a non-existent column, should fail altertable atacc1 alter bar setnotnull; altertable atacc1 alter bar dropnotnull;
-- try creating a view and altering that, should fail create view myview asselect * from atacc1; altertable myview altercolumn test dropnotnull; altertable myview altercolumn test setnotnull; drop view myview;
droptable atacc1;
-- set not null verified by constraints createtable atacc1 (test_a int, test_b int); insertinto atacc1 values (null, 1); -- constraint not cover all values, should fail altertable atacc1 addconstraint atacc1_constr_or check(test_a isnotnullor test_b < 10); altertable atacc1 alter test_a setnotnull; altertable atacc1 dropconstraint atacc1_constr_or; -- not valid constraint, should fail altertable atacc1 addconstraint atacc1_constr_invalid check(test_a isnotnull) not valid; altertable atacc1 alter test_a setnotnull; altertable atacc1 dropconstraint atacc1_constr_invalid; -- with valid constraint update atacc1 set test_a = 1; altertable atacc1 addconstraint atacc1_constr_a_valid check(test_a isnotnull); altertable atacc1 alter test_a setnotnull; deletefrom atacc1;
insertinto atacc1 values (2, null); altertable atacc1 alter test_a dropnotnull; -- test multiple set not null at same time -- test_a checked by atacc1_constr_a_valid, test_b should fail by table scan altertable atacc1 alter test_a setnotnull, alter test_b setnotnull; -- commands order has no importance altertable atacc1 alter test_b setnotnull, alter test_a setnotnull;
-- valid one by table scan, one by check constraints update atacc1 set test_b = 1; altertable atacc1 alter test_b setnotnull, alter test_a setnotnull;
altertable atacc1 alter test_a dropnotnull, alter test_b dropnotnull; -- both column has check constraints altertable atacc1 addconstraint atacc1_constr_b_valid check(test_b isnotnull); altertable atacc1 alter test_b setnotnull, alter test_a setnotnull; droptable atacc1;
-- not null not valid with partitions CREATETABLE atnnparted (id int, col1 int) PARTITION BY LIST (id); ALTERTABLE atnnparted ADDCONSTRAINT dummy_constr NOTNULL id NOT VALID; CREATETABLE atnnpart1 (col1 int, id int); ALTERTABLE atnnpart1 ADDCONSTRAINT another_constr NOTNULL id; ALTERTABLE atnnpart1 ADDPRIMARYKEY (id); ALTERTABLE atnnparted ATTACH PARTITION atnnpart1 FORVALUESIN ('1');
\d+ atnnpart*
BEGIN; ALTERTABLE atnnparted VALIDATE CONSTRAINT dummy_constr;
\d+ atnnpart*
ROLLBACK; -- leave a table in this state for the pg_upgrade test
-- test inheritance createtable parent (a int); createtable child (b varchar(255)) inherits (parent);
altertable parent alter a setnotnull; insertinto parent values (NULL); insertinto child (a, b) values (NULL, 'foo'); altertable parent alter a dropnotnull; insertinto parent values (NULL); insertinto child (a, b) values (NULL, 'foo'); altertable only parent alter a setnotnull; altertable child alter a setnotnull; droptable child; droptable parent;
-- set defaults to an incorrect type: this should fail altertable def_test altercolumn c1 setdefault'wrong_datatype'; altertable def_test altercolumn c2 setdefault20;
-- set defaults on a non-existent column: this should fail altertable def_test altercolumn c3 setdefault30;
-- set defaults on views: we need to create a view, add a rule -- to allow insertions into it, and then alter the view to add -- a default create view def_view_test asselect * from def_test; create rule def_view_test_ins as oninsertto def_view_test
do instead insertinto def_test select new.*; insertinto def_view_test defaultvalues; altertable def_view_test altercolumn c1 setdefault45; insertinto def_view_test defaultvalues; altertable def_view_test altercolumn c2 setdefault'view_default'; insertinto def_view_test defaultvalues; select * from def_view_test;
drop rule def_view_test_ins on def_view_test; drop view def_view_test; droptable def_test;
-- alter table / drop column tests -- try altering system catalogs, should fail altertable pg_class dropcolumn relname;
-- test dropping columns createtable atacc1 (a int4notnull, b int4, c int4notnull, d int4); insertinto atacc1 values (1, 2, 3, 4); altertable atacc1 drop a; altertable atacc1 drop a;
-- SELECTs select * from atacc1; select * from atacc1 orderby a; select * from atacc1 orderby"........pg.dropped.1........"; select * from atacc1 groupby a; select * from atacc1 groupby"........pg.dropped.1........"; select atacc1.* from atacc1; select a from atacc1; select atacc1.a from atacc1; select b,c,d from atacc1; select a,b,c,d from atacc1; select * from atacc1 where a = 1; select"........pg.dropped.1........"from atacc1; select atacc1."........pg.dropped.1........"from atacc1; select"........pg.dropped.1........",b,c,d from atacc1; select * from atacc1 where"........pg.dropped.1........" = 1;
-- UPDATEs update atacc1 set a = 3; update atacc1 set b = 2where a = 3; update atacc1 set"........pg.dropped.1........" = 3; update atacc1 set b = 2where"........pg.dropped.1........" = 3;
-- DELETEs deletefrom atacc1 where a = 3; deletefrom atacc1 where"........pg.dropped.1........" = 3; deletefrom atacc1;
-- try dropping a non-existent column, should fail altertable atacc1 drop bar;
-- try removing an oid column, should succeed (as it's nonexistent) altertable atacc1 SET WITHOUT OIDS;
-- try adding an oid column, should fail (not supported) altertable atacc1 SETWITH OIDS;
-- try dropping the xmin column, should fail altertable atacc1 drop xmin;
-- try creating a view and altering that, should fail create view myview asselect * from atacc1; select * from myview; altertable myview drop d; drop view myview;
-- test some commands to make sure they fail on the dropped column analyze atacc1(a); analyze atacc1("........pg.dropped.1........");
vacuum analyze atacc1(a);
vacuum analyze atacc1("........pg.dropped.1........");
comment oncolumn atacc1.a is'testing';
comment oncolumn atacc1."........pg.dropped.1........"is'testing'; altertable atacc1 alter a set storage plain; altertable atacc1 alter"........pg.dropped.1........"set storage plain; altertable atacc1 alter a set statistics 0; altertable atacc1 alter"........pg.dropped.1........"set statistics 0; altertable atacc1 alter a setdefault3; altertable atacc1 alter"........pg.dropped.1........"setdefault3; altertable atacc1 alter a dropdefault; altertable atacc1 alter"........pg.dropped.1........"dropdefault; altertable atacc1 alter a setnotnull; altertable atacc1 alter"........pg.dropped.1........"setnotnull; altertable atacc1 alter a dropnotnull; altertable atacc1 alter"........pg.dropped.1........"dropnotnull; altertable atacc1 rename a to x; altertable atacc1 rename"........pg.dropped.1........"to x; altertable atacc1 addprimarykey(a); altertable atacc1 addprimarykey("........pg.dropped.1........"); altertable atacc1 addunique(a); altertable atacc1 addunique("........pg.dropped.1........"); altertable atacc1 addcheck (a > 3); altertable atacc1 addcheck ("........pg.dropped.1........" > 3); createtable atacc2 (id int4unique); altertable atacc1 addforeignkey (a) references atacc2(id); altertable atacc1 addforeignkey ("........pg.dropped.1........") references atacc2(id); altertable atacc2 addforeignkey (id) references atacc1(a); altertable atacc2 addforeignkey (id) references atacc1("........pg.dropped.1........"); droptable atacc2; createindex"testing_idx"on atacc1(a); createindex"testing_idx"on atacc1("........pg.dropped.1........");
-- test create as and select into insertinto atacc1 values (21, 22, 23); createtable attest1 asselect * from atacc1; select * from attest1; droptable attest1; select * into attest2 from atacc1; select * from attest2; droptable attest2;
-- try dropping all columns altertable atacc1 drop c; altertable atacc1 drop d; altertable atacc1 drop b; select * from atacc1;
droptable atacc1;
-- test constraint error reporting in presence of dropped columns createtable atacc1 (id serial primarykey, value intcheck (value < 10)); insertinto atacc1(value) values (100); altertable atacc1 dropcolumn value; altertable atacc1 addcolumn value intcheck (value < 10); insertinto atacc1(value) values (100); insertinto atacc1(id, value) values (null, 0); droptable atacc1;
-- test inheritance createtable parent (a int, b int, c int); insertinto parent values (1, 2, 3); altertable parent drop a; createtable child (d varchar(255)) inherits (parent); insertinto child values (12, 13, 'testing');
select * from parent; select * from child; altertable parent drop c; select * from parent; select * from child;
droptable child; droptable parent;
-- check error cases for inheritance column merging createtable parent (a float8, b numeric(10,4), c text collate"C");
createtable child (a float4) inherits (parent); -- fail createtable child (b decimal(10,7)) inherits (parent); -- fail createtable child (c text collate"POSIX") inherits (parent); -- fail createtable child (a doubleprecision, b decimal(10,4)) inherits (parent);
droptable child; droptable parent;
-- test copy in/out createtable attest (a int4, b int4, c int4); insertinto attest values (1,2,3); altertable attest drop a;
copy attest to stdout;
copy attest(a) to stdout;
copy attest("........pg.dropped.1........") to stdout;
copy attest from stdin; 101112
\. select * from attest;
copy attest from stdin; 2122
\. select * from attest;
copy attest(a) from stdin;
copy attest("........pg.dropped.1........") from stdin;
copy attest(b,c) from stdin; 3132
\. select * from attest; droptable attest;
-- test inheritance
createtable dropColumn (a int, b int, e int); createtable dropColumnChild (c int) inherits (dropColumn); createtable dropColumnAnother (d int) inherits (dropColumnChild);
-- these two should fail altertable dropColumnchild dropcolumn a; altertable only dropColumnChild dropcolumn b;
-- these three should work altertable only dropColumn dropcolumn e; altertable dropColumnChild dropcolumn c; altertable dropColumn dropcolumn a;
-- these three should fail altertable renameColumnChild renamecolumn a to d; altertable only renameColumnChild renamecolumn a to d; altertable only renameColumn renamecolumn a to d;
-- these should work altertable renameColumn renamecolumn a to d; altertable renameColumnChild renamecolumn b to a;
-- these should work altertableifexists doesnt_exist_tab renamecolumn a to d; altertableifexists doesnt_exist_tab renamecolumn b to a;
-- this should work altertable renameColumn addcolumn w int;
-- this should fail altertable only renameColumn addcolumn x int;
-- this should work altertable renameColumn addcolumn x intcheck (x > 0) not enforced;
-- this should fail altertable renameColumn addcolumn y intcheck (x > 0) not enforced enforced;
-- Test corner cases in dropping of inherited columns
-- should be rejected since c1.f1 is inherited altertable c1 dropcolumn f1; -- should work altertable p1 dropcolumn f1; -- c1.f1 is still there, but no longer inherited select f1 from c1; altertable c1 dropcolumn f1; select f1 from c1;
-- should be rejected since c1.f1 is inherited altertable c1 dropcolumn f1; altertable p1 dropcolumn f1; -- c1.f1 is dropped now, since there is no local definition for it select f1 from c1;
-- should be rejected since c1.f1 is inherited altertable c1 dropcolumn f1; altertable only p1 dropcolumn f1; -- c1.f1 is NOT dropped, but must now be considered non-inherited altertable c1 dropcolumn f1;
-- should be rejected since c1.f1 is inherited altertable c1 dropcolumn f1; altertable only p1 dropcolumn f1; -- c1.f1 is still there, but no longer inherited altertable c1 dropcolumn f1;
droptable p1 cascade;
createtable p1(id int, name text); createtable p2(id2 int, name text, height int); createtable c1(age int) inherits(p1,p2); createtable gc1() inherits (c1);
select relname, attname, attinhcount, attislocal from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid) where relname in ('p1','p2','c1','gc1') and attnum > 0andnot attisdropped orderby relname, attnum;
-- should work altertable only p1 dropcolumn name; -- should work. Now c1.name is local and inhcount is 0. altertable p2 dropcolumn name; -- should be rejected since its inherited altertable gc1 dropcolumn name; -- should work, and drop gc1.name along altertable c1 dropcolumn name; -- should fail: column does not exist altertable gc1 dropcolumn name; -- should work and drop the attribute in all tables altertable p2 dropcolumn height;
-- IF EXISTS test createtable dropColumnExists (); altertable dropColumnExists dropcolumn non_existing; --fail altertable dropColumnExists dropcolumnifexists non_existing; --succeed
select relname, attname, attinhcount, attislocal from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid) where relname in ('p1','p2','c1','gc1') and attnum > 0andnot attisdropped orderby relname, attnum;
altertable anothertab altercolumn atcol2 type text usingcasewhen atcol2 istruethen'IT WAS TRUE' when atcol2 isfalsethen'IT WAS FALSE' else'IT WAS NULL!' end;
-- Test index handling in alter table column type (cf. bugs #15835, #15865) createtable anothertab(f1 intprimarykey, f2 intunique,
f3 int, f4 int, f5 int); altertable anothertab add exclude using btree (f3 with =); altertable anothertab add exclude using btree (f4 with =) where (f4 isnotnull); altertable anothertab add exclude using btree (f4 with =) where (f5 > 0); altertable anothertab addunique(f1,f4); createindexon anothertab(f2,f3); createuniqueindexon anothertab(f4);
\d anothertab altertable anothertab altercolumn f1 type bigint; altertable anothertab altercolumn f2 type bigint, altercolumn f3 type bigint, altercolumn f4 type bigint; altertable anothertab altercolumn f5 type bigint;
\d anothertab
droptable anothertab;
-- test that USING expressions are parsed before column alter type / drop steps createtable another (f1 int, f2 text, f3 text);
insertinto another values(1, 'one', 'uno'); insertinto another values(2, 'two', 'due'); insertinto another values(3, 'three', 'tre');
select * from another;
altertable another alter f1 type text using f2 || ' and ' || f3 || ' more', alter f2 type bigintusing f1 * 10, dropcolumn f3;
select * from another;
droptable another;
-- Create an index that skips WAL, then perform a SET DATA TYPE that skips -- rewriting the index.
begin; createtable skip_wal_skip_rewrite_index (c varchar(10) primarykey); altertable skip_wal_skip_rewrite_index alter c type varchar(20); commit;
-- We disallow changing table's row type if it's used for storage createtable at_tab1 (a int, b text); createtable at_tab2 (x int, y at_tab1); altertable at_tab1 altercolumn b type varchar; -- fails droptable at_tab2; -- Use of row type in an expression is defended differently createtable at_tab2 (x int, y text, check((x,y)::at_tab1 = (1,'42')::at_tab1)); altertable at_tab1 altercolumn b type varchar; -- allowed, but ... insertinto at_tab2 values(1,'42'); -- ... this will fail droptable at_tab1, at_tab2; -- Check it for a partitioned table, too createtable at_tab1 (a int, b text) partition by list(a); createtable at_tab2 (x int, y at_tab1); altertable at_tab1 altercolumn b type varchar; -- fails droptable at_tab1, at_tab2;
-- Alter column type that's part of a partitioned index createtable at_partitioned (a int, b text) partition by range (a); createtable at_part_1 partition of at_partitioned forvaluesfrom (0) to (1000); insertinto at_partitioned values (512, '0.123'); createtable at_part_2 (b text, a int); insertinto at_part_2 values ('1.234', 1024); createindexon at_partitioned (b); createindexon at_partitioned (a);
\d at_part_1
\d at_part_2 altertable at_partitioned attach partition at_part_2 forvaluesfrom (1000) to (2000);
\d at_part_2 altertable at_partitioned altercolumn b type numericusing b::numeric;
\d at_part_1
\d at_part_2 droptable at_partitioned;
-- Alter column type when no table rewrite is required -- Also check that comments are preserved createtable at_partitioned(id int, name varchar(64), unique (id, name))
partition by hash(id);
comment onconstraint at_partitioned_id_name_key on at_partitioned is'parent constraint';
comment onindex at_partitioned_id_name_key is'parent index'; createtable at_partitioned_0 partition of at_partitioned forvalueswith (modulus 2, remainder 0);
comment onconstraint at_partitioned_0_id_name_key on at_partitioned_0 is'child 0 constraint';
comment onindex at_partitioned_0_id_name_key is'child 0 index'; createtable at_partitioned_1 partition of at_partitioned forvalueswith (modulus 2, remainder 1);
comment onconstraint at_partitioned_1_id_name_key on at_partitioned_1 is'child 1 constraint';
comment onindex at_partitioned_1_id_name_key is'child 1 index'; insertinto at_partitioned values(1, 'foo'); insertinto at_partitioned values(3, 'bar');
create temp table old_oids as select relname, oid as oldoid, relfilenode as oldfilenode from pg_class where relname like'at_partitioned%';
select relname,
c.oid = oldoid as orig_oid, case relfilenode when0then'none' when c.oid then'own' when oldfilenode then'orig' else'OTHER'
end as storage,
obj_description(c.oid, 'pg_class') asdesc from pg_class c leftjoin old_oids using (relname) where relname like'at_partitioned%' orderby relname;
select conname, obj_description(oid, 'pg_constraint') asdesc from pg_constraint where conname like'at_partitioned%' orderby conname;
altertable at_partitioned altercolumn name type varchar(127);
select relname,
c.oid = oldoid as orig_oid, case relfilenode when0then'none' when c.oid then'own' when oldfilenode then'orig' else'OTHER'
end as storage,
obj_description(c.oid, 'pg_class') asdesc from pg_class c leftjoin old_oids using (relname) where relname like'at_partitioned%' orderby relname;
select conname, obj_description(oid, 'pg_constraint') asdesc from pg_constraint where conname like'at_partitioned%' orderby conname;
-- Don't remove this DROP, it exposes bug #15672 droptable at_partitioned;
-- SET STORAGE may need to add a TOAST table createtable test_storage (a text, c text storage plain); select reltoastrelid <> 0as has_toast_table from pg_class where oid = 'test_storage'::regclass; altertable test_storage alter a set storage plain; -- rewrite table to remove its TOAST table; need a non-constant column default altertable test_storage add b intdefault random()::int; select reltoastrelid <> 0as has_toast_table from pg_class where oid = 'test_storage'::regclass; altertable test_storage alter a set storage default; -- re-add TOAST table select reltoastrelid <> 0as has_toast_table from pg_class where oid = 'test_storage'::regclass;
-- check STORAGE correctness createtable test_storage_failed (a text, b int storage extended);
-- test that SET STORAGE propagates to index correctly createindex test_storage_idx on test_storage (b, a); altertable test_storage altercolumn a set storage external;
\d+ test_storage
\d+ test_storage_idx
-- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779) CREATETABLE test_inh_check (a floatcheck (a > 10.2), b float); CREATETABLE test_inh_check_child() INHERITS(test_inh_check);
\d test_inh_check
\d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like'test_inh_check%'and c.conrelid = r.oid orderby1, 2; ALTERTABLE test_inh_check ALTERCOLUMN a TYPE numeric;
\d test_inh_check
\d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like'test_inh_check%'and c.conrelid = r.oid orderby1, 2; -- also try noinherit, local, and local+inherited cases ALTERTABLE test_inh_check ADDCONSTRAINT bnoinherit CHECK (b > 100) NO INHERIT; ALTERTABLE test_inh_check_child ADDCONSTRAINT blocal CHECK (b < 1000); ALTERTABLE test_inh_check_child ADDCONSTRAINT bmerged CHECK (b > 1); ALTERTABLE test_inh_check ADDCONSTRAINT bmerged CHECK (b > 1);
\d test_inh_check
\d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like'test_inh_check%'and c.conrelid = r.oid orderby1, 2; ALTERTABLE test_inh_check ALTERCOLUMN b TYPE numeric;
\d test_inh_check
\d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like'test_inh_check%'and c.conrelid = r.oid orderby1, 2;
-- ALTER COLUMN TYPE with different schema in children -- Bug at https://postgr.es/m/20170102225618.GA10071@telsasoft.com CREATETABLE test_type_diff (f1 int); CREATETABLE test_type_diff_c (extra smallint) INHERITS (test_type_diff); ALTERTABLE test_type_diff ADDCOLUMN f2 int; INSERTINTO test_type_diff_c VALUES (1, 2, 3); ALTERTABLE test_type_diff ALTERCOLUMN f2 TYPE bigintUSING f2::bigint;
-- check for rollback of ANALYZE corrupting table property flags (bug #11638) CREATETABLE check_fk_presence_1 (id intPRIMARYKEY, t text); CREATETABLE check_fk_presence_2 (id intREFERENCES check_fk_presence_1, t text);
BEGIN; ALTERTABLE check_fk_presence_2 DROPCONSTRAINT check_fk_presence_2_id_fkey; ANALYZE check_fk_presence_2;
ROLLBACK;
\d check_fk_presence_2 DROPTABLE check_fk_presence_1, check_fk_presence_2;
-- check column addition within a view (bug #14876) createtable at_base_table(id int, stuff text); insertinto at_base_table values (23, 'skidoo'); create view at_view_1 asselect * from at_base_table bt; create view at_view_2 asselect *, to_json(v1) as j from at_view_1 v1;
\d+ at_view_1
\d+ at_view_2 explain (verbose, costs off) select * from at_view_2; select * from at_view_2;
createorreplace view at_view_1 asselect *, 2+2as more from at_base_table bt;
\d+ at_view_1
\d+ at_view_2 explain (verbose, costs off) select * from at_view_2; select * from at_view_2;
drop view at_view_2; drop view at_view_1; droptable at_base_table;
-- related case (bug #17811)
begin; create temp table t1 asselect * from int8_tbl; create temp view v1 asselect1::int8as q1; create temp view v2 asselect * from v1; createorreplace temp view v1 with (security_barrier = true) asselect * from t1;
create temp table log (q1 int8, q2 int8); create rule v1_upd_rule asonupdateto v1
do also insertinto log values (new.*);
update v2 set q1 = q1 + 1where q1 = 123;
select * from t1; select * from log;
rollback;
-- check adding a column not itself requiring a rewrite, together with -- a column requiring a default (bug #16038)
-- ensure that rewrites aren't silently optimized away, removing the -- value of the test CREATE FUNCTION check_ddl_rewrite(p_tablename regclass, p_ddl text)
RETURNS boolean
LANGUAGE plpgsql AS $$ DECLARE
v_relfilenode oid;
BEGIN
v_relfilenode := relfilenode FROM pg_class WHERE oid = p_tablename;
EXECUTE p_ddl;
RETURN v_relfilenode <> (SELECT relfilenode FROM pg_class WHERE oid = p_tablename);
END;
$$;
-- empty[12] don't need rewrite, but notempty[12]_rewrite will force one SELECT check_ddl_rewrite('rewrite_test', $$ ALTERTABLE rewrite_test ADDCOLUMN empty1 text, ADDCOLUMN notempty1_rewrite serial;
$$); SELECT check_ddl_rewrite('rewrite_test', $$ ALTERTABLE rewrite_test ADDCOLUMN notempty2_rewrite serial, ADDCOLUMN empty2 text;
$$); -- also check that fast defaults cause no problem, first without rewrite SELECT check_ddl_rewrite('rewrite_test', $$ ALTERTABLE rewrite_test ADDCOLUMN empty3 text, ADDCOLUMN notempty3_norewrite intdefault42;
$$); SELECT check_ddl_rewrite('rewrite_test', $$ ALTERTABLE rewrite_test ADDCOLUMN notempty4_norewrite intdefault42, ADDCOLUMN empty4 text;
$$); -- then with rewrite SELECT check_ddl_rewrite('rewrite_test', $$ ALTERTABLE rewrite_test ADDCOLUMN empty5 text, ADDCOLUMN notempty5_norewrite intdefault42, ADDCOLUMN notempty5_rewrite serial;
$$); SELECT check_ddl_rewrite('rewrite_test', $$ ALTERTABLE rewrite_test ADDCOLUMN notempty6_rewrite serial, ADDCOLUMN empty6 text, ADDCOLUMN notempty6_norewrite intdefault42;
$$);
-- cleanup DROP FUNCTION check_ddl_rewrite(regclass, text); DROPTABLE rewrite_test;
-- -- lock levels -- drop type lockmodes; create type lockmodes as enum ( 'SIReadLock'
,'AccessShareLock'
,'RowShareLock'
,'RowExclusiveLock'
,'ShareUpdateExclusiveLock'
,'ShareLock'
,'ShareRowExclusiveLock'
,'ExclusiveLock'
,'AccessExclusiveLock'
);
drop view my_locks; createorreplace view my_locks as selectcasewhen c.relname like'pg_toast%'then'pg_toast'else c.relname end, max(mode::lockmodes) as max_lockmode from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks where transactionid = pg_current_xact_id()::xid) and locktype = 'relation' and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') and c.relname != 'my_locks' groupby c.relname;
begin; altertable alterlock set (toast.autovacuum_enabled = off); select * from my_locks orderby1; commit;
begin; altertable alterlock set (autovacuum_enabled = off); select * from my_locks orderby1; commit;
begin; altertable alterlock altercolumn f2 set (n_distinct = 1); select * from my_locks orderby1;
rollback;
-- test that mixing options with different lock levels works as expected
begin; altertable alterlock set (autovacuum_enabled = off, fillfactor = 80); select * from my_locks orderby1; commit;
begin; altertable alterlock altercolumn f2 set storage extended; select * from my_locks orderby1;
rollback;
begin; create function ttdummy () returns trigger language plpgsql as
$$ begin return new; end $$; createtrigger ttdummy beforedeleteorupdateon alterlock foreach row
execute procedure
ttdummy (1, 1); select * from my_locks orderby1;
rollback;
begin; select * from my_locks orderby1; altertable alterlock2 addforeignkey (f1) references alterlock (f1); select * from my_locks orderby1;
rollback;
begin; altertable alterlock2 addconstraint alterlock2nv foreignkey (f1) references alterlock (f1) NOT VALID; select * from my_locks orderby1; commit;
begin; altertable alterlock2 validate constraint alterlock2nv; select * from my_locks orderby1;
rollback;
createorreplace view my_locks as selectcasewhen c.relname like'pg_toast%'then'pg_toast'else c.relname end, max(mode::lockmodes) as max_lockmode from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks where transactionid = pg_current_xact_id()::xid) and locktype = 'relation' and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') and c.relname = 'my_locks' groupby c.relname;
-- raise exception altertable my_locks set (autovacuum_enabled = false); alter view my_locks set (autovacuum_enabled = false); altertable my_locks reset (autovacuum_enabled); alter view my_locks reset (autovacuum_enabled);
begin; alter view my_locks set (security_barrier=off); select * from my_locks orderby1; alter view my_locks reset (security_barrier);
rollback;
-- this test intentionally applies the ALTER TABLE command against a view, but -- uses a view option so we expect this to succeed. This form of SQL is -- accepted for historical reasons, as shown in the docs for ALTER VIEW
begin; altertable my_locks set (security_barrier=off); select * from my_locks orderby1; altertable my_locks reset (security_barrier);
rollback;
-- cleanup droptable alterlock2; droptable alterlock; drop view my_locks; drop type lockmodes;
-- -- alter function -- create function test_strict(text) returns text as 'select coalesce($1, ''got passed a null'');'
language sql returns nullonnull input; select test_strict(NULL); alter function test_strict(text) called onnull input; select test_strict(NULL);
create function non_strict(text) returns text as 'select coalesce($1, ''got passed a null'');'
language sql called onnull input; select non_strict(NULL); alter function non_strict(text) returns nullonnull input; select non_strict(NULL);
-- -- alter object set schema --
createschema alter1; createschema alter2;
createtable alter1.t1(f1 serial primarykey, f2 intcheck (f2 > 0));
create view alter1.v1 asselect * from alter1.t1;
create function alter1.plus1(int) returns intas'select $1+1' language sql;
create function alter1.same(alter1.ctype, alter1.ctype) returns boolean language sql as'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2';
altertable alter1.t1 setschema alter1; -- no-op, same schema altertable alter1.t1 setschema alter2; altertable alter1.v1 setschema alter2; alter function alter1.plus1(int) setschema alter2; alter domain alter1.posint setschema alter2; alter operator class alter1.ctype_hash_ops using hash setschema alter2; alter operator family alter1.ctype_hash_ops using hash setschema alter2; alter operator alter1.=(alter1.ctype, alter1.ctype) setschema alter2; alter function alter1.same(alter1.ctype, alter1.ctype) setschema alter2; alter type alter1.ctype setschema alter1; -- no-op, same schema alter type alter1.ctype setschema alter2; alter conversion alter1.latin1_to_utf8 setschema alter2; alter text search parser alter1.prs setschema alter2; alter text search configuration alter1.cfg setschema alter2; alter text search template alter1.tmpl setschema alter2; alter text search dictionary alter1.dict setschema alter2;
-- this should succeed because nothing is left in alter1 dropschema alter1;
ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails
ALTER TYPE test_type ADD ATTRIBUTE b text;
\d test_type
ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails
ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar;
\d test_type
ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer;
\d test_type
ALTER TYPE test_type DROP ATTRIBUTE b;
\d test_type
ALTER TYPE test_type DROP ATTRIBUTE c; -- fails
ALTER TYPE test_type DROP ATTRIBUTE IFEXISTS c;
ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean;
\d test_type
ALTER TYPE test_type RENAME ATTRIBUTE a TO aa; ALTER TYPE test_type RENAME ATTRIBUTE d TO dd;
\d test_type
DROP TYPE test_type;
CREATE TYPE test_type1 AS (a int, b text); CREATETABLE test_tbl1 (x int, y test_type1); ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
DROPTABLE test_tbl1; CREATETABLE test_tbl1 (x int, y text); CREATEINDEX test_tbl1_idx ON test_tbl1((row(x,y)::test_type1)); ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
DROPTABLE test_tbl1; DROP TYPE test_type1;
CREATE TYPE test_type2 AS (a int, b text); CREATETABLE test_tbl2 OF test_type2; CREATETABLE test_tbl2_subclass () INHERITS (test_tbl2);
\d test_type2
\d test_tbl2
ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails ALTER TYPE test_type2 ADD ATTRIBUTE c text CASCADE;
\d test_type2
\d test_tbl2
ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varcharCASCADE;
\d test_type2
\d test_tbl2
ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails ALTER TYPE test_type2 DROP ATTRIBUTE b CASCADE;
\d test_type2
\d test_tbl2
ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa; -- fails ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa CASCADE;
\d test_type2
\d test_tbl2
\d test_tbl2_subclass
DROPTABLE test_tbl2_subclass, test_tbl2; DROP TYPE test_type2;
CREATE TYPE test_typex AS (a int, b text); CREATETABLE test_tblx (x int, y test_typex check ((y).a > 0)); ALTER TYPE test_typex DROP ATTRIBUTE a; -- fails ALTER TYPE test_typex DROP ATTRIBUTE a CASCADE;
\d test_tblx DROPTABLE test_tblx; DROP TYPE test_typex;
-- This test isn't that interesting on its own, but the purpose is to leave -- behind a table to test pg_upgrade with. The table has a composite type -- column in it, and the composite type has a dropped attribute. CREATE TYPE test_type3 AS (a int); CREATETABLE test_tbl3 (c) ASSELECT'(1)'::test_type3; ALTER TYPE test_type3 DROP ATTRIBUTE a, ADD ATTRIBUTE b int;
CREATE TYPE test_type_empty AS (); DROP TYPE test_type_empty;
-- -- typed tables: OF / NOT OF --
CREATE TYPE tt_t0 AS (z inet, x int, y numeric(8,2)); ALTER TYPE tt_t0 DROP ATTRIBUTE z; CREATETABLE tt0 (x intNOTNULL, y numeric(8,2)); -- OK CREATETABLE tt1 (x int, y bigint); -- wrong base type CREATETABLE tt2 (x int, y numeric(9,2)); -- wrong typmod CREATETABLE tt3 (y numeric(8,2), x int); -- wrong column order CREATETABLE tt4 (x int); -- too few columns CREATETABLE tt5 (x int, y numeric(8,2), z int); -- too few columns CREATETABLE tt6 () INHERITS (tt0); -- can't have a parent CREATETABLE tt7 (x int, q text, y numeric(8,2)); ALTERTABLE tt7 DROP q; -- OK
ALTERTABLE tt0 OF tt_t0; ALTERTABLE tt1 OF tt_t0; ALTERTABLE tt2 OF tt_t0; ALTERTABLE tt3 OF tt_t0; ALTERTABLE tt4 OF tt_t0; ALTERTABLE tt5 OF tt_t0; ALTERTABLE tt6 OF tt_t0; ALTERTABLE tt7 OF tt_t0;
CREATE TYPE tt_t1 AS (x int, y numeric(8,2)); ALTERTABLE tt7 OF tt_t1; -- reassign an already-typed table ALTERTABLE tt7 NOT OF;
\d tt7
-- make sure we can drop a constraint on the parent but it remains on the child CREATETABLE test_drop_constr_parent (c text CHECK (c ISNOTNULL)); CREATETABLE test_drop_constr_child () INHERITS (test_drop_constr_parent); ALTERTABLE ONLY test_drop_constr_parent DROPCONSTRAINT"test_drop_constr_parent_c_check"; -- should fail INSERTINTO test_drop_constr_child (c) VALUES (NULL); DROPTABLE test_drop_constr_parent CASCADE;
-- -- IF EXISTS test -- ALTERTABLEIFEXISTS tt8 ADDCOLUMN f int; ALTERTABLEIFEXISTS tt8 ADDCONSTRAINT xxx PRIMARYKEY(f); ALTERTABLEIFEXISTS tt8 ADDCHECK (f BETWEEN0AND10); ALTERTABLEIFEXISTS tt8 ALTERCOLUMN f SETDEFAULT0; ALTERTABLEIFEXISTS tt8 RENAMECOLUMN f TO f1; ALTERTABLEIFEXISTS tt8 SETSCHEMA alter2;
CREATETABLE tt8(a int); CREATESCHEMA alter2;
ALTERTABLEIFEXISTS tt8 ADDCOLUMN f int; ALTERTABLEIFEXISTS tt8 ADDCONSTRAINT xxx PRIMARYKEY(f); ALTERTABLEIFEXISTS tt8 ADDCHECK (f BETWEEN0AND10); ALTERTABLEIFEXISTS tt8 ALTERCOLUMN f SETDEFAULT0; ALTERTABLEIFEXISTS tt8 RENAMECOLUMN f TO f1; ALTERTABLEIFEXISTS tt8 SETSCHEMA alter2;
-- Check that comments on constraints and indexes are not lost at ALTER TABLE. CREATETABLE comment_test (
id int, constraint id_notnull_constraint notnull id,
positive_col intCHECK (positive_col > 0),
indexed_col int, CONSTRAINT comment_test_pk PRIMARYKEY (id)); CREATEINDEX comment_test_index ON comment_test(indexed_col);
COMMENT ONCOLUMN comment_test.id IS'Column ''id'' on comment_test';
COMMENT ONINDEX comment_test_index IS'Simple index on comment_test';
COMMENT ONCONSTRAINT comment_test_positive_col_check ON comment_test IS'CHECK constraint on comment_test.positive_col';
COMMENT ONCONSTRAINT comment_test_pk ON comment_test IS'PRIMARY KEY constraint of comment_test';
COMMENT ONCONSTRAINT id_notnull_constraint ON comment_test IS'NOT NULL constraint of comment_test';
COMMENT ONINDEX comment_test_pk IS'Index backing the PRIMARY KEY of comment_test';
SELECT col_description('comment_test'::regclass, 1) as comment; SELECT indexrelid::regclass::text asindex, obj_description(indexrelid, 'pg_class') ascomment FROM pg_index where indrelid = 'comment_test'::regclass ORDERBY1, 2; SELECT conname asconstraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass ORDERBY1, 2;
-- Change the datatype of all the columns. ALTER TABLE is optimized to not -- rebuild an index if the new data type is binary compatible with the old -- one. Check do a dummy ALTER TABLE that doesn't change the datatype -- first, to test that no-op codepath, and another one that does. ALTERTABLE comment_test ALTERCOLUMN indexed_col SET DATA TYPE int; ALTERTABLE comment_test ALTERCOLUMN indexed_col SET DATA TYPE text; ALTERTABLE comment_test ALTERCOLUMN id SET DATA TYPE int; ALTERTABLE comment_test ALTERCOLUMN id SET DATA TYPE text; ALTERTABLE comment_test ALTERCOLUMN positive_col SET DATA TYPE int; ALTERTABLE comment_test ALTERCOLUMN positive_col SET DATA TYPE bigint;
-- Some error cases. ALTERTABLE comment_test ALTERCOLUMN xmin SET DATA TYPE x; ALTERTABLE comment_test ALTERCOLUMN id SET DATA TYPE x; ALTERTABLE comment_test ALTERCOLUMN id SET DATA TYPE intCOLLATE"C";
-- Check that the comments are intact. SELECT col_description('comment_test'::regclass, 1) as comment; SELECT indexrelid::regclass::text asindex, obj_description(indexrelid, 'pg_class') ascomment FROM pg_index where indrelid = 'comment_test'::regclass ORDERBY1, 2; SELECT conname asconstraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass ORDERBY1, 2;
-- Check compatibility for foreign keys and comments. This is done -- separately as rebuilding the column type of the parent leads -- to an error and would reduce the test scope. CREATETABLE comment_test_child (
id text CONSTRAINT comment_test_child_fk REFERENCES comment_test); CREATEINDEX comment_test_child_fk ON comment_test_child(id);
COMMENT ONCOLUMN comment_test_child.id IS'Column ''id'' on comment_test_child';
COMMENT ONINDEX comment_test_child_fk IS'Index backing the FOREIGN KEY of comment_test_child';
COMMENT ONCONSTRAINT comment_test_child_fk ON comment_test_child IS'FOREIGN KEY constraint of comment_test_child';
-- Change column type of parent ALTERTABLE comment_test ALTERCOLUMN id SET DATA TYPE text; ALTERTABLE comment_test ALTERCOLUMN id SET DATA TYPE intUSING id::integer;
-- Comments should be intact SELECT col_description('comment_test_child'::regclass, 1) as comment; SELECT indexrelid::regclass::text asindex, obj_description(indexrelid, 'pg_class') ascomment FROM pg_index where indrelid = 'comment_test_child'::regclass ORDERBY1, 2; SELECT conname asconstraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test_child'::regclass ORDERBY1, 2;
-- Check that we map relation oids to filenodes and back correctly. Only -- display bad mappings so the test output doesn't change all the time. A -- filenode function call can return NULL for a relation dropped concurrently -- with the call's surrounding query, so ignore a NULL mapped_oid for -- relations that no longer exist after all calls finish. -- Temporary relations are ignored, as not supported by pg_filenode_relation(). CREATE TEMP TABLE filenode_mapping AS SELECT
oid, mapped_oid, reltablespace, relfilenode, relname FROM pg_class,
pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) AS mapped_oid WHERE relkind IN ('r', 'i', 'S', 't', 'm') AND relpersistence != 't' AND mapped_oid ISDISTINCTFROM oid; SELECT m.* FROM filenode_mapping m LEFTJOIN pg_class c ON c.oid = m.oid WHERE c.oid ISNOTNULLOR m.mapped_oid ISNOTNULL;
-- Checks on creating and manipulation of user defined relations in -- pg_catalog.
SHOW allow_system_table_mods; -- disallowed because of search_path issues with pg_dump CREATETABLE pg_catalog.new_system_table(); -- instead create in public first, move to catalog CREATETABLE new_system_table(id serial primarykey, othercol text); ALTERTABLE new_system_table SETSCHEMA pg_catalog; ALTERTABLE new_system_table SETSCHEMA public; ALTERTABLE new_system_table SETSCHEMA pg_catalog; -- will be ignored -- already there: ALTERTABLE new_system_table SETSCHEMA pg_catalog; ALTERTABLE new_system_table RENAMETO old_system_table; CREATEINDEX old_system_table__othercol ON old_system_table (othercol); INSERTINTO old_system_table(othercol) VALUES ('somedata'), ('otherdata'); UPDATE old_system_table SET id = -id; DELETEFROM old_system_table WHERE othercol = 'somedata';
TRUNCATE old_system_table; ALTERTABLE old_system_table DROPCONSTRAINT new_system_table_pkey; ALTERTABLE old_system_table DROPCOLUMN othercol; DROPTABLE old_system_table;
-- set logged CREATE UNLOGGED TABLE unlogged1(f1 SERIAL PRIMARYKEY, f2 TEXT); -- has sequence, toast -- check relpersistence of an unlogged table SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' UNIONALL SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' UNIONALL SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' ORDERBY relname; CREATE UNLOGGED TABLE unlogged2(f1 SERIAL PRIMARYKEY, f2 INTEGERREFERENCES unlogged1); -- foreign key CREATE UNLOGGED TABLE unlogged3(f1 SERIAL PRIMARYKEY, f2 INTEGERREFERENCES unlogged3); -- self-referencing foreign key ALTERTABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key ALTERTABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists ALTERTABLE unlogged1 SET LOGGED; -- check relpersistence of an unlogged table after changing to permanent SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' UNIONALL SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' UNIONALL SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' ORDERBY relname; ALTERTABLE unlogged1 SET LOGGED; -- silently do nothing DROPTABLE unlogged3; DROPTABLE unlogged2; DROPTABLE unlogged1;
-- set unlogged CREATETABLE logged1(f1 SERIAL PRIMARYKEY, f2 TEXT); -- has sequence, toast -- check relpersistence of a permanent table SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' UNIONALL SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' UNIONALL
SELECT r.relname ||' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1'
ORDER BY relname;
CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key
CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key
ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists
ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key
ALTER TABLE logged2 SET UNLOGGED;
ALTER TABLE logged1 SET UNLOGGED;
-- check relpersistence of a permanent table after changing to unlogged
SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1'
UNION ALL
SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1'
UNION ALL
SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1'
ORDER BY relname;
ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing
DROP TABLE logged3;
DROP TABLE logged2;
DROP TABLE logged1;
-- test ADD COLUMN IF NOT EXISTS
CREATE TABLE test_add_column(c1 integer);
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN c2 integer;
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN c2 integer; -- fail because c2 already exists
ALTER TABLE ONLY test_add_column
ADD COLUMN c2 integer; -- fail because c2 already exists
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists
ALTER TABLE ONLY test_add_column
ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN c2 integer, -- fail because c2 already exists
ADD COLUMN c3 integer primary key;
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists
ADD COLUMN c3 integer primary key;
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists
ADD COLUMN IF NOT EXISTS c3 integer primary key; -- skipping because c3 already exists
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists
ADD COLUMN IF NOT EXISTS c3 integer, -- skipping because c3 already exists
ADD COLUMN c4 integer REFERENCES test_add_column;
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN IF NOT EXISTS c4 integer REFERENCES test_add_column;
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN IF NOT EXISTS c5 SERIAL CHECK (c5 > 8);
\d test_add_column
ALTER TABLE test_add_column
ADD COLUMN IF NOT EXISTS c5 SERIAL CHECK (c5 > 10);
ALTER TABLE test_add_column
ADD c6 integer; -- omit COLUMN
ALTER TABLE test_add_column
ADD IF NOT EXISTS c6 integer;
ALTER TABLE test_add_column
DROP c6; -- omit COLUMN
ALTER TABLE test_add_column
DROP IF EXISTS c6;
\d test_add_column*
DROP TABLE test_add_column;
\d test_add_column*
-- assorted cases with multiple ALTER TABLE steps
CREATE TABLE ataddindex(f1 INT);
INSERT INTO ataddindex VALUES (42), (43);
CREATE UNIQUE INDEX ataddindexi0 ON ataddindex(f1);
ALTER TABLE ataddindex
ADD PRIMARY KEY USING INDEX ataddindexi0,
ALTER f1 TYPE BIGINT;
\d ataddindex
DROP TABLE ataddindex;
CREATE TABLE ataddindex(f1 VARCHAR(10));
INSERT INTO ataddindex(f1) VALUES ('foo'), ('a');
ALTER TABLE ataddindex
ALTER f1 SET DATA TYPE TEXT,
ADD EXCLUDE ((f1 LIKE 'a') WITH =);
\d ataddindex
DROP TABLE ataddindex;
CREATE TABLE atnotnull1 ();
ALTER TABLE atnotnull1
ADD COLUMN a INT,
ALTER a SET NOT NULL;
ALTER TABLE atnotnull1
ADD COLUMN b INT,
ADD NOT NULL b;
ALTER TABLE atnotnull1
ADD COLUMN c INT,
ADD PRIMARY KEY (c);
\d+ atnotnull1
-- cannot drop column that is part of the partition key
CREATE TABLE partitioned (
a int,
b int
) PARTITION BY RANGE (a, (a+b+1));
ALTER TABLE partitioned DROP COLUMN a;
ALTER TABLE partitioned ALTER COLUMN a TYPE char(5);
ALTER TABLE partitioned DROP COLUMN b;
ALTER TABLE partitioned ALTER COLUMN b TYPE char(5);
-- specifying storage parameters for partitioned tables is not supported
ALTER TABLE partitioned SET (fillfactor=100);
-- partitioned table cannot participate in regular inheritance
CREATE TABLE nonpartitioned (
a int,
b int
);
ALTER TABLE partitioned INHERIT nonpartitioned;
ALTER TABLE nonpartitioned INHERIT partitioned;
-- cannot add NO INHERIT constraint to partitioned tables
ALTER TABLE partitioned ADD CONSTRAINT chk_a CHECK (a > 0) NO INHERIT;
DROP TABLE partitioned, nonpartitioned;
--
-- ATTACH PARTITION
--
-- check that target table is partitioned
CREATE TABLE unparted (
a int
);
CREATE TABLE fail_part (like unparted);
ALTER TABLE unparted ATTACH PARTITION fail_part FOR VALUES IN ('a');
DROP TABLE unparted, fail_part;
-- check that partition bound is compatible
CREATE TABLE list_parted (
a int NOT NULL,
b char(2) COLLATE "C",
CONSTRAINT check_a CHECK (a > 0)
) PARTITION BY LIST (a);
CREATE TABLE fail_part (LIKE list_parted);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES FROM (1) TO (10);
DROP TABLE fail_part;
-- check that the table being attached exists
ALTER TABLE list_parted ATTACH PARTITION nonexistent FOR VALUES IN (1);
-- check ownership of the source table
CREATE ROLE regress_test_me;
CREATE ROLE regress_test_not_me;
CREATE TABLE not_owned_by_me (LIKE list_parted);
ALTER TABLE not_owned_by_me OWNER TO regress_test_not_me;
SET SESSION AUTHORIZATION regress_test_me;
CREATE TABLE owned_by_me (
a int
) PARTITION BY LIST (a);
ALTER TABLE owned_by_me ATTACH PARTITION not_owned_by_me FOR VALUES IN (1);
RESET SESSION AUTHORIZATION;
DROP TABLE owned_by_me, not_owned_by_me;
DROP ROLE regress_test_not_me;
DROP ROLE regress_test_me;
-- check that the table being attached is not part of regular inheritance
CREATE TABLE parent (LIKE list_parted);
CREATE TABLE child () INHERITS (parent);
ALTER TABLE list_parted ATTACH PARTITION child FOR VALUES IN (1);
ALTER TABLE list_parted ATTACH PARTITION parent FOR VALUES IN (1);
DROP TABLE child;
-- now it should work, with a little tweak
ALTER TABLE parent ADD CONSTRAINT check_a CHECK (a > 0);
ALTER TABLE list_parted ATTACH PARTITION parent FOR VALUES IN (1);
-- test insert/update, per bug #18550
INSERT INTO parent VALUES (1);
UPDATE parent SET a = 2 WHERE a = 1;
DROP TABLE parent CASCADE;
-- check any TEMP-ness
CREATE TEMP TABLE temp_parted (a int) PARTITION BY LIST (a);
CREATE TABLE perm_part (a int);
ALTER TABLE temp_parted ATTACH PARTITION perm_part FOR VALUES IN (1);
DROP TABLE temp_parted, perm_part;
-- check that the table being attached is not a typed table
CREATE TYPE mytype AS (a int);
CREATE TABLE fail_part OF mytype;
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
DROP TYPE mytype CASCADE;
-- check that the table being attached has only columns present in the parent
CREATE TABLE fail_part (like list_parted, c int);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
DROP TABLE fail_part;
-- check that the table being attached has every column of the parent
CREATE TABLE fail_part (a int NOT NULL);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
DROP TABLE fail_part;
-- check that columns match in type, collation and NOT NULL status
CREATE TABLE fail_part (
b char(3),
a int NOT NULL
);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
ALTER TABLE fail_part ALTER b TYPE char (2) COLLATE "POSIX";
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
DROP TABLE fail_part;
-- check that the table being attached has all constraints of the parent
CREATE TABLE fail_part (
b char(2) COLLATE "C",
a int NOT NULL
);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
-- check that the constraint matches in definition with parent's constraint
ALTER TABLE fail_part ADD CONSTRAINT check_a CHECK (a >= 0);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
DROP TABLE fail_part;
-- check the attributes and constraints after partition is attached
CREATE TABLE part_1 (
a int NOT NULL,
b char(2) COLLATE "C",
CONSTRAINT check_a CHECK (a > 0)
);
ALTER TABLE list_parted ATTACH PARTITION part_1 FOR VALUES IN (1);
-- attislocal and conislocal are always falsefor merged attributes and constraints respectively.
SELECT attislocal, attinhcount FROM pg_attribute WHERE attrelid = 'part_1'::regclass AND attnum > 0;
SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_1'::regclass AND conname = 'check_a';
-- check that NOT NULL NO INHERIT cannot be merged to a normal NOT NULL
CREATE TABLE part_fail (a int NOT NULL NO INHERIT,
b char(2) COLLATE "C",
CONSTRAINT check_a CHECK (a > 0)
);
ALTER TABLE list_parted ATTACH PARTITION part_fail FOR VALUES IN (2);
DROP TABLE part_fail;
-- check that the new partition won't overlap with an existing partition
CREATE TABLE fail_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1);
DROP TABLE fail_part;
-- check that an existing table can be attached as a default partition
CREATE TABLE def_part (LIKE list_parted INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION def_part DEFAULT;
-- check attaching default partition fails if a default partition already
-- exists
CREATE TABLE fail_def_part (LIKE part_1 INCLUDING CONSTRAINTS);
ALTER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT;
-- check validation when attaching list partitions
CREATE TABLE list_parted2 (
a int,
b char
) PARTITION BY LIST (a);
-- check that violating rows are correctly reported
CREATE TABLE part_2 (LIKE list_parted2);
INSERT INTO part_2 VALUES (3, 'a');
ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2);
-- should be ok after deleting the bad row DELETE FROM part_2;
ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2);
-- check partition cannot be attached ifdefault has some row for its values
CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT;
INSERT INTO list_parted2_def VALUES (11, 'z');
CREATE TABLE part_3 (LIKE list_parted2);
ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11);
-- should be ok after deleting the bad row DELETE FROM list_parted2_def WHERE a = 11;
ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11);
-- adding constraints that describe the desired partition constraint
-- (or more restrictive) will help skip the validation scan
CREATE TABLE part_3_4 (
LIKE list_parted2,
CONSTRAINT check_a CHECK (a IN (3))
);
-- however, if a list partition does not accept nulls, there should be
-- an explicit NOT NULL constraint on the partition key column for the
-- validation scan to be skipped;
ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4);
-- adding a NOT NULL constraint will cause the scan to be skipped
ALTER TABLE list_parted2 DETACH PARTITION part_3_4;
ALTER TABLE part_3_4 ALTER a SET NOT NULL;
ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4);
-- check ifdefault partition scan skipped
ALTER TABLE list_parted2_def ADD CONSTRAINT check_a CHECK (a IN (5, 6));
CREATE TABLE part_55_66 PARTITION OF list_parted2 FOR VALUES IN (55, 66);
-- check validation when attaching range partitions
CREATE TABLE range_parted (
a int,
b int
) PARTITION BY RANGE (a, b);
-- check that violating rows are correctly reported
CREATE TABLE part1 (
a int NOT NULL CHECK (a = 1),
b int NOT NULL CHECK (b >= 1 AND b <= 10)
);
INSERT INTO part1 VALUES (1, 10);
-- Remember the TO bound is exclusive
ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10);
-- should be ok after deleting the bad row DELETE FROM part1;
ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10);
-- adding constraints that describe the desired partition constraint
-- (or more restrictive) will help skip the validation scan
CREATE TABLE part2 (
a int NOT NULL CHECK (a = 1),
b int NOT NULL CHECK (b >= 10 AND b < 18)
);
ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20);
-- Only one default partition is allowed, hence, following should give error
CREATE TABLE partr_def2 (LIKE part1 INCLUDING CONSTRAINTS);
ALTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT;
-- Overlapping partitions cannot be attached, hence, following should give error
INSERT INTO partr_def1 VALUES (2, 10);
CREATE TABLE part3 (LIKE range_parted);
ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (2, 10) TO (2, 20);
-- Attaching partitions should be successful when there are no overlapping rows
ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (3, 10) TO (3, 20);
-- check that leaf partitions are scanned when attaching a partitioned
-- table
CREATE TABLE part_5 (
LIKE list_parted2
) PARTITION BY LIST (b);
-- check that violating rows are correctly reported
CREATE TABLE part_5_a PARTITION OF part_5 FOR VALUES IN ('a');
INSERT INTO part_5_a (a, b) VALUES (6, 'a');
ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5);
-- delete the faulting row and also add a constraint to skip the scan DELETE FROM part_5_a WHERE a NOT IN (3);
ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 5);
ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5);
ALTER TABLE list_parted2 DETACH PARTITION part_5;
ALTER TABLE part_5 DROP CONSTRAINT check_a;
-- scan should again be skipped, even though NOT NULL is now a column property
ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IN (5)), ALTER a SET NOT NULL;
ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5);
-- Check the case where attnos of the partitioning columns in the table being
-- attached differs from the parent. It should not affect the constraint-
-- checking logic that allows to skip the scan.
CREATE TABLE part_6 (
c int,
LIKE list_parted2,
CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 6)
);
ALTER TABLE part_6 DROP c;
ALTER TABLE list_parted2 ATTACH PARTITION part_6 FOR VALUES IN (6);
-- Similar to above, but the table being attached is a partitioned table
-- whose partition has still different attnos for the root partitioning
-- columns.
CREATE TABLE part_7 (
LIKE list_parted2,
CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7)
) PARTITION BY LIST (b);
CREATE TABLE part_7_a_null (
c int,
d int,
e int,
LIKE list_parted2, -- 'a' will have attnum = 4
CONSTRAINT check_b CHECK (b IS NULL OR b = 'a'),
CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7)
);
ALTER TABLE part_7_a_null DROP c, DROP d, DROP e;
ALTER TABLE part_7 ATTACH PARTITION part_7_a_null FOR VALUES IN ('a', null);
ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7);
-- Same example, but check this time that the constraint correctly detects
-- violating rows
ALTER TABLE list_parted2 DETACH PARTITION part_7;
ALTER TABLE part_7 DROP CONSTRAINT check_a; -- thusly, scan won't be skipped
INSERT INTO part_7 (a, b) VALUES (8, null), (9, 'a');
SELECT tableoid::regclass, a, b FROM part_7 order by a;
ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7);
-- check that leaf partitions of default partition are scanned when
-- attaching a partitioned table.
ALTER TABLE part_5 DROP CONSTRAINT check_a;
CREATE TABLE part5_def PARTITION OF part_5 DEFAULT PARTITION BY LIST(a);
CREATE TABLE part5_def_p1 PARTITION OF part5_def FOR VALUES IN (5);
INSERT INTO part5_def_p1 VALUES (5, 'y');
CREATE TABLE part5_p1 (LIKE part_5);
ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y');
-- should be ok after deleting the bad row DELETE FROM part5_def_p1 WHERE b = 'y';
ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y');
-- check that the table being attached is not already a partition
ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2);
-- check that circular inheritance is not allowed
ALTER TABLE part_5 ATTACH PARTITION list_parted2 FOR VALUES IN ('b');
ALTER TABLE list_parted2 ATTACH PARTITION list_parted2 FOR VALUES IN (0);
-- If a partitioned table being created or an existing table being attached
-- as a partition does not have a constraint that would allow validation scan
-- to be skipped, but an individual partition does, then the partition's
-- validation scan is skipped.
CREATE TABLE quuux (a int, b text) PARTITION BY LIST (a);
CREATE TABLE quuux_default PARTITION OF quuux DEFAULT PARTITION BY LIST (b);
CREATE TABLE quuux_default1 PARTITION OF quuux_default (
CONSTRAINT check_1 CHECK (a IS NOT NULL AND a = 1)
) FOR VALUES IN ('b');
CREATE TABLE quuux1 (a int, b text);
ALTER TABLE quuux ATTACH PARTITION quuux1 FOR VALUES IN (1); -- validate!
CREATE TABLE quuux2 (a int, b text);
ALTER TABLE quuux ATTACH PARTITION quuux2 FOR VALUES IN (2); -- skip validation
DROP TABLE quuux1, quuux2;
-- should validate for quuux1, but not for quuux2
CREATE TABLE quuux1 PARTITION OF quuux FOR VALUES IN (1);
CREATE TABLE quuux2 PARTITION OF quuux FOR VALUES IN (2);
DROP TABLE quuux;
-- check validation when attaching hash partitions
-- Use hand-rolled hash functions and operator class to get predictable result
-- on different machines. part_test_int4_ops is defined in test_setup.sql.
-- check that the new partition won't overlap with an existing partition
CREATE TABLE hash_parted (
a int,
b int
) PARTITION BY HASH (a part_test_int4_ops);
CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE fail_part (LIKE hpart_1);
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 4);
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 0);
DROP TABLE fail_part;
-- check validation when attaching hash partitions
-- check that violating rows are correctly reported
CREATE TABLE hpart_2 (LIKE hash_parted);
INSERT INTO hpart_2 VALUES (3, 0);
ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1);
-- should be ok after deleting the bad row DELETE FROM hpart_2;
ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1);
-- check that leaf partitions are scanned when attaching a partitioned
-- table
CREATE TABLE hpart_5 (
LIKE hash_parted
) PARTITION BY LIST (b);
-- check that violating rows are correctly reported
CREATE TABLE hpart_5_a PARTITION OF hpart_5 FOR VALUES IN ('1', '2', '3');
INSERT INTO hpart_5_a (a, b) VALUES (7, 1);
ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2);
-- should be ok after deleting the bad row DELETE FROM hpart_5_a;
ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2);
-- check that the table being attach is with valid modulus and remainder value
CREATE TABLE fail_part(LIKE hash_parted);
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 0, REMAINDER 1);
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 8);
ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 3, REMAINDER 2);
DROP TABLE fail_part;
--
-- DETACH PARTITION
--
-- check that the table is partitioned at all
CREATE TABLE regular_table (a int);
ALTER TABLE regular_table DETACH PARTITION any_name;
ALTER TABLE regular_table DETACH PARTITION any_name CONCURRENTLY;
ALTER TABLE regular_table DETACH PARTITION any_name FINALIZE;
DROP TABLE regular_table;
-- check that the partition being detached exists at all
ALTER TABLE list_parted2 DETACH PARTITION part_4;
ALTER TABLE hash_parted DETACH PARTITION hpart_4;
-- check that the partition being detached is actually a partition of the parent
CREATE TABLE not_a_part (a int);
ALTER TABLE list_parted2 DETACH PARTITION not_a_part;
ALTER TABLE list_parted2 DETACH PARTITION part_1;
ALTER TABLE hash_parted DETACH PARTITION not_a_part;
DROP TABLE not_a_part;
-- check that, after being detached, attinhcount/coninhcount is dropped to 0 and
-- attislocal/conislocal is set to true
ALTER TABLE list_parted2 DETACH PARTITION part_3_4;
SELECT attinhcount, attislocal FROM pg_attribute WHERE attrelid = 'part_3_4'::regclass AND attnum > 0;
SELECT coninhcount, conislocal FROM pg_constraint WHERE conrelid = 'part_3_4'::regclass AND conname = 'check_a';
DROP TABLE part_3_4;
-- check that a detached partition is not dropped on dropping a partitioned table
CREATE TABLE range_parted2 (
a int
) PARTITION BY RANGE(a);
CREATE TABLE part_rp PARTITION OF range_parted2 FOR VALUES FROM (0) to (100);
ALTER TABLE range_parted2 DETACH PARTITION part_rp;
DROP TABLE range_parted2;
SELECT * from part_rp;
DROP TABLE part_rp;
-- concurrent detach
CREATE TABLE range_parted2 (
a int
) PARTITION BY RANGE(a);
CREATE TABLE part_rp PARTITION OF range_parted2 FOR VALUES FROM (0) to (100);
BEGIN;
-- doesn't work in a partition block
ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY;
COMMIT;
CREATE TABLE part_rpd PARTITION OF range_parted2 DEFAULT;
-- doesn't work if there's a default partition
ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY;
-- doesn't work for the default partition
ALTER TABLE range_parted2 DETACH PARTITION part_rpd CONCURRENTLY;
DROP TABLE part_rpd;
-- works fine
ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY;
\d+ range_parted2
-- constraint should be created
\d part_rp
CREATE TABLE part_rp100 PARTITION OF range_parted2 (CHECK (a>=123 AND a<133 AND a IS NOT NULL)) FOR VALUES FROM (100) to (200);
ALTER TABLE range_parted2 DETACH PARTITION part_rp100 CONCURRENTLY;
-- redundant constraint should not be created
\d part_rp100
DROP TABLE range_parted2;
-- Test that hash partitions continue to work after they're concurrently
-- detached (bugs #18371, #19070)
CREATE TABLE hash_parted2 (a int) PARTITION BY HASH(a);
CREATE TABLE part_hp PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 0);
ALTER TABLE hash_parted2 DETACH PARTITION part_hp CONCURRENTLY;
DROP TABLE hash_parted2;
INSERT INTO part_hp VALUES (1);
DROP TABLE part_hp;
-- Check ALTER TABLE commands for partitioned tables and partitions
-- cannot add/drop column to/from *only* the parent
ALTER TABLE ONLY list_parted2 ADD COLUMN c int;
ALTER TABLE ONLY list_parted2 DROP COLUMN b;
-- cannot add a column to partition or drop an inherited one
ALTER TABLE part_2 ADD COLUMN c text;
ALTER TABLE part_2 DROP COLUMN b;
-- Nor rename, alter type
ALTER TABLE part_2 RENAME COLUMN b to c;
ALTER TABLE part_2 ALTER COLUMN b TYPE text;
-- cannot add NOT NULL or check constraints to *only* the parent, when
-- partitions exist
ALTER TABLE ONLY list_parted2 ALTER b SET NOT NULL;
ALTER TABLE ONLY list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz');
-- dropping them is ok though
ALTER TABLE list_parted2 ALTER b SET NOT NULL;
ALTER TABLE ONLY list_parted2 ALTER b DROP NOT NULL;
ALTER TABLE list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz');
ALTER TABLE ONLY list_parted2 DROP CONSTRAINT check_b;
-- ... and the partitions should still have both
\d+ part_2
-- It's alright though, if no partitions are yet created
CREATE TABLE parted_no_parts (a int) PARTITION BY LIST (a);
ALTER TABLE ONLY parted_no_parts ALTER a SET NOT NULL;
ALTER TABLE ONLY parted_no_parts ADD CONSTRAINT check_a CHECK (a > 0);
DROP TABLE parted_no_parts;
-- cannot drop inherited NOT NULL or check constraints from partition
ALTER TABLE list_parted2 ALTER b SET NOT NULL, ADD CONSTRAINT check_a2 CHECK (a > 0);
ALTER TABLE part_2 ALTER b DROP NOT NULL;
ALTER TABLE part_2 DROP CONSTRAINT check_a2;
-- can't drop NOT NULL from under an invalid PK
CREATE TABLE list_parted3 (a int NOT NULL) PARTITION BY LIST (a);
CREATE TABLE list_parted3_1 PARTITION OF list_parted3 FOR VALUES IN (1);
ALTER TABLE ONLY list_parted3 ADD PRIMARY KEY (a);
ALTER TABLE ONLY list_parted3 DROP CONSTRAINT list_parted3_a_not_null;
-- Doesn't make sense to add NO INHERIT constraints on partitioned tables
ALTER TABLE list_parted2 add constraint check_b2 check (b <> 'zz') NO INHERIT;
-- check that a partition cannot participate in regular inheritance
CREATE TABLE inh_test () INHERITS (part_2);
CREATE TABLE inh_test (LIKE part_2);
ALTER TABLE inh_test INHERIT part_2;
ALTER TABLE part_2 INHERIT inh_test;
-- cannot drop or alter type of partition key columns of lower level
-- partitioned tables; for example, part_5, which is list_parted2's
-- partition, is partitioned on b;
ALTER TABLE list_parted2 DROP COLUMN b;
ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
-- dropping non-partition key columns should be allowed on the parent table.
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
-- cleanup
DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- 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;
alter table p1 attach partition p11 for values from (2) to (5);
insert into p1 (a, b) values (2, 3);
-- check that partition validation scan correctly detects violating rows
alter table p attach partition p1 for values from (1, 2) to (1, 10);
-- cleanup
drop table p;
drop table p1;
-- validate constraint on partitioned tables should only scan leaf partitions
create table parted_validate_test (a int) partition by list (a);
create table parted_validate_test_1 partition of parted_validate_test for values in (0, 1);
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
-- test alter column options
CREATE TABLE attmp(i integer);
INSERT INTO attmp VALUES (1);
ALTER TABLE attmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
ALTER TABLE attmp ALTER COLUMN i RESET (n_distinct_inherited);
ANALYZE attmp;
DROP TABLE attmp;
DROP USER regress_alter_table_user1;
-- check that violating rows are correctly reported when attaching as the
-- default partition
create table defpart_attach_test (a int) partition by list (a);
create table defpart_attach_test1 partition of defpart_attach_test for values in (1);
create table defpart_attach_test_d (b int, a int);
alter table defpart_attach_test_d drop b;
insert into defpart_attach_test_d values (1), (2);
-- error because its constraint as the default partition would be violated
-- by the row containing 1
alter table defpart_attach_test attach partition defpart_attach_test_d default; delete from defpart_attach_test_d where a = 1;
alter table defpart_attach_test_d add check (a > 1);
-- should be attached successfully and without needing to be scanned
alter table defpart_attach_test attach partition defpart_attach_test_d default;
-- check that attaching a partition correctly reports any rows in the default
-- partition that should not be there for the new partition to be attached
-- successfully
create table defpart_attach_test_2 (like defpart_attach_test_d);
alter table defpart_attach_test attach partition defpart_attach_test_2 for values in (2);
drop table defpart_attach_test;
-- check combinations of temporary and permanent relations when attaching
-- partitions.
create table perm_part_parent (a int) partition by list (a);
create temp table temp_part_parent (a int) partition by list (a);
create table perm_part_child (a int);
create temp table temp_part_child (a int);
alter table temp_part_parent attach partition perm_part_child default; -- error
alter table perm_part_parent attach partition temp_part_child default; -- error
alter table temp_part_parent attach partition temp_part_child default; -- ok
drop table perm_part_parent cascade;
drop table temp_part_parent cascade;
-- check that attaching partitions to a table while it is being used is
-- prevented
create table tab_part_attach (a int) partition by list (a);
create or replace function func_part_attach() returns trigger
language plpgsql as $$
begin
execute 'create table tab_part_attach_1 (a int)';
execute 'alter table tab_part_attach attach partition tab_part_attach_1 for values in (1)'; returnnull;
end $$;
create trigger trig_part_attach before insert on tab_part_attach for each statement execute procedure func_part_attach();
insert into tab_part_attach values (1);
drop table tab_part_attach;
drop function func_part_attach();
-- test case where the partitioning operator is a SQL function whose
-- evaluation results in the table's relcache being rebuilt partway through
-- the execution of an ATTACH PARTITION command
create function at_test_sql_partop (int4, int4) returns int language sql
as $$ select case when $1 = $2 then 0 when $1 > $2 then 1else -1 end; $$;
create operator class at_test_sql_partop for type int4 using btree as
operator 1 < (int4, int4), operator 2 <= (int4, int4),
operator 3 = (int4, int4), operator 4 >= (int4, int4),
operator 5 > (int4, int4), function1 at_test_sql_partop(int4, int4);
create table at_test_sql_partop (a int) partition by range (a at_test_sql_partop);
create table at_test_sql_partop_1 (a int);
alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values from (0) to (10);
drop table at_test_sql_partop;
drop operator class at_test_sql_partop using btree;
drop function at_test_sql_partop;
/* Test case for bug #16242 */
-- We create a parent and child where the child has missing
-- non-null attribute values, and arrange to pass them through
-- tuple conversion from the child to the parent tupdesc
create table bar1 (a integer, b integer not nulldefault1)
partition by range (a);
create table bar2 (a integer);
insert into bar2 values (1);
alter table bar2 add column b integer not nulldefault1;
-- (at this point bar2 contains tuple with natts=1)
alter table bar1 attach partition bar2 default;
-- this works:
select * from bar1;
-- this exercises tuple conversion:
create function xtrig()
returns trigger language plpgsql
as $$
declare
r record;
begin for r in select * from old loop
raise info 'a=%, b=%', r.a, r.b;
end loop; returnNULL;
end;
$$;
create trigger xtrig
after update on bar1
referencing old table as old for each statement execute procedure xtrig();
update bar1 set a = a + 1;
/* End test case for bug #16242 */
/* Test case for bug #17409 */
create table attbl (p1 int constraint pk_attbl primary key);
create table atref (c1 int references attbl(p1));
cluster attbl using pk_attbl;
alter table attbl alter column p1 set data type bigint;
alter table atref alter column c1 set data type bigint;
drop table attbl, atref;
create table attbl (p1 int constraint pk_attbl primary key);
alter table attbl replica identity using index pk_attbl;
create table atref (c1 int references attbl(p1));
alter table attbl alter column p1 set data type bigint;
alter table atref alter column c1 set data type bigint;
drop table attbl, atref;
/* End test case for bug #17409 */
/* Test case for bug #18970 */
create table attbl(a int);
create table atref(b attbl check ((b).a is not null));
alter table attbl alter column a type numeric; -- someday this should work
alter table atref drop constraint atref_b_check;
create statistics atref_stat on ((b).a is not null) from atref;
alter table attbl alter column a type numeric; -- someday this should work
drop statistics atref_stat;
create index atref_idx on atref (((b).a));
alter table attbl alter column a type numeric; -- someday this should work
drop table attbl, atref;
/* End test case for bug #18970 */
-- Test that ALTER TABLE rewrite preserves a clustered index
-- for normal indexes and indexes on constraints.
create table alttype_cluster (a int);
alter table alttype_cluster add primary key (a);
create index alttype_cluster_ind on alttype_cluster (a);
alter table alttype_cluster cluster on alttype_cluster_ind;
-- Normal index remains clustered.
select indexrelid::regclass, indisclustered from pg_index
where indrelid = 'alttype_cluster'::regclass
order by indexrelid::regclass::text;
alter table alttype_cluster alter a type bigint;
select indexrelid::regclass, indisclustered from pg_index
where indrelid = 'alttype_cluster'::regclass
order by indexrelid::regclass::text;
-- Constraint index remains clustered.
alter table alttype_cluster cluster on alttype_cluster_pkey;
select indexrelid::regclass, indisclustered from pg_index
where indrelid = 'alttype_cluster'::regclass
order by indexrelid::regclass::text;
alter table alttype_cluster alter a type int;
select indexrelid::regclass, indisclustered from pg_index
where indrelid = 'alttype_cluster'::regclass
order by indexrelid::regclass::text;
drop table alttype_cluster;
--
-- Check that attaching or detaching a partitioned partition correctly leads
-- to its partitions' constraint being updated to reflect the parent's
-- newly added/removed constraint
create table target_parted (a int, b int) partition by list (a);
create table attach_parted (a int, b int) partition by list (b);
create table attach_parted_part1 partition of attach_parted for values in (1);
-- insert a row directly into the leaf partition so that its partition
-- constraint is built and stored in the relcache
insert into attach_parted_part1 values (1, 1);
-- the following better invalidate the partition constraint of the leaf
-- partition too...
alter table target_parted attach partition attach_parted for values in (1);
-- ...such that the following insert fails
insert into attach_parted_part1 values (2, 1);
-- ...and doesn't when the partition is detached along with its own partition
alter table target_parted detach partition attach_parted;
insert into attach_parted_part1 values (2, 1);
-- Test altering table having publication
create schema alter1;
create schema alter2;
create table alter1.t1 (a int);
set client_min_messages = 'ERROR';
create publication pub1 for table alter1.t1, tables in schema alter2;
reset client_min_messages;
alter table alter1.t1 set schema alter2;
\d+ alter2.t1
drop publication pub1;
drop schema alter1 cascade;
drop schema alter2 cascade;
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.85Angebot
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.