--
-- MULTI_INDEX_STATEMENTS
--
-- Check that we can run CREATE INDEX and DROP INDEX statements on distributed
-- tables.
--
-- CREATE TEST TABLES
--
SET citus.next_shard_id TO 102080;
CREATE TABLE index_test_range(a int, b int, c int);
SELECT create_distributed_table('index_test_range', 'a', 'range');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT master_create_empty_shard('index_test_range');
 master_create_empty_shard
---------------------------------------------------------------------
                    102080
(1 row)

SELECT master_create_empty_shard('index_test_range');
 master_create_empty_shard
---------------------------------------------------------------------
                    102081
(1 row)

SET citus.shard_count TO 8;
SET citus.shard_replication_factor TO 2;
CREATE TABLE index_test_hash(a int, b int, c int);
SELECT create_distributed_table('index_test_hash', 'a', 'hash');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE TABLE index_test_append(a int, b int, c int);
SELECT create_distributed_table('index_test_append', 'a', 'append');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT master_create_empty_shard('index_test_append');
 master_create_empty_shard
---------------------------------------------------------------------
                    102090
(1 row)

SELECT master_create_empty_shard('index_test_append');
 master_create_empty_shard
---------------------------------------------------------------------
                    102091
(1 row)

--
-- CREATE INDEX
--
-- Verify that we can create different types of indexes
CREATE INDEX lineitem_orderkey_index ON lineitem (l_orderkey);
CREATE INDEX lineitem_partkey_desc_index ON lineitem (l_partkey DESC);
CREATE INDEX lineitem_partial_index ON lineitem (l_shipdate)
	WHERE l_shipdate < '1995-01-01';
CREATE INDEX lineitem_colref_index ON lineitem (record_ne(lineitem.*, NULL));
SET client_min_messages = ERROR; -- avoid version dependant warning about WAL
CREATE INDEX lineitem_orderkey_hash_index ON lineitem USING hash (l_partkey);
CREATE UNIQUE INDEX index_test_range_index_a ON index_test_range(a);
CREATE UNIQUE INDEX index_test_range_index_a_b ON index_test_range(a,b);
CREATE UNIQUE INDEX index_test_hash_index_a ON index_test_hash(a);
CREATE UNIQUE INDEX index_test_hash_index_a_b ON index_test_hash(a,b);
CREATE UNIQUE INDEX index_test_hash_index_a_b_partial ON index_test_hash(a,b) WHERE c IS NOT NULL;
CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON index_test_range(a,b) WHERE c IS NOT NULL;
CREATE UNIQUE INDEX index_test_hash_index_a_b_c ON index_test_hash(a) INCLUDE (b,c);
RESET client_min_messages;
-- Verify that we handle if not exists statements correctly
CREATE INDEX lineitem_orderkey_index on lineitem(l_orderkey);
ERROR:  relation "lineitem_orderkey_index" already exists
CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on lineitem(l_orderkey);
NOTICE:  relation "lineitem_orderkey_index" already exists, skipping
CREATE INDEX IF NOT EXISTS lineitem_orderkey_index_new on lineitem(l_orderkey);
-- Verify if not exists behavior with an index with same name on a different table
CREATE INDEX lineitem_orderkey_index on index_test_hash(a);
ERROR:  relation "lineitem_orderkey_index" already exists
CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on index_test_hash(a);
NOTICE:  relation "lineitem_orderkey_index" already exists, skipping
-- Verify that we can create indexes concurrently
CREATE INDEX CONCURRENTLY lineitem_concurrently_index ON lineitem (l_orderkey);
-- Verify that we warn out on CLUSTER command for distributed tables and no parameter
CLUSTER index_test_hash USING index_test_hash_index_a;
WARNING:  not propagating CLUSTER command to worker nodes
CLUSTER;
WARNING:  not propagating CLUSTER command to worker nodes
-- Verify that no-name local CREATE INDEX CONCURRENTLY works
CREATE TABLE local_table (id integer, name text);
CREATE INDEX CONCURRENTLY local_table_index ON local_table(id);
-- Vefify we don't warn out on CLUSTER command for local tables
CLUSTER local_table USING local_table_index;
DROP TABLE local_table;
-- Verify that all indexes got created on the master node and one of the workers
SELECT * FROM pg_indexes WHERE tablename = 'lineitem' or tablename like 'index_test_%' ORDER BY indexname;
 schemaname |    tablename     |             indexname              | tablespace |                                                          indexdef
---------------------------------------------------------------------
 public     | index_test_hash  | index_test_hash_index_a            |            | CREATE UNIQUE INDEX index_test_hash_index_a ON public.index_test_hash USING btree (a)
 public     | index_test_hash  | index_test_hash_index_a_b          |            | CREATE UNIQUE INDEX index_test_hash_index_a_b ON public.index_test_hash USING btree (a, b)
 public     | index_test_hash  | index_test_hash_index_a_b_c        |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c ON public.index_test_hash USING btree (a) INCLUDE (b, c)
 public     | index_test_hash  | index_test_hash_index_a_b_partial  |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_partial ON public.index_test_hash USING btree (a, b) WHERE (c IS NOT NULL)
 public     | index_test_range | index_test_range_index_a           |            | CREATE UNIQUE INDEX index_test_range_index_a ON public.index_test_range USING btree (a)
 public     | index_test_range | index_test_range_index_a_b         |            | CREATE UNIQUE INDEX index_test_range_index_a_b ON public.index_test_range USING btree (a, b)
 public     | index_test_range | index_test_range_index_a_b_partial |            | CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON public.index_test_range USING btree (a, b) WHERE (c IS NOT NULL)
 public     | lineitem         | lineitem_colref_index              |            | CREATE INDEX lineitem_colref_index ON public.lineitem USING btree (record_ne(lineitem.*, NULL::record))
 public     | lineitem         | lineitem_concurrently_index        |            | CREATE INDEX lineitem_concurrently_index ON public.lineitem USING btree (l_orderkey)
 public     | lineitem         | lineitem_orderkey_hash_index       |            | CREATE INDEX lineitem_orderkey_hash_index ON public.lineitem USING hash (l_partkey)
 public     | lineitem         | lineitem_orderkey_index            |            | CREATE INDEX lineitem_orderkey_index ON public.lineitem USING btree (l_orderkey)
 public     | lineitem         | lineitem_orderkey_index_new        |            | CREATE INDEX lineitem_orderkey_index_new ON public.lineitem USING btree (l_orderkey)
 public     | lineitem         | lineitem_partial_index             |            | CREATE INDEX lineitem_partial_index ON public.lineitem USING btree (l_shipdate) WHERE (l_shipdate < '01-01-1995'::date)
 public     | lineitem         | lineitem_partkey_desc_index        |            | CREATE INDEX lineitem_partkey_desc_index ON public.lineitem USING btree (l_partkey DESC)
 public     | lineitem         | lineitem_pkey                      |            | CREATE UNIQUE INDEX lineitem_pkey ON public.lineitem USING btree (l_orderkey, l_linenumber)
 public     | lineitem         | lineitem_time_index                |            | CREATE INDEX lineitem_time_index ON public.lineitem USING btree (l_shipdate)
(16 rows)

\c - - - :worker_1_port
SELECT count(*) FROM pg_indexes WHERE tablename = (SELECT relname FROM pg_class WHERE relname LIKE 'lineitem%' ORDER BY relname LIMIT 1);
 count
---------------------------------------------------------------------
     9
(1 row)

SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_hash%';
 count
---------------------------------------------------------------------
    32
(1 row)

SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_range%';
 count
---------------------------------------------------------------------
     6
(1 row)

SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_append%';
 count
---------------------------------------------------------------------
     0
(1 row)

\c - - - :master_port
-- Verify that we error out on unsupported statement types
CREATE UNIQUE INDEX try_index ON lineitem (l_orderkey);
ERROR:  creating unique indexes on append-partitioned tables is currently unsupported
CREATE INDEX try_index ON lineitem (l_orderkey) TABLESPACE newtablespace;
ERROR:  specifying tablespaces with CREATE INDEX statements is currently unsupported
CREATE UNIQUE INDEX try_unique_range_index ON index_test_range(b);
ERROR:  creating unique indexes on non-partition columns is currently unsupported
CREATE UNIQUE INDEX try_unique_range_index_partial ON index_test_range(b) WHERE c IS NOT NULL;
ERROR:  creating unique indexes on non-partition columns is currently unsupported
CREATE UNIQUE INDEX try_unique_hash_index ON index_test_hash(b);
ERROR:  creating unique indexes on non-partition columns is currently unsupported
CREATE UNIQUE INDEX try_unique_hash_index_partial ON index_test_hash(b) WHERE c IS NOT NULL;
ERROR:  creating unique indexes on non-partition columns is currently unsupported
CREATE UNIQUE INDEX try_unique_append_index ON index_test_append(b);
ERROR:  creating unique indexes on append-partitioned tables is currently unsupported
CREATE UNIQUE INDEX try_unique_append_index ON index_test_append(a);
ERROR:  creating unique indexes on append-partitioned tables is currently unsupported
CREATE UNIQUE INDEX try_unique_append_index_a_b ON index_test_append(a,b);
ERROR:  creating unique indexes on append-partitioned tables is currently unsupported
-- Verify that we error out in case of postgres errors on supported statement
-- types.
CREATE INDEX lineitem_orderkey_index ON lineitem (l_orderkey);
ERROR:  relation "lineitem_orderkey_index" already exists
CREATE INDEX try_index ON lineitem USING gist (l_orderkey);
ERROR:  data type bigint has no default operator class for access method "gist"
HINT:  You must specify an operator class for the index or define a default operator class for the data type.
CREATE INDEX try_index ON lineitem (non_existent_column);
ERROR:  column "non_existent_column" does not exist
CREATE INDEX ON lineitem (l_orderkey);
ERROR:  creating index without a name on a distributed table is currently unsupported
-- Verify that none of failed indexes got created on the master node
SELECT * FROM pg_indexes WHERE tablename = 'lineitem' or tablename like 'index_test_%' ORDER BY indexname;
 schemaname |    tablename     |             indexname              | tablespace |                                                          indexdef
---------------------------------------------------------------------
 public     | index_test_hash  | index_test_hash_index_a            |            | CREATE UNIQUE INDEX index_test_hash_index_a ON public.index_test_hash USING btree (a)
 public     | index_test_hash  | index_test_hash_index_a_b          |            | CREATE UNIQUE INDEX index_test_hash_index_a_b ON public.index_test_hash USING btree (a, b)
 public     | index_test_hash  | index_test_hash_index_a_b_c        |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c ON public.index_test_hash USING btree (a) INCLUDE (b, c)
 public     | index_test_hash  | index_test_hash_index_a_b_partial  |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_partial ON public.index_test_hash USING btree (a, b) WHERE (c IS NOT NULL)
 public     | index_test_range | index_test_range_index_a           |            | CREATE UNIQUE INDEX index_test_range_index_a ON public.index_test_range USING btree (a)
 public     | index_test_range | index_test_range_index_a_b         |            | CREATE UNIQUE INDEX index_test_range_index_a_b ON public.index_test_range USING btree (a, b)
 public     | index_test_range | index_test_range_index_a_b_partial |            | CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON public.index_test_range USING btree (a, b) WHERE (c IS NOT NULL)
 public     | lineitem         | lineitem_colref_index              |            | CREATE INDEX lineitem_colref_index ON public.lineitem USING btree (record_ne(lineitem.*, NULL::record))
 public     | lineitem         | lineitem_concurrently_index        |            | CREATE INDEX lineitem_concurrently_index ON public.lineitem USING btree (l_orderkey)
 public     | lineitem         | lineitem_orderkey_hash_index       |            | CREATE INDEX lineitem_orderkey_hash_index ON public.lineitem USING hash (l_partkey)
 public     | lineitem         | lineitem_orderkey_index            |            | CREATE INDEX lineitem_orderkey_index ON public.lineitem USING btree (l_orderkey)
 public     | lineitem         | lineitem_orderkey_index_new        |            | CREATE INDEX lineitem_orderkey_index_new ON public.lineitem USING btree (l_orderkey)
 public     | lineitem         | lineitem_partial_index             |            | CREATE INDEX lineitem_partial_index ON public.lineitem USING btree (l_shipdate) WHERE (l_shipdate < '01-01-1995'::date)
 public     | lineitem         | lineitem_partkey_desc_index        |            | CREATE INDEX lineitem_partkey_desc_index ON public.lineitem USING btree (l_partkey DESC)
 public     | lineitem         | lineitem_pkey                      |            | CREATE UNIQUE INDEX lineitem_pkey ON public.lineitem USING btree (l_orderkey, l_linenumber)
 public     | lineitem         | lineitem_time_index                |            | CREATE INDEX lineitem_time_index ON public.lineitem USING btree (l_shipdate)
(16 rows)

--
-- REINDEX
--
REINDEX INDEX lineitem_orderkey_index;
REINDEX TABLE lineitem;
REINDEX SCHEMA public;
REINDEX DATABASE regression;
REINDEX SYSTEM regression;
--
-- DROP INDEX
--
-- Verify that we can't drop multiple indexes in a single command
DROP INDEX lineitem_orderkey_index, lineitem_partial_index;
ERROR:  cannot drop multiple distributed objects in a single command
HINT:  Try dropping each object in a separate DROP command.
-- Verify that we can succesfully drop indexes
DROP INDEX lineitem_orderkey_index;
DROP INDEX lineitem_orderkey_index_new;
DROP INDEX lineitem_partkey_desc_index;
DROP INDEX lineitem_partial_index;
DROP INDEX lineitem_colref_index;
-- Verify that we handle if exists statements correctly
DROP INDEX non_existent_index;
ERROR:  index "non_existent_index" does not exist
DROP INDEX IF EXISTS non_existent_index;
NOTICE:  index "non_existent_index" does not exist, skipping
DROP INDEX IF EXISTS lineitem_orderkey_hash_index;
DROP INDEX lineitem_orderkey_hash_index;
ERROR:  index "lineitem_orderkey_hash_index" does not exist
DROP INDEX index_test_range_index_a;
DROP INDEX index_test_range_index_a_b;
DROP INDEX index_test_range_index_a_b_partial;
DROP INDEX index_test_hash_index_a;
DROP INDEX index_test_hash_index_a_b;
DROP INDEX index_test_hash_index_a_b_partial;
-- Verify that we can drop indexes concurrently
DROP INDEX CONCURRENTLY lineitem_concurrently_index;
-- Verify that all the indexes are dropped from the master and one worker node.
-- As there's a primary key, so exclude those from this check.
SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = (SELECT relname FROM pg_class WHERE relname LIKE 'lineitem%' ORDER BY relname LIMIT 1)::regclass AND NOT indisprimary AND indexrelid::regclass::text NOT LIKE 'lineitem_time_index%';
 indrelid | indexrelid
---------------------------------------------------------------------
(0 rows)

SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname;
 schemaname |    tablename    |          indexname          | tablespace |                                                 indexdef
---------------------------------------------------------------------
 public     | index_test_hash | index_test_hash_index_a_b_c |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c ON public.index_test_hash USING btree (a) INCLUDE (b, c)
(1 row)

\c - - - :worker_1_port
SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = (SELECT relname FROM pg_class WHERE relname LIKE 'lineitem%' ORDER BY relname LIMIT 1)::regclass AND NOT indisprimary AND indexrelid::regclass::text NOT LIKE 'lineitem_time_index%';
 indrelid | indexrelid
---------------------------------------------------------------------
(0 rows)

SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname;
 schemaname |       tablename        |             indexname              | tablespace |                                                        indexdef
---------------------------------------------------------------------
 public     | index_test_hash_102082 | index_test_hash_index_a_b_c_102082 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102082 ON public.index_test_hash_102082 USING btree (a) INCLUDE (b, c)
 public     | index_test_hash_102083 | index_test_hash_index_a_b_c_102083 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102083 ON public.index_test_hash_102083 USING btree (a) INCLUDE (b, c)
 public     | index_test_hash_102084 | index_test_hash_index_a_b_c_102084 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102084 ON public.index_test_hash_102084 USING btree (a) INCLUDE (b, c)
 public     | index_test_hash_102085 | index_test_hash_index_a_b_c_102085 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102085 ON public.index_test_hash_102085 USING btree (a) INCLUDE (b, c)
 public     | index_test_hash_102086 | index_test_hash_index_a_b_c_102086 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102086 ON public.index_test_hash_102086 USING btree (a) INCLUDE (b, c)
 public     | index_test_hash_102087 | index_test_hash_index_a_b_c_102087 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102087 ON public.index_test_hash_102087 USING btree (a) INCLUDE (b, c)
 public     | index_test_hash_102088 | index_test_hash_index_a_b_c_102088 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102088 ON public.index_test_hash_102088 USING btree (a) INCLUDE (b, c)
 public     | index_test_hash_102089 | index_test_hash_index_a_b_c_102089 |            | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102089 ON public.index_test_hash_102089 USING btree (a) INCLUDE (b, c)
(8 rows)

-- create index that will conflict with master operations
CREATE INDEX CONCURRENTLY ith_b_idx_102089 ON index_test_hash_102089(b);
\c - - - :master_port
-- should fail because worker index already exists
CREATE INDEX CONCURRENTLY ith_b_idx ON index_test_hash(b);
ERROR:  CONCURRENTLY-enabled index command failed
DETAIL:  CONCURRENTLY-enabled index commands can fail partially, leaving behind an INVALID index.
HINT:  Use DROP INDEX CONCURRENTLY IF EXISTS to remove the invalid index, then retry the original command.
-- the failure results in an INVALID index
SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx'::regclass;
 Index Valid?
---------------------------------------------------------------------
 f
(1 row)

-- we can clean it up and recreate with an DROP IF EXISTS
DROP INDEX CONCURRENTLY IF EXISTS ith_b_idx;
CREATE INDEX CONCURRENTLY ith_b_idx ON index_test_hash(b);
SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx'::regclass;
 Index Valid?
---------------------------------------------------------------------
 t
(1 row)

\c - - - :worker_1_port
-- now drop shard index to test partial master DROP failure
DROP INDEX CONCURRENTLY ith_b_idx_102089;
\c - - - :master_port
DROP INDEX CONCURRENTLY ith_b_idx;
ERROR:  CONCURRENTLY-enabled index command failed
DETAIL:  CONCURRENTLY-enabled index commands can fail partially, leaving behind an INVALID index.
HINT:  Use DROP INDEX CONCURRENTLY IF EXISTS to remove the invalid index, then retry the original command.
-- the failure results in an INVALID index
SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx'::regclass;
 Index Valid?
---------------------------------------------------------------------
 f
(1 row)

-- final clean up
DROP INDEX CONCURRENTLY IF EXISTS ith_b_idx;
-- Drop created tables
DROP TABLE index_test_range;
DROP TABLE index_test_hash;
DROP TABLE index_test_append;
