--
-- Failure tests for COPY to hash distributed tables
--
CREATE SCHEMA copy_distributed_table;
SET search_path TO 'copy_distributed_table';
SET citus.next_shard_id TO 1710000;
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

-- With one placement COPY should error out and placement should stay healthy.
SET citus.shard_replication_factor TO 1;
SET citus.shard_count to 4;
SET citus.max_cached_conns_per_worker to 0;
CREATE TABLE test_table(id int, value_1 int);
SELECT create_distributed_table('test_table','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW unhealthy_shard_count AS
	SELECT count(*)
	FROM pg_dist_shard_placement pdsp
	JOIN
	pg_dist_shard pds
	ON pdsp.shardid=pds.shardid
	WHERE logicalrelid='copy_distributed_table.test_table'::regclass AND shardstate != 1;
-- Just kill the connection after sending the first query to the worker.
SELECT citus.mitmproxy('conn.kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table FROM stdin delimiter ',';
ERROR:  connection to the remote node postgres@localhost:xxxxx failed with the following error: connection not open
CONTEXT:  COPY test_table, line 1: "1,2"
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     0
(1 row)

-- Now, kill the connection while copying the data
SELECT citus.mitmproxy('conn.onCopyData().kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table FROM stdin delimiter ',';
ERROR:  failed to COPY to shard xxxxx on localhost:xxxxx
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     0
(1 row)

-- Similar to the above one, but now cancel the connection
-- instead of killing it.
SELECT citus.mitmproxy('conn.onCopyData().cancel(' ||  pg_backend_pid() || ')');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table FROM stdin delimiter ',';
ERROR:  canceling statement due to user request
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     0
(1 row)

-- kill the connection after worker sends command complete message
SELECT citus.mitmproxy('conn.onCommandComplete(command="COPY 1").kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table FROM stdin delimiter ',';
ERROR:  failed to COPY to shard xxxxx on localhost:xxxxx
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     0
(1 row)

-- similar to above one, but cancel the connection on command complete
SELECT citus.mitmproxy('conn.onCommandComplete(command="COPY 1").cancel(' || pg_backend_pid() || ')');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table FROM stdin delimiter ',';
ERROR:  canceling statement due to user request
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     0
(1 row)

-- kill the connection on PREPARE TRANSACTION
SELECT citus.mitmproxy('conn.onQuery(query="PREPARE TRANSACTION").kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table FROM stdin delimiter ',';
ERROR:  connection not open
CONTEXT:  while executing command on localhost:xxxxx
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     0
(1 row)

-- we don't want to see the prepared transaction numbers in the warnings
SET client_min_messages TO ERROR;
-- kill on command complete on COMMIT PREPARE, command should succeed
SELECT citus.mitmproxy('conn.onCommandComplete(command="COMMIT PREPARED").kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table FROM stdin delimiter ',';
SET client_min_messages TO NOTICE;
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     4
(1 row)

TRUNCATE TABLE test_table;
-- kill on ROLLBACK, command could be rollbacked
SELECT citus.mitmproxy('conn.onQuery(query="ROLLBACK").kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

BEGIN;
\COPY test_table FROM stdin delimiter ',';
ROLLBACK;
WARNING:  connection not open
CONTEXT:  while executing command on localhost:xxxxx
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT * FROM unhealthy_shard_count;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM test_table;
 count
---------------------------------------------------------------------
     0
(1 row)

DROP TABLE test_table CASCADE;
NOTICE:  drop cascades to view unhealthy_shard_count
-- With two placement, should we error out or mark untouched shard placements as inactive?
SET citus.shard_replication_factor TO 2;
CREATE TABLE test_table_2(id int, value_1 int);
SELECT create_distributed_table('test_table_2','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT citus.mitmproxy('conn.kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table_2 FROM stdin delimiter ',';
ERROR:  connection to the remote node postgres@localhost:xxxxx failed with the following error: connection not open
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT pds.logicalrelid, pdsd.shardid, pdsd.shardstate
	FROM pg_dist_shard_placement as pdsd
	INNER JOIN pg_dist_shard as pds
	ON pdsd.shardid = pds.shardid
	WHERE pds.logicalrelid = 'test_table_2'::regclass
	ORDER BY shardid, nodeport;
 logicalrelid | shardid | shardstate
---------------------------------------------------------------------
 test_table_2 | 1710004 |          1
 test_table_2 | 1710004 |          1
 test_table_2 | 1710005 |          1
 test_table_2 | 1710005 |          1
 test_table_2 | 1710006 |          1
 test_table_2 | 1710006 |          1
 test_table_2 | 1710007 |          1
 test_table_2 | 1710007 |          1
(8 rows)

-- Create test_table_2 again to have healthy one
DROP TABLE test_table_2;
CREATE TABLE test_table_2(id int, value_1 int);
SELECT create_distributed_table('test_table_2','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- Kill the connection when we try to start the COPY
-- The query should abort
SELECT citus.mitmproxy('conn.onQuery(query="FROM STDIN WITH").killall()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table_2 FROM stdin delimiter ',';
ERROR:  connection not open
CONTEXT:  while executing command on localhost:xxxxx
COPY test_table_2, line 1: "1,2"
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT pds.logicalrelid, pdsd.shardid, pdsd.shardstate
	FROM pg_dist_shard_placement as pdsd
	INNER JOIN pg_dist_shard as pds
	ON pdsd.shardid = pds.shardid
	WHERE pds.logicalrelid = 'test_table_2'::regclass
	ORDER BY shardid, nodeport;
 logicalrelid | shardid | shardstate
---------------------------------------------------------------------
 test_table_2 | 1710008 |          1
 test_table_2 | 1710008 |          1
 test_table_2 | 1710009 |          1
 test_table_2 | 1710009 |          1
 test_table_2 | 1710010 |          1
 test_table_2 | 1710010 |          1
 test_table_2 | 1710011 |          1
 test_table_2 | 1710011 |          1
(8 rows)

-- Create test_table_2 again to have healthy one
DROP TABLE test_table_2;
CREATE TABLE test_table_2(id int, value_1 int);
SELECT create_distributed_table('test_table_2','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- When kill on copying data, it will be rollbacked and placements won't be labaled as invalid.
-- Note that now we sent data to shard xxxxx, yet it is not marked as invalid.
-- You can check the issue about this behaviour: https://github.com/citusdata/citus/issues/1933
SELECT citus.mitmproxy('conn.onCopyData().kill()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

\COPY test_table_2 FROM stdin delimiter ',';
ERROR:  failed to COPY to shard xxxxx on localhost:xxxxx
SELECT citus.mitmproxy('conn.allow()');
 mitmproxy
---------------------------------------------------------------------

(1 row)

SELECT pds.logicalrelid, pdsd.shardid, pdsd.shardstate
	FROM pg_dist_shard_placement as pdsd
	INNER JOIN pg_dist_shard as pds
	ON pdsd.shardid = pds.shardid
	WHERE pds.logicalrelid = 'test_table_2'::regclass
	ORDER BY shardid, nodeport;
 logicalrelid | shardid | shardstate
---------------------------------------------------------------------
 test_table_2 | 1710012 |          1
 test_table_2 | 1710012 |          1
 test_table_2 | 1710013 |          1
 test_table_2 | 1710013 |          1
 test_table_2 | 1710014 |          1
 test_table_2 | 1710014 |          1
 test_table_2 | 1710015 |          1
 test_table_2 | 1710015 |          1
(8 rows)

DROP SCHEMA copy_distributed_table CASCADE;
NOTICE:  drop cascades to table test_table_2
SET search_path TO default;
