-- -- MULTI_ALTER_TABLE_STATEMENTS -- ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 220000; -- Check that we can run ALTER TABLE statements on distributed tables. -- We set the shardid sequence here so that the shardids in this test -- aren't affected by changes to the previous tests. CREATE TABLE lineitem_alter ( l_orderkey bigint not null, l_partkey integer not null, l_suppkey integer not null, l_linenumber integer not null, l_quantity decimal(15, 2) not null, l_extendedprice decimal(15, 2) not null, l_discount decimal(15, 2) not null, l_tax decimal(15, 2) not null, l_returnflag char(1) not null, l_linestatus char(1) not null, l_shipdate date not null, l_commitdate date not null, l_receiptdate date not null, l_shipinstruct char(25) not null, l_shipmode char(10) not null, l_comment varchar(44) not null ); SELECT master_create_distributed_table('lineitem_alter', 'l_orderkey', 'append'); master_create_distributed_table --------------------------------- (1 row) \copy lineitem_alter FROM '@abs_srcdir@/data/lineitem.1.data' with delimiter '|' -- Verify that we can add columns ALTER TABLE lineitem_alter ADD COLUMN float_column FLOAT; NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' ALTER TABLE lineitem_alter ADD COLUMN date_column DATE; ALTER TABLE lineitem_alter ADD COLUMN int_column1 INTEGER DEFAULT 1; ALTER TABLE lineitem_alter ADD COLUMN int_column2 INTEGER DEFAULT 2; ALTER TABLE lineitem_alter ADD COLUMN null_column INTEGER; -- show changed schema on one worker \c - - - :worker_1_port SELECT attname, atttypid::regtype FROM (SELECT oid FROM pg_class WHERE relname LIKE 'lineitem_alter_%' ORDER BY relname LIMIT 1) pc JOIN pg_attribute ON (pc.oid = pg_attribute.attrelid) ORDER BY attnum; attname | atttypid -----------------+------------------- tableoid | oid cmax | cid xmax | xid cmin | cid xmin | xid ctid | tid l_orderkey | bigint l_partkey | integer l_suppkey | integer l_linenumber | integer l_quantity | numeric l_extendedprice | numeric l_discount | numeric l_tax | numeric l_returnflag | character l_linestatus | character l_shipdate | date l_commitdate | date l_receiptdate | date l_shipinstruct | character l_shipmode | character l_comment | character varying float_column | double precision date_column | date int_column1 | integer int_column2 | integer null_column | integer (27 rows) \c - - - :master_port SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null float_column | double precision | date_column | date | int_column1 | integer | default 1 int_column2 | integer | default 2 null_column | integer | (21 rows) SELECT float_column, count(*) FROM lineitem_alter GROUP BY float_column; float_column | count --------------+------- | 6000 (1 row) SELECT int_column1, count(*) FROM lineitem_alter GROUP BY int_column1; int_column1 | count -------------+------- 1 | 6000 (1 row) -- Verify that SET|DROP DEFAULT works ALTER TABLE lineitem_alter ALTER COLUMN float_column SET DEFAULT 1; NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' ALTER TABLE lineitem_alter ALTER COLUMN int_column1 DROP DEFAULT; -- \copy to verify that default values take effect \copy lineitem_alter (l_orderkey, l_partkey, l_suppkey, l_linenumber, l_quantity, l_extendedprice, l_discount, l_tax, l_returnflag, l_linestatus, l_shipdate, l_commitdate, l_receiptdate, l_shipinstruct, l_shipmode, l_comment) FROM '@abs_srcdir@/data/lineitem.1.data' with delimiter '|' SELECT float_column, count(*) FROM lineitem_alter GROUP BY float_column; float_column | count --------------+------- | 6000 1 | 6000 (2 rows) SELECT int_column1, count(*) FROM lineitem_alter GROUP BY int_column1; int_column1 | count -------------+------- | 6000 1 | 6000 (2 rows) -- Verify that SET NOT NULL works ALTER TABLE lineitem_alter ALTER COLUMN int_column2 SET NOT NULL; SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+-------------------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null float_column | double precision | default 1 date_column | date | int_column1 | integer | int_column2 | integer | not null default 2 null_column | integer | (21 rows) -- Drop default so that NULLs will be inserted for this column ALTER TABLE lineitem_alter ALTER COLUMN int_column2 DROP DEFAULT; -- \copy should fail because it will try to insert NULLs for a NOT NULL column -- Note, this operation will create a table on the workers but it won't be in the metadata \copy lineitem_alter (l_orderkey, l_partkey, l_suppkey, l_linenumber, l_quantity, l_extendedprice, l_discount, l_tax, l_returnflag, l_linestatus, l_shipdate, l_commitdate, l_receiptdate, l_shipinstruct, l_shipmode, l_comment) FROM '@abs_srcdir@/data/lineitem.1.data' with delimiter '|' ERROR: null value in column "int_column2" violates not-null constraint DETAIL: Failing row contains (1, 155190, 7706, 1, 17.00, 21168.23, 0.04, 0.02, N, O, 1996-03-13, 1996-02-12, 1996-03-22, DELIVER IN PERSON , TRUCK , egular courts above the, 1, null, null, null, null). -- Verify that DROP NOT NULL works ALTER TABLE lineitem_alter ALTER COLUMN int_column2 DROP NOT NULL; SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null float_column | double precision | default 1 date_column | date | int_column1 | integer | int_column2 | integer | null_column | integer | (21 rows) -- \copy should succeed now \copy lineitem_alter (l_orderkey, l_partkey, l_suppkey, l_linenumber, l_quantity, l_extendedprice, l_discount, l_tax, l_returnflag, l_linestatus, l_shipdate, l_commitdate, l_receiptdate, l_shipinstruct, l_shipmode, l_comment) FROM '@abs_srcdir@/data/lineitem.1.data' with delimiter '|' SELECT count(*) from lineitem_alter; count ------- 18000 (1 row) -- Verify that SET DATA TYPE works SELECT int_column2, pg_typeof(int_column2), count(*) from lineitem_alter GROUP BY int_column2; int_column2 | pg_typeof | count -------------+-----------+------- | integer | 6000 2 | integer | 12000 (2 rows) ALTER TABLE lineitem_alter ALTER COLUMN int_column2 SET DATA TYPE FLOAT; SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null float_column | double precision | default 1 date_column | date | int_column1 | integer | int_column2 | double precision | null_column | integer | (21 rows) SELECT int_column2, pg_typeof(int_column2), count(*) from lineitem_alter GROUP BY int_column2; int_column2 | pg_typeof | count -------------+------------------+------- | double precision | 6000 2 | double precision | 12000 (2 rows) -- Verify that DROP COLUMN works ALTER TABLE lineitem_alter DROP COLUMN int_column1; ALTER TABLE lineitem_alter DROP COLUMN float_column; ALTER TABLE lineitem_alter DROP COLUMN date_column; -- Verify that RENAME COLUMN works ALTER TABLE lineitem_alter RENAME COLUMN l_orderkey TO l_orderkey_renamed; SELECT SUM(l_orderkey_renamed) FROM lineitem_alter; sum ---------- 53620791 (1 row) -- Verify that IF EXISTS works as expected ALTER TABLE non_existent_table ADD COLUMN new_column INTEGER; ERROR: relation "non_existent_table" does not exist ALTER TABLE IF EXISTS non_existent_table ADD COLUMN new_column INTEGER; NOTICE: relation "non_existent_table" does not exist, skipping ALTER TABLE IF EXISTS lineitem_alter ALTER COLUMN int_column2 SET DATA TYPE INTEGER; ALTER TABLE lineitem_alter DROP COLUMN non_existent_column; ERROR: column "non_existent_column" of relation "lineitem_alter" does not exist ALTER TABLE lineitem_alter DROP COLUMN IF EXISTS non_existent_column; NOTICE: column "non_existent_column" of relation "lineitem_alter" does not exist, skipping ALTER TABLE lineitem_alter DROP COLUMN IF EXISTS int_column2; -- Verify with IF EXISTS for extant table ALTER TABLE IF EXISTS lineitem_alter RENAME COLUMN l_orderkey_renamed TO l_orderkey; SELECT SUM(l_orderkey) FROM lineitem_alter; sum ---------- 53620791 (1 row) SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null null_column | integer | (17 rows) -- Verify that we can execute commands with multiple subcommands ALTER TABLE lineitem_alter ADD COLUMN int_column1 INTEGER, ADD COLUMN int_column2 INTEGER; SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null null_column | integer | int_column1 | integer | int_column2 | integer | (19 rows) ALTER TABLE lineitem_alter ADD COLUMN int_column3 INTEGER, ALTER COLUMN int_column1 SET STATISTICS 10; ERROR: alter table command is currently unsupported DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT, ATTACH|DETACH PARTITION and TYPE subcommands are supported. ALTER TABLE lineitem_alter DROP COLUMN int_column1, DROP COLUMN int_column2; SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null null_column | integer | (17 rows) -- Verify that we cannot execute alter commands on the distribution column ALTER TABLE lineitem_alter ALTER COLUMN l_orderkey DROP NOT NULL; ERROR: cannot execute ALTER TABLE command involving partition column ALTER TABLE lineitem_alter DROP COLUMN l_orderkey; ERROR: cannot execute ALTER TABLE command involving partition column -- Verify that we error out on unsupported statement types ALTER TABLE lineitem_alter ALTER COLUMN l_orderkey SET STATISTICS 100; ERROR: alter table command is currently unsupported DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT, ATTACH|DETACH PARTITION and TYPE subcommands are supported. ALTER TABLE lineitem_alter DROP CONSTRAINT IF EXISTS non_existent_contraint; NOTICE: constraint "non_existent_contraint" of relation "lineitem_alter" does not exist, skipping ALTER TABLE lineitem_alter SET WITHOUT OIDS; ERROR: alter table command is currently unsupported DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT, ATTACH|DETACH PARTITION and TYPE subcommands are supported. -- Verify that we error out in case of postgres errors on supported statement -- types ALTER TABLE lineitem_alter ADD COLUMN new_column non_existent_type; ERROR: type "non_existent_type" does not exist LINE 1: ALTER TABLE lineitem_alter ADD COLUMN new_column non_existen... ^ ALTER TABLE lineitem_alter ALTER COLUMN null_column SET NOT NULL; ERROR: column "null_column" contains null values CONTEXT: while executing command on localhost:57638 ALTER TABLE lineitem_alter ALTER COLUMN l_partkey SET DEFAULT 'a'; ERROR: invalid input syntax for integer: "a" -- Verify that we error out on non-column RENAME statements ALTER TABLE lineitem_alter RENAME TO lineitem_renamed; ERROR: renaming distributed tables is currently unsupported ALTER TABLE lineitem_alter RENAME CONSTRAINT constraint_a TO constraint_b; ERROR: renaming constraints belonging to distributed tables is currently unsupported -- Verify that IF EXISTS works as expected with RENAME statements ALTER TABLE non_existent_table RENAME TO non_existent_table_renamed; ERROR: relation "non_existent_table" does not exist ALTER TABLE IF EXISTS non_existent_table RENAME TO non_existent_table_renamed; NOTICE: relation "non_existent_table" does not exist, skipping ALTER TABLE IF EXISTS non_existent_table RENAME COLUMN column1 TO column2; NOTICE: relation "non_existent_table" does not exist, skipping -- Verify that none of the failed alter table commands took effect on the master -- node SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null null_column | integer | (17 rows) -- verify that non-propagated ddl commands are allowed inside a transaction block SET citus.enable_ddl_propagation to false; BEGIN; CREATE INDEX temp_index_1 ON lineitem_alter(l_linenumber); COMMIT; SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename --------------+---------------- temp_index_1 | lineitem_alter (1 row) DROP INDEX temp_index_1; -- verify that single distributed ddl commands are allowed inside a transaction block SET citus.enable_ddl_propagation to true; BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); COMMIT; SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename --------------+---------------- temp_index_2 | lineitem_alter (1 row) DROP INDEX temp_index_2; -- and so are multiple ddl statements BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); ALTER TABLE lineitem_alter ADD COLUMN first integer; COMMIT; SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.lineitem_alter'::regclass; Column | Type | Modifiers -----------------+-----------------------+----------- l_orderkey | bigint | not null l_partkey | integer | not null l_suppkey | integer | not null l_linenumber | integer | not null l_quantity | numeric(15,2) | not null l_extendedprice | numeric(15,2) | not null l_discount | numeric(15,2) | not null l_tax | numeric(15,2) | not null l_returnflag | character(1) | not null l_linestatus | character(1) | not null l_shipdate | date | not null l_commitdate | date | not null l_receiptdate | date | not null l_shipinstruct | character(25) | not null l_shipmode | character(10) | not null l_comment | character varying(44) | not null null_column | integer | first | integer | (18 rows) \d temp_index_2 Index "public.temp_index_2" Column | Type | Definition ------------+--------+------------ l_orderkey | bigint | l_orderkey btree, for table "public.lineitem_alter" ALTER TABLE lineitem_alter DROP COLUMN first; DROP INDEX temp_index_2; -- ensure that user-specified rollback causes full rollback BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); CREATE INDEX temp_index_3 ON lineitem_alter(l_partkey); ROLLBACK; SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename -----------+----------- (0 rows) -- ensure that errors cause full rollback BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); ERROR: relation "temp_index_2" already exists ROLLBACK; SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename -----------+----------- (0 rows) -- verify that SAVEPOINT is allowed... BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); SAVEPOINT my_savepoint; CREATE INDEX temp_index_3 ON lineitem_alter(l_partkey); ROLLBACK; -- and also rolling back to it is also allowed BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); SAVEPOINT my_savepoint; CREATE INDEX temp_index_3 ON lineitem_alter(l_partkey); ROLLBACK TO my_savepoint; COMMIT; SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename --------------+---------------- temp_index_2 | lineitem_alter (1 row) DROP INDEX temp_index_2; -- Add column on only one worker... \c - - - :worker_2_port ALTER TABLE lineitem_alter_220000 ADD COLUMN first integer; \c - - - :master_port -- and try to add it in a multi-statement block, which fails BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' ALTER TABLE lineitem_alter ADD COLUMN first integer; ERROR: column "first" of relation "lineitem_alter_220000" already exists CONTEXT: while executing command on localhost:57638 COMMIT; -- Nothing from the block should have committed SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename -----------+----------- (0 rows) -- Create single-shard table (to avoid deadlocks in the upcoming test hackery) CREATE TABLE single_shard_items (id integer NOT NULL, name text); SELECT master_create_distributed_table('single_shard_items', 'id', 'hash'); master_create_distributed_table --------------------------------- (1 row) SELECT master_create_worker_shards('single_shard_items', 1, 2); master_create_worker_shards ----------------------------- (1 row) -- Verify that ALTER TABLE .. REPLICATION IDENTITY [USING INDEX]* .. works CREATE UNIQUE INDEX replica_idx on single_shard_items(id); SELECT relreplident FROM pg_class WHERE relname = 'single_shard_items'; relreplident -------------- d (1 row) SELECT run_command_on_workers('SELECT relreplident FROM pg_class WHERE relname LIKE ''single_shard_items_%'' LIMIT 1;'); run_command_on_workers ------------------------ (localhost,57637,t,d) (localhost,57638,t,d) (2 rows) ALTER TABLE single_shard_items REPLICA IDENTITY nothing; SELECT relreplident FROM pg_class WHERE relname = 'single_shard_items'; relreplident -------------- n (1 row) SELECT run_command_on_workers('SELECT relreplident FROM pg_class WHERE relname LIKE ''single_shard_items_%'' LIMIT 1;'); run_command_on_workers ------------------------ (localhost,57637,t,n) (localhost,57638,t,n) (2 rows) ALTER TABLE single_shard_items REPLICA IDENTITY full; SELECT relreplident FROM pg_class WHERE relname = 'single_shard_items'; relreplident -------------- f (1 row) SELECT run_command_on_workers('SELECT relreplident FROM pg_class WHERE relname LIKE ''single_shard_items_%'' LIMIT 1;'); run_command_on_workers ------------------------ (localhost,57637,t,f) (localhost,57638,t,f) (2 rows) ALTER TABLE single_shard_items REPLICA IDENTITY USING INDEX replica_idx; SELECT relreplident FROM pg_class WHERE relname = 'single_shard_items'; relreplident -------------- i (1 row) SELECT run_command_on_workers('SELECT relreplident FROM pg_class WHERE relname LIKE ''single_shard_items_%'' LIMIT 1;'); run_command_on_workers ------------------------ (localhost,57637,t,i) (localhost,57638,t,i) (2 rows) ALTER TABLE single_shard_items REPLICA IDENTITY default, REPLICA IDENTITY USING INDEX replica_idx, REPLICA IDENTITY nothing; SELECT relreplident FROM pg_class WHERE relname = 'single_shard_items'; relreplident -------------- n (1 row) SELECT run_command_on_workers('SELECT relreplident FROM pg_class WHERE relname LIKE ''single_shard_items_%'' LIMIT 1;'); run_command_on_workers ------------------------ (localhost,57637,t,n) (localhost,57638,t,n) (2 rows) ALTER TABLE single_shard_items ADD COLUMN test_col int, REPLICA IDENTITY full; DROP INDEX replica_idx; ALTER TABLE single_shard_items REPLICA IDENTITY default; -- Drop the column from the worker... \c - - - :worker_2_port ALTER TABLE lineitem_alter_220000 DROP COLUMN first; -- Create table to trigger at-xact-end (deferred) failure CREATE TABLE ddl_commands (command text UNIQUE DEFERRABLE INITIALLY DEFERRED); -- Use an event trigger to log all DDL event tags in it CREATE FUNCTION log_ddl_tag() RETURNS event_trigger AS $ldt$ BEGIN INSERT INTO ddl_commands VALUES (tg_tag); END; $ldt$ LANGUAGE plpgsql; CREATE EVENT TRIGGER log_ddl_tag ON ddl_command_end EXECUTE PROCEDURE log_ddl_tag(); \c - - - :master_port -- The above trigger will cause failure at transaction end on one placement. -- We'll test 2PC first, as it should handle this "best" (no divergence) SET citus.multi_shard_commit_protocol TO '2pc'; BEGIN; CREATE INDEX single_index_2 ON single_shard_items(id); CREATE INDEX single_index_3 ON single_shard_items(name); COMMIT; WARNING: duplicate key value violates unique constraint "ddl_commands_command_key" DETAIL: Key (command)=(CREATE INDEX) already exists. CONTEXT: while executing command on localhost:57638 ERROR: failure on connection marked as essential: localhost:57638 -- Nothing from the block should have committed SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'single_shard_items' ORDER BY 1; indexname | tablename -----------+----------- (0 rows) -- Now try with 2pc off RESET citus.multi_shard_commit_protocol; BEGIN; CREATE INDEX single_index_2 ON single_shard_items(id); NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' CREATE INDEX single_index_3 ON single_shard_items(name); COMMIT; WARNING: duplicate key value violates unique constraint "ddl_commands_command_key" DETAIL: Key (command)=(CREATE INDEX) already exists. CONTEXT: while executing command on localhost:57638 WARNING: failed to commit critical transaction on localhost:57638, metadata is likely out of sync -- The block should have committed with a warning SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'single_shard_items' ORDER BY 1; indexname | tablename ----------------+-------------------- single_index_2 | single_shard_items single_index_3 | single_shard_items (2 rows) \c - - - :worker_2_port DROP EVENT TRIGGER log_ddl_tag; DROP FUNCTION log_ddl_tag(); DROP TABLE ddl_commands; \c - - - :master_port -- Distributed SELECTs cannot appear after ALTER BEGIN; CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' SELECT count(*) FROM lineitem_alter; ERROR: cannot open new connections after the first modification command within a transaction COMMIT; -- but are allowed before BEGIN; SELECT count(*) FROM lineitem_alter; count ------- 18000 (1 row) CREATE INDEX temp_index_2 ON lineitem_alter(l_orderkey); COMMIT; SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename --------------+---------------- temp_index_2 | lineitem_alter (1 row) DROP INDEX temp_index_2; --- verify that distributed ddl commands can be used with 2pc SET citus.multi_shard_commit_protocol TO '2pc'; CREATE INDEX temp_index_3 ON lineitem_alter(l_orderkey); SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename --------------+---------------- temp_index_3 | lineitem_alter (1 row) DROP INDEX temp_index_3; SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename -----------+----------- (0 rows) RESET citus.multi_shard_commit_protocol; -- verify that not any of shard placements are marked as failed when a query failure occurs CREATE TABLE test_ab (a int, b int); SELECT master_create_distributed_table('test_ab', 'a', 'hash'); master_create_distributed_table --------------------------------- (1 row) SELECT master_create_worker_shards('test_ab', 8, 2); master_create_worker_shards ----------------------------- (1 row) INSERT INTO test_ab VALUES (2, 10); INSERT INTO test_ab VALUES (2, 11); CREATE UNIQUE INDEX temp_unique_index_1 ON test_ab(a); ERROR: could not create unique index "temp_unique_index_1_220022" DETAIL: Key (a)=(2) is duplicated. CONTEXT: while executing command on localhost:57638 SELECT shardid FROM pg_dist_shard_placement NATURAL JOIN pg_dist_shard WHERE logicalrelid='test_ab'::regclass AND shardstate=3; shardid --------- (0 rows) -- Check that the schema on the worker still looks reasonable \c - - - :worker_1_port SELECT attname, atttypid::regtype FROM (SELECT oid FROM pg_class WHERE relname LIKE 'lineitem_alter_%' ORDER BY relname LIMIT 1) pc JOIN pg_attribute ON (pc.oid = pg_attribute.attrelid) ORDER BY attnum; attname | atttypid -------------------------------+------------------- tableoid | oid cmax | cid xmax | xid cmin | cid xmin | xid ctid | tid l_orderkey | bigint l_partkey | integer l_suppkey | integer l_linenumber | integer l_quantity | numeric l_extendedprice | numeric l_discount | numeric l_tax | numeric l_returnflag | character l_linestatus | character l_shipdate | date l_commitdate | date l_receiptdate | date l_shipinstruct | character l_shipmode | character l_comment | character varying ........pg.dropped.17........ | - ........pg.dropped.18........ | - ........pg.dropped.19........ | - ........pg.dropped.20........ | - null_column | integer ........pg.dropped.22........ | - ........pg.dropped.23........ | - ........pg.dropped.24........ | - (30 rows) \c - - - :master_port -- verify that we don't intercept DDL commands if propagation is turned off SET citus.enable_ddl_propagation to false; -- table rename statement can be performed now ALTER TABLE lineitem_alter RENAME TO lineitem_renamed; -- verify rename is performed SELECT relname FROM pg_class WHERE relname = 'lineitem_alter' or relname = 'lineitem_renamed'; relname ------------------ lineitem_renamed (1 row) -- revert it to original name ALTER TABLE lineitem_renamed RENAME TO lineitem_alter; -- this column is added to master table and not workers ALTER TABLE lineitem_alter ADD COLUMN column_only_added_to_master int; -- verify newly added column is not present in a worker shard \c - - - :worker_1_port SELECT column_only_added_to_master FROM lineitem_alter_220000 LIMIT 0; ERROR: column "column_only_added_to_master" does not exist LINE 1: SELECT column_only_added_to_master FROM lineitem_alter_22000... ^ \c - - - :master_port -- ddl propagation flag is reset to default, disable it again SET citus.enable_ddl_propagation to false; -- following query succeeds since it accesses an previously existing column SELECT l_orderkey FROM lineitem_alter LIMIT 0; l_orderkey ------------ (0 rows) -- make master and workers have the same schema again ALTER TABLE lineitem_alter DROP COLUMN column_only_added_to_master; -- now this should succeed SELECT * FROM lineitem_alter LIMIT 0; l_orderkey | l_partkey | l_suppkey | l_linenumber | l_quantity | l_extendedprice | l_discount | l_tax | l_returnflag | l_linestatus | l_shipdate | l_commitdate | l_receiptdate | l_shipinstruct | l_shipmode | l_comment | null_column ------------+-----------+-----------+--------------+------------+-----------------+------------+-------+--------------+--------------+------------+--------------+---------------+----------------+------------+-----------+------------- (0 rows) -- previously unsupported statements are accepted by postgresql now ALTER TABLE lineitem_alter ALTER COLUMN l_orderkey SET STATISTICS 100; ALTER TABLE lineitem_alter DROP CONSTRAINT IF EXISTS non_existent_contraint; NOTICE: constraint "non_existent_contraint" of relation "lineitem_alter" does not exist, skipping ALTER TABLE lineitem_alter SET WITHOUT OIDS; -- distribution column still cannot be dropped. ALTER TABLE lineitem_alter DROP COLUMN l_orderkey; ERROR: cannot execute ALTER TABLE command dropping partition column -- Even unique indexes on l_partkey (non-partition column) are allowed. -- Citus would have prevented that. CREATE UNIQUE INDEX unique_lineitem_partkey on lineitem_alter(l_partkey); SELECT indexname, tablename FROM pg_indexes WHERE tablename = 'lineitem_alter'; indexname | tablename -------------------------+---------------- unique_lineitem_partkey | lineitem_alter (1 row) -- verify index is not created on worker \c - - - :worker_1_port SELECT indexname, tablename FROM pg_indexes WHERE tablename like 'lineitem_alter_%'; indexname | tablename -----------+----------- (0 rows) \c - - - :master_port -- verify alter table and drop sequence in the same transaction does not cause deadlock CREATE TABLE sequence_deadlock_test (a serial, b serial); SELECT create_distributed_table('sequence_deadlock_test', 'a'); create_distributed_table -------------------------- (1 row) BEGIN; ALTER TABLE sequence_deadlock_test ADD COLUMN c int; NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' DROP SEQUENCE sequence_deadlock_test_b_seq CASCADE; NOTICE: drop cascades to default for table sequence_deadlock_test column b END; DROP TABLE sequence_deadlock_test; -- verify enable/disable trigger all works SET citus.shard_replication_factor TO 1; SET citus.shard_count TO 1; CREATE TABLE trigger_table ( id int, value text ); SELECT create_distributed_table('trigger_table', 'id'); create_distributed_table -------------------------- (1 row) -- first set a trigger on a shard \c - - - :worker_1_port CREATE FUNCTION update_value() RETURNS trigger AS $up$ BEGIN NEW.value := 'trigger enabled'; RETURN NEW; END; $up$ LANGUAGE plpgsql; CREATE TRIGGER update_value BEFORE INSERT ON trigger_table_220056 FOR EACH ROW EXECUTE PROCEDURE update_value(); \c - - - :master_port INSERT INTO trigger_table VALUES (1, 'trigger disabled'); SELECT value, count(*) FROM trigger_table GROUP BY value ORDER BY value; value | count -----------------+------- trigger enabled | 1 (1 row) ALTER TABLE trigger_table DISABLE TRIGGER ALL; NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' INSERT INTO trigger_table VALUES (1, 'trigger disabled'); SELECT value, count(*) FROM trigger_table GROUP BY value ORDER BY value; value | count ------------------+------- trigger disabled | 1 trigger enabled | 1 (2 rows) ALTER TABLE trigger_table ENABLE TRIGGER ALL; INSERT INTO trigger_table VALUES (1, 'trigger disabled'); SELECT value, count(*) FROM trigger_table GROUP BY value ORDER BY value; value | count ------------------+------- trigger disabled | 1 trigger enabled | 2 (2 rows) DROP TABLE trigger_table; -- test ALTER TABLE ALL IN TABLESPACE -- we expect that it will warn out CREATE TABLESPACE super_fast_ssd LOCATION '@abs_srcdir@/data'; ALTER TABLE ALL IN TABLESPACE pg_default SET TABLESPACE super_fast_ssd; WARNING: not propagating ALTER TABLE ALL IN TABLESPACE commands to worker nodes HINT: Connect to worker nodes directly to manually move all tables. ALTER TABLE ALL IN TABLESPACE super_fast_ssd SET TABLESPACE pg_default; WARNING: not propagating ALTER TABLE ALL IN TABLESPACE commands to worker nodes HINT: Connect to worker nodes directly to manually move all tables. DROP TABLESPACE super_fast_ssd; -- Cleanup the table and its shards SET citus.enable_ddl_propagation to true; SELECT master_apply_delete_command('DELETE FROM lineitem_alter'); master_apply_delete_command ----------------------------- 14 (1 row) DROP TABLE lineitem_alter; -- check that nothing's left over on workers, other than the leftover shard created -- during the unsuccessful COPY \c - - - :worker_1_port SELECT relname FROM pg_class WHERE relname LIKE 'lineitem_alter%'; relname ----------------------- lineitem_alter_220009 (1 row) \c - - - :master_port -- Test alter table with drop table in the same transaction BEGIN; CREATE TABLE test_table_1(id int); SELECT create_distributed_table('test_table_1','id'); create_distributed_table -------------------------- (1 row) ALTER TABLE test_table_1 ADD CONSTRAINT u_key UNIQUE(id); NOTICE: using one-phase commit for distributed DDL commands HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc' DROP TABLE test_table_1; END; -- There should be no test_table_1 shard on workers \c - - - :worker_1_port SELECT relname FROM pg_class WHERE relname LIKE 'test_table_1%'; relname --------- (0 rows) \c - - - :master_port