--
-- MULTI_TRANSACTIONAL_DROP_SHARDS
--
-- Tests that check the metadata returned by the master node.
ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1410000;
SET citus.shard_count TO 4;
-- test DROP TABLE(ergo master_drop_all_shards) in transaction, then ROLLBACK
CREATE TABLE transactional_drop_shards(column1 int);
SELECT create_distributed_table('transactional_drop_shards', 'column1');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

BEGIN;
DROP TABLE transactional_drop_shards;
ROLLBACK;
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410000
 1410001
 1410002
 1410003
(4 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410000 |          1 | localhost |    57637
 1410000 |          1 | localhost |    57638
 1410001 |          1 | localhost |    57637
 1410001 |          1 | localhost |    57638
 1410002 |          1 | localhost |    57637
 1410002 |          1 | localhost |    57638
 1410003 |          1 | localhost |    57637
 1410003 |          1 | localhost |    57638
(8 rows)

-- verify table is not dropped
\dt transactional_drop_shards
                   List of relations
 Schema |           Name            | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards | table | postgres
(1 row)

-- verify shards are not dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
                       List of relations
 Schema |               Name                | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards_1410000 | table | postgres
 public | transactional_drop_shards_1410001 | table | postgres
 public | transactional_drop_shards_1410002 | table | postgres
 public | transactional_drop_shards_1410003 | table | postgres
(4 rows)

\c - - - :master_port
-- test DROP TABLE(ergo master_drop_all_shards) in transaction, then COMMIT
BEGIN;
DROP TABLE transactional_drop_shards;
COMMIT;
-- verify metadata is deleted
SELECT shardid FROM pg_dist_shard WHERE shardid IN (1410000, 1410001, 1410002, 1410003) ORDER BY shardid;
 shardid
---------------------------------------------------------------------
(0 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (1410000, 1410001, 1410002, 1410003)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename | nodeport
---------------------------------------------------------------------
(0 rows)

-- verify table is dropped
\dt transactional_drop_shards
      List of relations
 Schema | Name | Type | Owner
---------------------------------------------------------------------
(0 rows)

-- verify shards are dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
      List of relations
 Schema | Name | Type | Owner
---------------------------------------------------------------------
(0 rows)

\c - - - :master_port
-- test master_delete_protocol in transaction, then ROLLBACK
CREATE TABLE transactional_drop_shards(column1 int);
SELECT create_distributed_table('transactional_drop_shards', 'column1', 'append');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT master_create_empty_shard('transactional_drop_shards');
 master_create_empty_shard
---------------------------------------------------------------------
                   1410004
(1 row)

BEGIN;
SELECT master_apply_delete_command('DELETE FROM transactional_drop_shards');
 master_apply_delete_command
---------------------------------------------------------------------
                           1
(1 row)

ROLLBACK;
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410004
(1 row)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410004 |          1 | localhost |    57637
 1410004 |          1 | localhost |    57638
(2 rows)

-- verify shards are not dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
                       List of relations
 Schema |               Name                | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards_1410004 | table | postgres
(1 row)

\c - - - :master_port
-- test master_delete_protocol in transaction, then COMMIT
BEGIN;
SELECT master_apply_delete_command('DELETE FROM transactional_drop_shards');
 master_apply_delete_command
---------------------------------------------------------------------
                           1
(1 row)

COMMIT;
-- verify metadata is deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
(0 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename | nodeport
---------------------------------------------------------------------
(0 rows)

-- verify shards are dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
      List of relations
 Schema | Name | Type | Owner
---------------------------------------------------------------------
(0 rows)

\c - - - :master_port
-- test DROP table in a transaction after insertion
SELECT master_create_empty_shard('transactional_drop_shards');
 master_create_empty_shard
---------------------------------------------------------------------
                   1410005
(1 row)

BEGIN;
INSERT INTO transactional_drop_shards VALUES (1);
DROP TABLE transactional_drop_shards;
ROLLBACK;
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410005
(1 row)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410005 |          1 | localhost |    57637
 1410005 |          1 | localhost |    57638
(2 rows)

-- verify table is not dropped
\dt transactional_drop_shards
                   List of relations
 Schema |           Name            | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards | table | postgres
(1 row)

-- verify shards are not dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
                       List of relations
 Schema |               Name                | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards_1410005 | table | postgres
(1 row)

\c - - - :master_port
-- test master_apply_delete_command in a transaction after insertion
BEGIN;
INSERT INTO transactional_drop_shards VALUES (1);
SELECT master_apply_delete_command('DELETE FROM transactional_drop_shards');
 master_apply_delete_command
---------------------------------------------------------------------
                           1
(1 row)

ROLLBACK;
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410005
(1 row)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410005 |          1 | localhost |    57637
 1410005 |          1 | localhost |    57638
(2 rows)

-- verify shards are not dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
                       List of relations
 Schema |               Name                | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards_1410005 | table | postgres
(1 row)

-- test DROP table with failing worker
CREATE FUNCTION fail_drop_table() RETURNS event_trigger AS $fdt$
    BEGIN
        RAISE 'illegal value';
    END;
$fdt$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER fail_drop_table ON sql_drop EXECUTE PROCEDURE fail_drop_table();
\c - - - :master_port
\set VERBOSITY terse
DROP TABLE transactional_drop_shards;
ERROR:  illegal value
\set VERBOSITY default
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410005
(1 row)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410005 |          1 | localhost |    57637
 1410005 |          1 | localhost |    57638
(2 rows)

-- verify table is not dropped
\dt transactional_drop_shards
                   List of relations
 Schema |           Name            | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards | table | postgres
(1 row)

-- verify shards are not dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
                       List of relations
 Schema |               Name                | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards_1410005 | table | postgres
(1 row)

\c - - - :master_port
-- test DROP reference table with failing worker
CREATE TABLE transactional_drop_reference(column1 int);
SELECT create_reference_table('transactional_drop_reference');
 create_reference_table
---------------------------------------------------------------------

(1 row)

\set VERBOSITY terse
DROP TABLE transactional_drop_reference;
ERROR:  illegal value
\set VERBOSITY default
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_reference'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410006
(1 row)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_reference'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410006 |          1 | localhost |    57637
 1410006 |          1 | localhost |    57638
(2 rows)

-- verify table is not dropped
\dt transactional_drop_reference
                    List of relations
 Schema |             Name             | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_reference | table | postgres
(1 row)

-- verify shards are not dropped
\c - - - :worker_1_port
\dt transactional_drop_reference*
                        List of relations
 Schema |                 Name                 | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_reference_1410006 | table | postgres
(1 row)

\c - - - :master_port
-- test master_apply_delete_command table with failing worker
\set VERBOSITY terse
SELECT master_apply_delete_command('DELETE FROM transactional_drop_shards');
ERROR:  illegal value
\set VERBOSITY default
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410005
(1 row)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_shards'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410005 |          1 | localhost |    57637
 1410005 |          1 | localhost |    57638
(2 rows)

-- verify shards are not dropped
\c - - - :worker_1_port
\dt transactional_drop_shards_*
                       List of relations
 Schema |               Name                | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_shards_1410005 | table | postgres
(1 row)

DROP EVENT TRIGGER fail_drop_table;
\c - - - :master_port
-- test with SERIAL column + with more shards
SET citus.shard_count TO 8;
CREATE TABLE transactional_drop_serial(column1 int, column2 SERIAL);
SELECT create_distributed_table('transactional_drop_serial', 'column1');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- test DROP TABLE(ergo master_drop_all_shards) in transaction, then ROLLBACK
BEGIN;
DROP TABLE transactional_drop_serial;
ROLLBACK;
-- verify metadata is not deleted
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_serial'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410007
 1410008
 1410009
 1410010
 1410011
 1410012
 1410013
 1410014
(8 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_serial'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410007 |          1 | localhost |    57637
 1410007 |          1 | localhost |    57638
 1410008 |          1 | localhost |    57637
 1410008 |          1 | localhost |    57638
 1410009 |          1 | localhost |    57637
 1410009 |          1 | localhost |    57638
 1410010 |          1 | localhost |    57637
 1410010 |          1 | localhost |    57638
 1410011 |          1 | localhost |    57637
 1410011 |          1 | localhost |    57638
 1410012 |          1 | localhost |    57637
 1410012 |          1 | localhost |    57638
 1410013 |          1 | localhost |    57637
 1410013 |          1 | localhost |    57638
 1410014 |          1 | localhost |    57637
 1410014 |          1 | localhost |    57638
(16 rows)

-- verify table is not dropped
\dt transactional_drop_serial
                   List of relations
 Schema |           Name            | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_serial | table | postgres
(1 row)

-- verify shards and sequence are not dropped
\c - - - :worker_1_port
\dt transactional_drop_serial_*
                       List of relations
 Schema |               Name                | Type  |  Owner
---------------------------------------------------------------------
 public | transactional_drop_serial_1410007 | table | postgres
 public | transactional_drop_serial_1410008 | table | postgres
 public | transactional_drop_serial_1410009 | table | postgres
 public | transactional_drop_serial_1410010 | table | postgres
 public | transactional_drop_serial_1410011 | table | postgres
 public | transactional_drop_serial_1410012 | table | postgres
 public | transactional_drop_serial_1410013 | table | postgres
 public | transactional_drop_serial_1410014 | table | postgres
(8 rows)

\ds transactional_drop_serial_column2_seq
      List of relations
 Schema | Name | Type | Owner
---------------------------------------------------------------------
(0 rows)

\c - - - :master_port
-- test DROP TABLE(ergo master_drop_all_shards) in transaction, then COMMIT
BEGIN;
DROP TABLE transactional_drop_serial;
COMMIT;
-- verify metadata is deleted
SELECT shardid FROM pg_dist_shard WHERE shardid IN (1410007, 1410008, 1410009, 1410010, 1410011, 1410012, 1410013, 1410014) ORDER BY shardid;
 shardid
---------------------------------------------------------------------
(0 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (1410007, 1410008, 1410009, 1410010, 1410011, 1410012, 1410013, 1410014)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename | nodeport
---------------------------------------------------------------------
(0 rows)

-- verify table is dropped
\dt transactional_drop_serial
      List of relations
 Schema | Name | Type | Owner
---------------------------------------------------------------------
(0 rows)

-- verify shards and sequence are dropped
\c - - - :worker_1_port
\dt transactional_drop_serial_*
      List of relations
 Schema | Name | Type | Owner
---------------------------------------------------------------------
(0 rows)

\ds transactional_drop_serial_column2_seq
      List of relations
 Schema | Name | Type | Owner
---------------------------------------------------------------------
(0 rows)

\c - - - :master_port
-- test with MX, DROP TABLE, then ROLLBACK
SET citus.shard_replication_factor TO 1;
SET citus.shard_count TO 4;
CREATE TABLE transactional_drop_mx(column1 int);
SELECT create_distributed_table('transactional_drop_mx', 'column1');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='transactional_drop_mx'::regclass;
-- make worker 1 receive metadata changes
SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
 start_metadata_sync_to_node
---------------------------------------------------------------------

(1 row)

-- see metadata is propogated to the worker
\c - - - :worker_1_port
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_mx'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410015
 1410016
 1410017
 1410018
(4 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_mx'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410015 |          1 | localhost |    57637
 1410016 |          1 | localhost |    57638
 1410017 |          1 | localhost |    57637
 1410018 |          1 | localhost |    57638
(4 rows)

\c - - - :master_port
BEGIN;
DROP TABLE transactional_drop_mx;
ROLLBACK;
-- verify metadata is not deleted
\c - - - :worker_1_port
SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_mx'::regclass ORDER BY shardid;
 shardid
---------------------------------------------------------------------
 1410015
 1410016
 1410017
 1410018
(4 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'transactional_drop_mx'::regclass ORDER BY shardid)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename  | nodeport
---------------------------------------------------------------------
 1410015 |          1 | localhost |    57637
 1410016 |          1 | localhost |    57638
 1410017 |          1 | localhost |    57637
 1410018 |          1 | localhost |    57638
(4 rows)

-- test with MX, DROP TABLE, then COMMIT
\c - - - :master_port
BEGIN;
DROP TABLE transactional_drop_mx;
COMMIT;
-- verify metadata is deleted
\c - - - :worker_1_port
SELECT shardid FROM pg_dist_shard WHERE shardid IN (1410015, 1410016, 1410017, 1410018) ORDER BY shardid;
 shardid
---------------------------------------------------------------------
(0 rows)

SELECT
    shardid, shardstate, nodename, nodeport
FROM
    pg_dist_shard_placement
WHERE
    shardid IN (1410015, 1410016, 1410017, 1410018)
ORDER BY
    shardid, nodename, nodeport;
 shardid | shardstate | nodename | nodeport
---------------------------------------------------------------------
(0 rows)

\c - - - :master_port
-- try using the coordinator as a worker and then dropping the table
SELECT 1 FROM master_add_node('localhost', :master_port);
NOTICE:  Replicating reference table "transactional_drop_reference" to the node localhost:xxxxx
 ?column?
---------------------------------------------------------------------
        1
(1 row)

CREATE TABLE citus_local (id serial, k int);
SELECT create_distributed_table('citus_local', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

INSERT INTO citus_local (k) VALUES (2);
DROP TABLE citus_local;
SELECT master_remove_node('localhost', :master_port);
 master_remove_node
---------------------------------------------------------------------

(1 row)

-- clean the workspace
DROP TABLE transactional_drop_shards, transactional_drop_reference;
SELECT stop_metadata_sync_to_node('localhost', :worker_1_port);
 stop_metadata_sync_to_node
---------------------------------------------------------------------

(1 row)

-- test DROP TABLE as a non-superuser in a transaction block
CREATE USER try_drop_table WITH LOGIN;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
SELECT run_command_on_workers('CREATE USER try_drop_table WITH LOGIN');
      run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"CREATE ROLE")
 (localhost,57638,t,"CREATE ROLE")
(2 rows)

GRANT ALL ON SCHEMA public TO try_drop_table;
\c - try_drop_table - :master_port
BEGIN;
CREATE TABLE temp_dist_table (x int, y int);
SELECT create_distributed_table('temp_dist_table','x');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

DROP TABLE temp_dist_table;
END;
