--
-- Distributed Partitioned Table Tests
--
SET citus.next_shard_id TO 1760000;
CREATE SCHEMA partitioned_table_replicated;
SET search_path TO partitioned_table_replicated;
SET citus.shard_count TO 4;
SET citus.shard_replication_factor TO 2;
CREATE TABLE collections (
	key bigint,
	ts timestamptz,
	collection_id integer,
	value numeric
) PARTITION BY LIST ( collection_id );
CREATE TABLE collections_1
	PARTITION OF collections (key, ts, collection_id, value)
	FOR VALUES IN ( 1 );
CREATE TABLE collections_2
	PARTITION OF collections (key, ts, collection_id, value)
	FOR VALUES IN ( 2 );
-- load some data data
INSERT INTO collections (key, ts, collection_id, value) VALUES (1, '2009-01-01', 1, 1);
INSERT INTO collections (key, ts, collection_id, value) VALUES (2, '2009-01-01', 1, 2);
INSERT INTO collections (key, ts, collection_id, value) VALUES (3, '2009-01-01', 2, 1);
INSERT INTO collections (key, ts, collection_id, value) VALUES (4, '2009-01-01', 2, 2);
-- in the first case, we'll distributed the
-- already existing partitioninong hierarcy
SELECT create_distributed_table('collections', 'key');
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
DETAIL:  The local data in the table is no longer visible, but is still on disk.
HINT:  To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$partitioned_table_replicated.collections_1$$)
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
DETAIL:  The local data in the table is no longer visible, but is still on disk.
HINT:  To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$partitioned_table_replicated.collections_2$$)
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- now create partition of a already distributed table
CREATE TABLE collections_3 PARTITION OF collections FOR VALUES IN ( 3 );
-- now attaching non distributed table to a distributed table
CREATE TABLE collections_4 AS SELECT * FROM collections LIMIT 0;
-- load some data
INSERT INTO collections_4 SELECT i, '2009-01-01', 4, i FROM generate_series (0, 10) i;
ALTER TABLE collections ATTACH PARTITION collections_4 FOR VALUES IN ( 4 );
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
DETAIL:  The local data in the table is no longer visible, but is still on disk.
HINT:  To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$partitioned_table_replicated.collections_4$$)
-- finally attach a distributed table to a distributed table
CREATE TABLE collections_5 AS SELECT * FROM collections LIMIT 0;
SELECT create_distributed_table('collections_5', 'key');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- load some data
INSERT INTO collections_5 SELECT i, '2009-01-01', 5, i FROM generate_series (0, 10) i;
ALTER TABLE collections ATTACH PARTITION collections_5 FOR VALUES IN ( 5 );
-- make sure that we've all the placements
SELECT
	logicalrelid, count(*) as placement_count
FROM
	pg_dist_shard, pg_dist_shard_placement
WHERE
	logicalrelid::text LIKE '%collections%' AND
	pg_dist_shard.shardid = pg_dist_shard_placement.shardid
GROUP BY
	logicalrelid
ORDER BY
	1,2;
 logicalrelid  | placement_count
---------------------------------------------------------------------
 collections   |               8
 collections_1 |               8
 collections_2 |               8
 collections_3 |               8
 collections_4 |               8
 collections_5 |               8
(6 rows)

-- and, make sure that all tables are colocated
SELECT
	count(DISTINCT colocationid)
FROM
	pg_dist_partition
WHERE
	logicalrelid::text LIKE '%collections%';
 count
---------------------------------------------------------------------
     1
(1 row)

-- make sure that any kind of modification is disallowed on partitions
-- given that replication factor > 1
INSERT INTO collections_4 (key, ts, collection_id, value) VALUES (4, '2009-01-01', 2, 2);
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- single shard update/delete not allowed
UPDATE collections_1 SET ts = now() WHERE key = 1;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
DELETE FROM collections_1 WHERE ts = now() AND key = 1;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- multi shard update/delete are not allowed
UPDATE collections_1 SET ts = now();
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
DELETE FROM collections_1 WHERE ts = now();
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- insert..select pushdown
INSERT INTO collections_1 SELECT * FROM collections_1;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- insert..select via coordinator
INSERT INTO collections_1 SELECT * FROM collections_1 OFFSET 0;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- COPY is not allowed
COPY collections_1 FROM STDIN;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
\.
invalid command \.
-- DDLs are not allowed
CREATE INDEX index_on_partition ON collections_1(key);
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- EXPLAIN with modifications is not allowed as well
UPDATE collections_1 SET ts = now() WHERE key = 1;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- TRUNCATE is also not allowed
TRUNCATE collections_1;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
TRUNCATE collections, collections_1;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- modifying CTEs are also not allowed
WITH collections_5_cte AS
(
	DELETE FROM collections_5 RETURNING *
)
SELECT * FROM collections_5_cte;
ERROR:  modifications on partitions when replication factor is greater than 1 is not supported
HINT:  Run the query on the parent table "collections" instead.
-- foreign key creation is disallowed due to replication factor > 1
CREATE TABLE fkey_test (key bigint PRIMARY KEY);
SELECT create_distributed_table('fkey_test', 'key');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

ALTER TABLE
	collections_5
ADD CONSTRAINT
	fkey_delete FOREIGN KEY(key)
REFERENCES
	fkey_test(key) ON DELETE CASCADE;
ERROR:  cannot create foreign key constraint
DETAIL:  Citus currently supports foreign key constraints only for "citus.shard_replication_factor = 1".
HINT:  Please change "citus.shard_replication_factor to 1". To learn more about using foreign keys with other replication factors, please contact us at https://citusdata.com/about/contact_us.
-- we should be able to attach and detach partitions
-- given that those DDLs are on the parent table
CREATE TABLE collections_6
	PARTITION OF collections (key, ts, collection_id, value)
	FOR VALUES IN ( 6 );
ALTER TABLE collections DETACH PARTITION collections_6;
ALTER TABLE collections ATTACH PARTITION collections_6 FOR VALUES IN ( 6 );
-- read queries works just fine
SELECT count(*) FROM collections_1 WHERE key = 1;
 count
---------------------------------------------------------------------
     1
(1 row)

SELECT count(*) FROM collections_1 WHERE key != 1;
 count
---------------------------------------------------------------------
     1
(1 row)

-- rollups SELECT'ing from partitions should work just fine
CREATE TABLE collections_agg (
	key bigint,
	sum_value numeric
);
SELECT create_distributed_table('collections_agg', 'key');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- pushdown roll-up
INSERT INTO collections_agg SELECT key, sum(key) FROM collections_1 GROUP BY key;
-- coordinator roll-up
INSERT INTO collections_agg SELECT collection_id, sum(key) FROM collections_1 GROUP BY collection_id;
-- now make sure that copy functionality works fine
-- create a table and create its distribution metadata
CREATE TABLE customer_engagements (id integer, event_id int) PARTITION BY LIST ( event_id );
CREATE TABLE customer_engagements_1
	PARTITION OF customer_engagements
	FOR VALUES IN ( 1 );
CREATE TABLE customer_engagements_2
	PARTITION OF customer_engagements
	FOR VALUES IN ( 2 );
-- add some indexes
CREATE INDEX ON customer_engagements (id);
CREATE INDEX ON customer_engagements (event_id);
CREATE INDEX ON customer_engagements (id, event_id);
-- distribute the table
-- create a single shard on the first worker
SET citus.shard_count TO 1;
SET citus.shard_replication_factor TO 2;
SELECT create_distributed_table('customer_engagements', 'id', 'hash', colocate_with := 'none');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- ingest some data for the tests
INSERT INTO customer_engagements VALUES (1, 1);
INSERT INTO customer_engagements VALUES (2, 1);
INSERT INTO customer_engagements VALUES (1, 2);
INSERT INTO customer_engagements VALUES (2, 2);
-- get the newshardid
SELECT shardid as newshardid FROM pg_dist_shard WHERE logicalrelid = 'customer_engagements'::regclass
\gset
-- delete all the placements on the second node belonging to partitioning hierarchy
DELETE FROM pg_dist_shard_placement p USING pg_dist_shard s
WHERE s.shardid = p.shardid AND nodeport = :worker_2_port AND logicalrelid::text LIKE 'customer_engagements%';
-- cannot copy a shard after a modification (transaction still open during copy)
BEGIN;
INSERT INTO customer_engagements VALUES (1, 1);
SELECT citus_copy_shard_placement(:newshardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port, transfer_mode := 'block_writes');
ERROR:  cannot open new connections after the first modification command within a transaction
ROLLBACK;
-- modifications after copying a shard are fine (will use new metadata)
BEGIN;
SELECT citus_copy_shard_placement(:newshardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port, transfer_mode := 'block_writes');
 citus_copy_shard_placement
---------------------------------------------------------------------

(1 row)

ALTER TABLE customer_engagements ADD COLUMN value float DEFAULT 1.0;
SELECT * FROM customer_engagements ORDER BY 1,2,3;
 id | event_id | value
---------------------------------------------------------------------
  1 |        1 |     1
  1 |        2 |     1
  2 |        1 |     1
  2 |        2 |     1
(4 rows)

ROLLBACK;
BEGIN;
SELECT citus_copy_shard_placement(:newshardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port, transfer_mode := 'block_writes');
 citus_copy_shard_placement
---------------------------------------------------------------------

(1 row)

INSERT INTO customer_engagements VALUES (1, 1);
SELECT count(*) FROM customer_engagements;
 count
---------------------------------------------------------------------
     5
(1 row)

ROLLBACK;
-- TRUNCATE is allowed on the parent table
-- try it just before dropping the table
TRUNCATE collections;
SET search_path TO public;
DROP SCHEMA partitioned_table_replicated CASCADE;
NOTICE:  drop cascades to 4 other objects
DETAIL:  drop cascades to table partitioned_table_replicated.collections
drop cascades to table partitioned_table_replicated.fkey_test
drop cascades to table partitioned_table_replicated.collections_agg
drop cascades to table partitioned_table_replicated.customer_engagements
