-- -- MULTI_INDEX_STATEMENTS -- -- Check that we can run CREATE INDEX and DROP INDEX statements on distributed -- tables. -- -- CREATE TEST TABLES -- CREATE SCHEMA multi_index_statements; CREATE SCHEMA multi_index_statements_2; SET search_path TO multi_index_statements; 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, a_text text, b_text text); 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 public.lineitem (l_orderkey); CREATE INDEX lineitem_partkey_desc_index ON public.lineitem (l_partkey DESC); CREATE INDEX lineitem_partial_index ON public.lineitem (l_shipdate) WHERE l_shipdate < '1995-01-01'; CREATE INDEX lineitem_colref_index ON public.lineitem (record_ne(lineitem.*, NULL)); SET client_min_messages = ERROR; -- avoid version dependent warning about WAL CREATE INDEX lineitem_orderkey_hash_index ON public.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 can create expression indexes and be robust to different schemas CREATE OR REPLACE FUNCTION value_plus_one(a int) RETURNS int IMMUTABLE AS $$ BEGIN RETURN a + 1; END; $$ LANGUAGE plpgsql; SELECT create_distributed_function('value_plus_one(int)'); NOTICE: procedure multi_index_statements.value_plus_one is already distributed DETAIL: Citus distributes procedures with CREATE [PROCEDURE|FUNCTION|AGGREGATE] commands create_distributed_function --------------------------------------------------------------------- (1 row) CREATE OR REPLACE FUNCTION multi_index_statements_2.value_plus_one(a int) RETURNS int IMMUTABLE AS $$ BEGIN RETURN a + 1; END; $$ LANGUAGE plpgsql; SELECT create_distributed_function('multi_index_statements_2.value_plus_one(int)'); NOTICE: procedure multi_index_statements_2.value_plus_one is already distributed DETAIL: Citus distributes procedures with CREATE [PROCEDURE|FUNCTION|AGGREGATE] commands create_distributed_function --------------------------------------------------------------------- (1 row) CREATE FUNCTION predicate_stable() RETURNS bool IMMUTABLE LANGUAGE plpgsql AS $$ BEGIN EXECUTE 'SELECT txid_current()'; RETURN true; END; $$; CREATE INDEX ON index_test_hash ((value_plus_one(b))); CREATE INDEX ON index_test_hash ((value_plus_one(b) + value_plus_one(c))) WHERE value_plus_one(c) > 10; CREATE INDEX ON index_test_hash (a) WHERE value_plus_one(c) > 10; CREATE INDEX ON index_test_hash (abs(a)) WHERE value_plus_one(c) > 10; CREATE INDEX ON index_test_hash (value_plus_one(a)) WHERE c > 10; CREATE INDEX ON index_test_hash ((multi_index_statements.value_plus_one(b))); CREATE INDEX ON index_test_hash ((multi_index_statements_2.value_plus_one(b))); CREATE INDEX ON index_test_hash (a) INCLUDE (b) WHERE value_plus_one(c) > 10; CREATE INDEX ON index_test_hash (c, (c+0)) INCLUDE (a); CREATE INDEX ON index_test_hash (value_plus_one(a)) INCLUDE (c,b) WHERE value_plus_one(c) > 10; CREATE INDEX ON index_test_hash ((a_text || b_text)); CREATE INDEX ON index_test_hash ((a_text || b_text)) WHERE value_plus_one(c) > 10; CREATE INDEX ON index_test_hash ((a_text || b_text)) WHERE (a_text || b_text) = 'ttt'; CREATE INDEX CONCURRENTLY ON index_test_hash (a) WHERE predicate_stable(); -- Verify that we handle if not exists statements correctly CREATE INDEX lineitem_orderkey_index on public.lineitem(l_orderkey); ERROR: relation "lineitem_orderkey_index" already exists CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on public.lineitem(l_orderkey); NOTICE: relation "lineitem_orderkey_index" already exists, skipping CREATE INDEX IF NOT EXISTS lineitem_orderkey_index_new on public.lineitem(l_orderkey); -- Verify if not exists behavior with an index with same name on a different table CREATE INDEX lineitem_orderkey_index on public.nation(n_nationkey); ERROR: relation "lineitem_orderkey_index" already exists CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on public.nation(n_nationkey); NOTICE: relation "lineitem_orderkey_index" already exists, skipping -- Verify that we can create indexes concurrently CREATE INDEX CONCURRENTLY lineitem_concurrently_index ON public.lineitem (l_orderkey); -- Verify that no-name local CREATE INDEX CONCURRENTLY works CREATE TABLE local_table (id integer, name text); CREATE INDEX CONCURRENTLY ON local_table(id); -- Vefify we don't warn out on CLUSTER command for local tables CREATE INDEX CONCURRENTLY local_table_index ON local_table(id); CLUSTER local_table USING local_table_index; DROP TABLE local_table; -- Verify that we can run CLUSTER command CLUSTER index_test_hash USING index_test_hash_index_a; -- Verify that we ERROR on CLUSTER VERBOSE CLUSTER VERBOSE index_test_hash USING index_test_hash_index_a; ERROR: cannot run CLUSTER command DETAIL: VERBOSE option is currently unsupported for distributed tables. -- Verify that we WARN on CLUSTER ALL CLUSTER; WARNING: not propagating CLUSTER command to worker nodes HINT: Provide a specific table in order to CLUSTER distributed tables. -- 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 --------------------------------------------------------------------- multi_index_statements | index_test_hash | index_test_hash_a_b_idx | | CREATE INDEX index_test_hash_a_b_idx ON multi_index_statements.index_test_hash USING btree (a) INCLUDE (b) WHERE (value_plus_one(c) > 10) multi_index_statements | index_test_hash | index_test_hash_a_idx | | CREATE INDEX index_test_hash_a_idx ON multi_index_statements.index_test_hash USING btree (a) WHERE (value_plus_one(c) > 10) multi_index_statements | index_test_hash | index_test_hash_a_idx1 | | CREATE INDEX index_test_hash_a_idx1 ON multi_index_statements.index_test_hash USING btree (a) WHERE predicate_stable() multi_index_statements | index_test_hash | index_test_hash_abs_idx | | CREATE INDEX index_test_hash_abs_idx ON multi_index_statements.index_test_hash USING btree (abs(a)) WHERE (value_plus_one(c) > 10) multi_index_statements | index_test_hash | index_test_hash_c_expr_a_idx | | CREATE INDEX index_test_hash_c_expr_a_idx ON multi_index_statements.index_test_hash USING btree (c, ((c + 0))) INCLUDE (a) multi_index_statements | index_test_hash | index_test_hash_expr_idx | | CREATE INDEX index_test_hash_expr_idx ON multi_index_statements.index_test_hash USING btree (((value_plus_one(b) + value_plus_one(c)))) WHERE (value_plus_one(c) > 10) multi_index_statements | index_test_hash | index_test_hash_expr_idx1 | | CREATE INDEX index_test_hash_expr_idx1 ON multi_index_statements.index_test_hash USING btree (((a_text || b_text))) multi_index_statements | index_test_hash | index_test_hash_expr_idx2 | | CREATE INDEX index_test_hash_expr_idx2 ON multi_index_statements.index_test_hash USING btree (((a_text || b_text))) WHERE (value_plus_one(c) > 10) multi_index_statements | index_test_hash | index_test_hash_expr_idx3 | | CREATE INDEX index_test_hash_expr_idx3 ON multi_index_statements.index_test_hash USING btree (((a_text || b_text))) WHERE ((a_text || b_text) = 'ttt'::text) multi_index_statements | index_test_hash | index_test_hash_index_a | | CREATE UNIQUE INDEX index_test_hash_index_a ON multi_index_statements.index_test_hash USING btree (a) multi_index_statements | index_test_hash | index_test_hash_index_a_b | | CREATE UNIQUE INDEX index_test_hash_index_a_b ON multi_index_statements.index_test_hash USING btree (a, b) multi_index_statements | index_test_hash | index_test_hash_index_a_b_c | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c ON multi_index_statements.index_test_hash USING btree (a) INCLUDE (b, c) multi_index_statements | index_test_hash | index_test_hash_index_a_b_partial | | CREATE UNIQUE INDEX index_test_hash_index_a_b_partial ON multi_index_statements.index_test_hash USING btree (a, b) WHERE (c IS NOT NULL) multi_index_statements | index_test_hash | index_test_hash_value_plus_one_c_b_idx | | CREATE INDEX index_test_hash_value_plus_one_c_b_idx ON multi_index_statements.index_test_hash USING btree (value_plus_one(a)) INCLUDE (c, b) WHERE (value_plus_one(c) > 10) multi_index_statements | index_test_hash | index_test_hash_value_plus_one_idx | | CREATE INDEX index_test_hash_value_plus_one_idx ON multi_index_statements.index_test_hash USING btree (value_plus_one(b)) multi_index_statements | index_test_hash | index_test_hash_value_plus_one_idx1 | | CREATE INDEX index_test_hash_value_plus_one_idx1 ON multi_index_statements.index_test_hash USING btree (value_plus_one(a)) WHERE (c > 10) multi_index_statements | index_test_hash | index_test_hash_value_plus_one_idx2 | | CREATE INDEX index_test_hash_value_plus_one_idx2 ON multi_index_statements.index_test_hash USING btree (value_plus_one(b)) multi_index_statements | index_test_hash | index_test_hash_value_plus_one_idx3 | | CREATE INDEX index_test_hash_value_plus_one_idx3 ON multi_index_statements.index_test_hash USING btree (multi_index_statements_2.value_plus_one(b)) multi_index_statements | index_test_range | index_test_range_index_a | | CREATE UNIQUE INDEX index_test_range_index_a ON multi_index_statements.index_test_range USING btree (a) multi_index_statements | index_test_range | index_test_range_index_a_b | | CREATE UNIQUE INDEX index_test_range_index_a_b ON multi_index_statements.index_test_range USING btree (a, b) multi_index_statements | index_test_range | index_test_range_index_a_b_partial | | CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON multi_index_statements.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) (30 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 --------------------------------------------------------------------- 144 (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) -- Verify that we actually run the CLUSTER COMMAND SELECT sum(indisclustered::integer) FROM pg_index WHERE indrelid::regclass::text SIMILAR TO '%\d'; sum --------------------------------------------------------------------- 8 (1 row) \c - - - :master_port SET search_path TO multi_index_statements, public; -- Verify that we error out on unsupported statement types 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 -- show that we support indexes without names CREATE INDEX ON lineitem (l_orderkey); CREATE UNIQUE INDEX ON index_test_hash(a); CREATE INDEX CONCURRENTLY ON lineitem USING hash (l_shipdate); -- -- 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 indexes got created on the coordinator node and on the workers -- by dropping the indexes. We do this because in different PG versions, -- the expression indexes are named differently -- and, being able to drop the index ensures that the index names are -- proper CREATE OR REPLACE FUNCTION drop_all_indexes(table_name regclass) RETURNS INTEGER AS $$ DECLARE i RECORD; BEGIN FOR i IN (SELECT indexrelid::regclass::text as relname FROM pg_index WHERE indrelid = table_name and indexrelid::regclass::text not ilike '%pkey%') LOOP EXECUTE 'DROP INDEX ' || i.relname; END LOOP; RETURN 1; END; $$ LANGUAGE plpgsql; SELECT drop_all_indexes('public.lineitem'); drop_all_indexes --------------------------------------------------------------------- 1 (1 row) SELECT drop_all_indexes('index_test_range'); drop_all_indexes --------------------------------------------------------------------- 1 (1 row) SELECT drop_all_indexes('index_test_hash'); drop_all_indexes --------------------------------------------------------------------- 1 (1 row) SELECT drop_all_indexes('index_test_append'); drop_all_indexes --------------------------------------------------------------------- 1 (1 row) -- 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%' ORDER BY 1,2; indrelid | indexrelid --------------------------------------------------------------------- (0 rows) SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname; schemaname | tablename | indexname | tablespace | indexdef --------------------------------------------------------------------- (0 rows) \c - - - :worker_1_port SET citus.override_table_visibility TO FALSE; SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = (SELECT relname FROM pg_class WHERE relname SIMILAR TO 'lineitem%\d' ORDER BY relname LIMIT 1)::regclass AND NOT indisprimary AND indexrelid::regclass::text NOT LIKE 'lineitem_time_index%' ORDER BY 1,2; indrelid | indexrelid --------------------------------------------------------------------- (0 rows) SELECT * FROM pg_indexes WHERE tablename SIMILAR TO 'index_test_%\d' ORDER BY indexname; schemaname | tablename | indexname | tablespace | indexdef --------------------------------------------------------------------- (0 rows) -- create index that will conflict with master operations CREATE INDEX CONCURRENTLY ith_b_idx_102089 ON multi_index_statements.index_test_hash_102089(b); \c - - - :master_port SET search_path TO multi_index_statements; -- 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 SET search_path TO multi_index_statements; -- now drop shard index to test partial master DROP failure DROP INDEX CONCURRENTLY ith_b_idx_102089; \c - - - :master_port SET search_path TO multi_index_statements; SET citus.next_shard_id TO 103080; SET citus.shard_count TO 32; SET citus.shard_replication_factor TO 1; -- the following tests are intended to show that -- Citus does not get into self-deadlocks because -- of long index names. So, make sure that we have -- enough remote connections to trigger the case SET citus.force_max_query_parallelization TO ON; CREATE TABLE test_index_creation1 ( tenant_id integer NOT NULL, timeperiod timestamp without time zone NOT NULL, field1 integer NOT NULL, inserted_utc timestamp without time zone NOT NULL DEFAULT now(), PRIMARY KEY(tenant_id, timeperiod) ) PARTITION BY RANGE (timeperiod); select create_distributed_table('test_index_creation1', 'tenant_id'); create_distributed_table --------------------------------------------------------------------- (1 row) -- should be able to create short named indexes in parallel -- as there are no partitions even if the index name is too long SET client_min_messages TO DEBUG1; CREATE INDEX ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1 ON test_index_creation1 USING btree (tenant_id, timeperiod); NOTICE: identifier "ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1" will be truncated to "ix_test_index_creation1_ix_test_index_creation1_ix_test_index_c" DEBUG: identifier "ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1" will be truncated to "ix_test_index_creation1_ix_test_index_creation1_ix_test_index_c" DETAIL: from localhost:xxxxx DEBUG: identifier "ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1" will be truncated to "ix_test_index_creation1_ix_test_index_creation1_ix_test_index_c" DETAIL: from localhost:xxxxx RESET client_min_messages; CREATE TABLE test_index_creation1_p2020_09_26 PARTITION OF test_index_creation1 FOR VALUES FROM ('2020-09-26 00:00:00') TO ('2020-09-27 00:00:00'); CREATE TABLE test_index_creation1_p2020_09_27 PARTITION OF test_index_creation1 FOR VALUES FROM ('2020-09-27 00:00:00') TO ('2020-09-28 00:00:00'); -- should switch to sequential execution as the index name on the partition is -- longer than 63 SET client_min_messages TO DEBUG1; CREATE INDEX ix_test_index_creation2 ON test_index_creation1 USING btree (tenant_id, timeperiod); DEBUG: the index name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_xxxxxx_tenant_id_timeperiod_idx -- same test with schema qualified SET search_path TO public; CREATE INDEX ix_test_index_creation3 ON multi_index_statements.test_index_creation1 USING btree (tenant_id, timeperiod); DEBUG: the index name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_xxxxxx_tenant_id_timeperiod_idx SET search_path TO multi_index_statements; -- we cannot switch to sequential execution -- after a parallel query BEGIN; SELECT count(*) FROM test_index_creation1; count --------------------------------------------------------------------- 0 (1 row) CREATE INDEX ix_test_index_creation4 ON test_index_creation1 USING btree (tenant_id, timeperiod); ERROR: The index name (test_index_creation1_p2020_09_26_xxxxxx_tenant_id_timeperiod_idx) on a shard is too long and could lead to deadlocks when executed in a transaction block after a parallel query HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" ROLLBACK; -- try inside a sequential block BEGIN; SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; SELECT count(*) FROM test_index_creation1; count --------------------------------------------------------------------- 0 (1 row) CREATE INDEX ix_test_index_creation4 ON test_index_creation1 USING btree (tenant_id, timeperiod); DEBUG: the index name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_xxxxxx_tenant_id_timeperiod_idx ROLLBACK; -- should be able to create indexes with INCLUDE/WHERE CREATE INDEX ix_test_index_creation5 ON test_index_creation1 USING btree(tenant_id, timeperiod) INCLUDE (field1) WHERE (tenant_id = 100); DEBUG: the index name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: test_index_creation1_p2020_09_2_tenant_id_timeperiod_field1_idx CREATE UNIQUE INDEX ix_test_index_creation6 ON test_index_creation1 USING btree(tenant_id, timeperiod); DEBUG: the index name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_xxxxxx_tenant_id_timeperiod_idx -- should be able to create short named indexes in parallel -- as the table/index name is short CREATE INDEX f1 ON test_index_creation1 USING btree (field1); -- should be able to create index only for parent on both -- coordinator and worker nodes CREATE INDEX parent_index ON ONLY test_index_creation1 USING btree (field1); -- show that we have parent index only on the parent table not on the partitions SELECT count(*) FROM pg_index WHERE indrelid::regclass::text = 'test_index_creation1' AND indexrelid::regclass::text = 'parent_index'; count --------------------------------------------------------------------- 1 (1 row) SELECT count(*) FROM pg_index WHERE indrelid::regclass::text LIKE 'test_index_creation1_p2020%' AND indexrelid::regclass::text LIKE 'parent_index%'; count --------------------------------------------------------------------- 0 (1 row) \c - - - :worker_1_port SET search_path TO multi_index_statements; -- show that we have parent index_* only on the parent shards not on the partition shards SELECT count(*) FROM pg_index WHERE indrelid::regclass::text LIKE 'test_index_creation1_%' AND indexrelid::regclass::text LIKE 'parent_index%'; count --------------------------------------------------------------------- 16 (1 row) SELECT count(*) FROM pg_index WHERE indrelid::regclass::text LIKE 'test_index_creation1_p2020%' AND indexrelid::regclass::text LIKE 'parent_index%'; count --------------------------------------------------------------------- 0 (1 row) \c - - - :master_port SET search_path TO multi_index_statements; -- attach child index of a partition to parent index of the partitioned table CREATE INDEX child_index ON test_index_creation1_p2020_09_26 USING btree (field1); ALTER INDEX parent_index ATTACH PARTITION child_index; -- show that child index inherits from parent index which means it is attached to it SELECT count(*) FROM pg_inherits WHERE inhrelid::regclass::text = 'child_index' AND inhparent::regclass::text = 'parent_index'; count --------------------------------------------------------------------- 1 (1 row) \c - - - :worker_1_port SET search_path TO multi_index_statements; -- show that child indices of partition shards also inherit from parent indices of parent shards SELECT count(*) FROM pg_inherits WHERE inhrelid::regclass::text LIKE 'child_index\_%' AND inhparent::regclass::text LIKE 'parent_index\_%'; count --------------------------------------------------------------------- 16 (1 row) \c - - - :master_port SET search_path TO multi_index_statements; -- verify error check for partitioned index ALTER INDEX parent_index SET TABLESPACE foo; ERROR: alter index ... set tablespace ... is currently unsupported DETAIL: Only RENAME TO, SET (), RESET (), ATTACH PARTITION and SET STATISTICS are supported. -- drop parent index and show that child index will also be dropped DROP INDEX parent_index; SELECT count(*) FROM pg_index where indexrelid::regclass::text = 'child_index'; count --------------------------------------------------------------------- 0 (1 row) -- show that having a foreign key to reference table causes sequential execution mode -- with ALTER INDEX ... ATTACH PARTITION CREATE TABLE index_creation_reference_table (id int primary key); SELECT create_reference_table('index_creation_reference_table'); create_reference_table --------------------------------------------------------------------- (1 row) ALTER TABLE test_index_creation1 ADD CONSTRAINT foreign_key_to_ref_table FOREIGN KEY (tenant_id) REFERENCES index_creation_reference_table (id); CREATE INDEX parent_index ON ONLY test_index_creation1 USING btree (field1); CREATE INDEX child_index ON test_index_creation1_p2020_09_26 USING btree (field1); BEGIN; show citus.multi_shard_modify_mode; citus.multi_shard_modify_mode --------------------------------------------------------------------- parallel (1 row) ALTER INDEX parent_index ATTACH PARTITION child_index; show citus.multi_shard_modify_mode; citus.multi_shard_modify_mode --------------------------------------------------------------------- sequential (1 row) ROLLBACK; DROP TABLE index_creation_reference_table CASCADE; NOTICE: drop cascades to constraint foreign_key_to_ref_table on table test_index_creation1 SELECT 'CREATE TABLE distributed_table(' || string_Agg('col' || x::text || ' int,', ' ') || ' last_column int)' FROM generate_Series(1, 32) x; ?column? --------------------------------------------------------------------- CREATE TABLE distributed_table(col1 int, col2 int, col3 int, col4 int, col5 int, col6 int, col7 int, col8 int, col9 int, col10 int, col11 int, col12 int, col13 int, col14 int, col15 int, col16 int, col17 int, col18 int, col19 int, col20 int, col21 int, col22 int, col23 int, col24 int, col25 int, col26 int, col27 int, col28 int, col29 int, col30 int, col31 int, col32 int, last_column int) (1 row) \gexec CREATE TABLE distributed_table(col1 int, col2 int, col3 int, col4 int, col5 int, col6 int, col7 int, col8 int, col9 int, col10 int, col11 int, col12 int, col13 int, col14 int, col15 int, col16 int, col17 int, col18 int, col19 int, col20 int, col21 int, col22 int, col23 int, col24 int, col25 int, col26 int, col27 int, col28 int, col29 int, col30 int, col31 int, col32 int, last_column int) SELECT create_distributed_table('distributed_table', 'last_column'); create_distributed_table --------------------------------------------------------------------- (1 row) -- try to use all 33 columns to create the index -- show that we error out as postgres would do SELECT 'CREATE INDEX ON distributed_table(' || string_Agg('col' || x::text || ',', ' ') || ' last_column)' FROM generate_Series(1, 32) x; ?column? --------------------------------------------------------------------- CREATE INDEX ON distributed_table(col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12, col13, col14, col15, col16, col17, col18, col19, col20, col21, col22, col23, col24, col25, col26, col27, col28, col29, col30, col31, col32, last_column) (1 row) \gexec CREATE INDEX ON distributed_table(col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12, col13, col14, col15, col16, col17, col18, col19, col20, col21, col22, col23, col24, col25, col26, col27, col28, col29, col30, col31, col32, last_column) ERROR: cannot use more than 32 columns in an index -- show that we generate different default index names -- for the indexes with same parameters on the same relation CREATE INDEX ON distributed_table(last_column); CREATE INDEX ON distributed_table(last_column); SELECT indexrelid::regclass FROM pg_index WHERE indrelid='distributed_table'::regclass ORDER BY indexrelid; indexrelid --------------------------------------------------------------------- distributed_table_last_column_idx distributed_table_last_column_idx1 (2 rows) -- test CREATE INDEX in plpgsql to verify that we don't break parse tree CREATE OR REPLACE FUNCTION create_index_in_plpgsql() RETURNS VOID AS $BODY$ BEGIN CREATE INDEX ON distributed_table(last_column); END; $BODY$ LANGUAGE plpgsql; CREATE TABLE test_for_func( a int ); SELECT create_distributed_table('test_for_func', 'a'); create_distributed_table --------------------------------------------------------------------- (1 row) -- create a function that depends on a relation that depends on an extension CREATE OR REPLACE FUNCTION function_on_table_depends_on_extension ( p_table_name text) RETURNS TABLE (LIKE pg_dist_partition) AS $$ BEGIN RETURN QUERY SELECT * FROM pg_dist_partition WHERE logicalrelid::regclass::text = p_table_name; END; $$ LANGUAGE plpgsql; SELECT logicalrelid FROM function_on_table_depends_on_extension('test_for_func'); logicalrelid --------------------------------------------------------------------- test_for_func (1 row) -- create a function that depends on a relation that does not depend on an extension CREATE TABLE local_test(a int); CREATE OR REPLACE FUNCTION function_on_table_does_not_depend_on_extension ( input int) RETURNS TABLE (LIKE local_test) AS $$ BEGIN RETURN QUERY SELECT * FROM local_test WHERE a = input; END; $$ LANGUAGE plpgsql; WARNING: "function function_on_table_does_not_depend_on_extension(integer)" has dependency to "table local_test" that is not in Citus' metadata DETAIL: "function function_on_table_does_not_depend_on_extension(integer)" will be created only locally HINT: Distribute "table local_test" first to distribute "function function_on_table_does_not_depend_on_extension(integer)" SELECT * FROM function_on_table_does_not_depend_on_extension(5); a --------------------------------------------------------------------- (0 rows) -- hide plpgsql messages as they differ across pg versions \set VERBOSITY terse SELECT create_index_in_plpgsql(); create_index_in_plpgsql --------------------------------------------------------------------- (1 row) SELECT create_index_in_plpgsql(); create_index_in_plpgsql --------------------------------------------------------------------- (1 row) SELECT create_index_in_plpgsql(); create_index_in_plpgsql --------------------------------------------------------------------- (1 row) SELECT indexrelid::regclass FROM pg_index WHERE indrelid='distributed_table'::regclass ORDER BY indexrelid; indexrelid --------------------------------------------------------------------- distributed_table_last_column_idx distributed_table_last_column_idx1 distributed_table_last_column_idx2 distributed_table_last_column_idx3 distributed_table_last_column_idx4 (5 rows) SET citus.force_max_query_parallelization TO OFF; SET client_min_messages TO ERROR; DROP INDEX f1; DROP INDEX ix_test_index_creation2; DROP INDEX ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1_ix_test_index_creation1; DROP INDEX CONCURRENTLY ith_b_idx; ERROR: index "ith_b_idx_102089" does not exist -- 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 SCHEMA multi_index_statements CASCADE; DROP SCHEMA multi_index_statements_2 CASCADE;