diff --git a/.gitattributes b/.gitattributes index 86ee448b9..ac1ca0c17 100644 --- a/.gitattributes +++ b/.gitattributes @@ -30,6 +30,7 @@ src/backend/distributed/utils/pg11_snprintf.c -citus-style src/backend/distributed/deparser/ruleutils_11.c -citus-style src/backend/distributed/deparser/ruleutils_12.c -citus-style src/backend/distributed/deparser/ruleutils_13.c -citus-style +src/backend/distributed/commands/index_pg_source.c -citus-style src/include/distributed/citus_nodes.h -citus-style /vendor/** -citus-style diff --git a/src/backend/distributed/commands/index.c b/src/backend/distributed/commands/index.c index 708e143f1..761c9bd22 100644 --- a/src/backend/distributed/commands/index.c +++ b/src/backend/distributed/commands/index.c @@ -29,8 +29,11 @@ #include "distributed/listutils.h" #include "distributed/coordinator_protocol.h" #include "distributed/metadata_cache.h" +#include "distributed/multi_executor.h" #include "distributed/multi_physical_planner.h" +#include "distributed/multi_partitioning_utils.h" #include "distributed/resource_lock.h" +#include "distributed/relation_access_tracking.h" #include "distributed/version_compat.h" #include "lib/stringinfo.h" #include "miscadmin.h" @@ -41,8 +44,15 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" + /* Local functions forward declarations for helper functions */ static List * CreateIndexTaskList(Oid relationId, IndexStmt *indexStmt); +static void SwitchToSequentialExecutionIfIndexNameTooLong(Oid relationId, Oid + namespaceId, + IndexStmt *createIndexStatement); +static char * GenerateLongestShardPartitionIndexName(Oid relationId, Oid namespaceId, + IndexStmt *createIndexStatement); + static List * CreateReindexTaskList(Oid relationId, ReindexStmt *reindexStmt); static void RangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg); @@ -183,6 +193,25 @@ PreprocessIndexStmt(Node *node, const char *createIndexCommand) ddlJob->taskList = CreateIndexTaskList(relationId, createIndexStatement); ddlJobs = list_make1(ddlJob); + + /* + * Citus has the logic to truncate the long shard names to prevent + * various issues, including self-deadlocks. However, for partitioned + * tables, when index is created on the parent table, the index names + * on the partitions are auto-generated by Postgres. We use the same + * Postgres function to generate the index names on the shards of the + * partitions. If the length exceeds the limit, we switch to sequential + * execution mode. + * + * The root cause of the problem is that postgres truncates the + * table/index names if they are longer than "NAMEDATALEN - 1". + * From Citus' perspective, running commands in parallel on the + * shards could mean these table/index names are truncated to be + * the same, and thus forming a self-deadlock as these tables/ + * indexes are inserted into postgres' metadata tables, like pg_class. + */ + SwitchToSequentialExecutionIfIndexNameTooLong(relationId, namespaceId, + createIndexStatement); } } } @@ -191,6 +220,111 @@ PreprocessIndexStmt(Node *node, const char *createIndexCommand) } +/* + * SwitchToSequentialExecutionIfIndexNameTooLong generates the longest index name + * on the shards of the partitions, and if exceeds the limit switches to the + * sequential execution to prevent self-deadlocks. + */ +static void +SwitchToSequentialExecutionIfIndexNameTooLong(Oid relationId, Oid namespaceId, + IndexStmt *createIndexStatement) +{ + if (!PartitionedTable(relationId)) + { + /* Citus already handles long names for regular tables */ + return; + } + + if (ShardIntervalCount(relationId) == 0) + { + /* + * Relation has no shards, so we cannot run into "long shard index + * name" issue. + */ + return; + } + + char *indexName = + GenerateLongestShardPartitionIndexName(relationId, namespaceId, + createIndexStatement); + + if (indexName && + strnlen(indexName, NAMEDATALEN) >= NAMEDATALEN - 1) + { + if (ParallelQueryExecutedInTransaction()) + { + /* + * If there has already been a parallel query executed, the sequential mode + * would still use the already opened parallel connections to the workers, + * thus contradicting our purpose of using sequential mode. + */ + ereport(ERROR, (errmsg( + "The index name (%s) on a shard is too long and could lead " + "to deadlocks when executed in a transaction " + "block after a parallel query", indexName), + errhint("Try re-running the transaction with " + "\"SET LOCAL citus.multi_shard_modify_mode TO " + "\'sequential\';\""))); + } + + elog(DEBUG1, "the index name on the shards of the partition " + "is too long, switching to sequential execution " + "mode to prevent self deadlocks: %s", indexName); + + SetLocalMultiShardModifyModeToSequential(); + } +} + + +/* + * GenerateLongestShardPartitionIndexName emulates Postgres index name + * generation for partitions on the shards. It returns the longest + * possible index name. + */ +static char * +GenerateLongestShardPartitionIndexName(Oid relationId, Oid namespaceId, + IndexStmt *createIndexStatement) +{ + char *longestPartitionName = LongestPartitionName(relationId); + if (longestPartitionName == NULL) + { + /* no partitions have been created yet */ + return NULL; + } + + char *longestPartitionShardName = pstrdup(longestPartitionName); + ShardInterval *shardInterval = LoadShardIntervalWithLongestShardName(relationId); + + AppendShardIdToName(&longestPartitionShardName, shardInterval->shardId); + + /* + * The rest of the code is copy & paste from DefineIndex() + * postgres/src/backend/commands/indexcmds.c + */ + + /* + * Calculate the new list of index columns including both key columns and + * INCLUDE columns. Later we can determine which of these are key + * columns, and which are just part of the INCLUDE list by checking the + * list position. A list item in a position less than ii_NumIndexKeyAttrs + * is part of the key columns, and anything equal to and over is part of + * the INCLUDE columns. + */ + List *allIndexParams = + list_concat(list_copy(createIndexStatement->indexParams), + list_copy(createIndexStatement->indexIncludingParams)); + + List *indexColNames = ChooseIndexColumnNames(allIndexParams); + + char *choosenIndexName = ChooseIndexName(longestPartitionShardName, namespaceId, + indexColNames, + createIndexStatement->excludeOpNames, + createIndexStatement->primary, + createIndexStatement->isconstraint); + return choosenIndexName; +} + + /* * PreprocessReindexStmt determines whether a given REINDEX statement involves * a distributed table. If so (and if the statement does not use unsupported diff --git a/src/backend/distributed/commands/index_pg_source.c b/src/backend/distributed/commands/index_pg_source.c new file mode 100644 index 000000000..279c3e09d --- /dev/null +++ b/src/backend/distributed/commands/index_pg_source.c @@ -0,0 +1,200 @@ +/*------------------------------------------------------------------------- + * + * index_pg_source.c + * Helper functions copy & pasted from PostgreSQL source code. + * All the functions in this file is copied from + * postgres/src/backend/commands/indexcmds.c + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/distributed/commands/index_pg_source.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "commands/defrem.h" +#include "distributed/commands.h" +#include "distributed/listutils.h" +#include "mb/pg_wchar.h" + + +/* *INDENT-OFF* */ + +/* + * This function is copy & paste from Postgres source code: + * postgres/src/backend/commands/indexcmds.c + * + * Select the name to be used for an index. + * + * The argument list is pretty ad-hoc :-( + */ +char * +ChooseIndexName(const char *tabname, Oid namespaceId, + List *colnames, List *exclusionOpNames, + bool primary, bool isconstraint) +{ + char *indexname; + + if (primary) + { + /* the primary key's name does not depend on the specific column(s) */ + indexname = ChooseRelationName(tabname, + NULL, + "pkey", + namespaceId, + true); + } + else if (exclusionOpNames != NIL) + { + indexname = ChooseRelationName(tabname, + ChooseIndexNameAddition(colnames), + "excl", + namespaceId, + true); + } + else if (isconstraint) + { + indexname = ChooseRelationName(tabname, + ChooseIndexNameAddition(colnames), + "key", + namespaceId, + true); + } + else + { + indexname = ChooseRelationName(tabname, + ChooseIndexNameAddition(colnames), + "idx", + namespaceId, + false); + } + + return indexname; +} + + +/* + * This function is copy & paste from Postgres source code: + * postgres/src/backend/commands/indexcmds.c + * + * Generate "name2" for a new index given the list of column names for it + * (as produced by ChooseIndexColumnNames). This will be passed to + * ChooseRelationName along with the parent table name and a suitable label. + * + * We know that less than NAMEDATALEN characters will actually be used, + * so we can truncate the result once we've generated that many. + * + * XXX See also ChooseForeignKeyConstraintNameAddition and + * ChooseExtendedStatisticNameAddition. + */ +char * +ChooseIndexNameAddition(List *colnames) +{ + char buf[NAMEDATALEN * 2]; + int buflen = 0; + ListCell *lc; + + buf[0] = '\0'; + foreach(lc, colnames) + { + const char *name = (const char *) lfirst(lc); + + if (buflen > 0) + { + buf[buflen++] = '_'; /* insert _ between names */ + } + + /* + * At this point we have buflen <= NAMEDATALEN. name should be less + * than NAMEDATALEN already, but use strlcpy for paranoia. + */ + strlcpy(buf + buflen, name, NAMEDATALEN); + buflen += strlen(buf + buflen); + if (buflen >= NAMEDATALEN) + { + break; + } + } + return pstrdup(buf); +} + + +/* + * * This function is copy & paste from Postgres source code: + * postgres/src/backend/commands/indexcmds.c + * + * Select the actual names to be used for the columns of an index, given the + * list of IndexElems for the columns. This is mostly about ensuring the + * names are unique so we don't get a conflicting-attribute-names error. + * + * Returns a List of plain strings (char *, not String nodes). + */ +List * +ChooseIndexColumnNames(List *indexElems) +{ + List *result = NIL; + ListCell *lc; + + foreach(lc, indexElems) + { + IndexElem *ielem = (IndexElem *) lfirst(lc); + const char *origname; + const char *curname; + int i; + char buf[NAMEDATALEN]; + + /* Get the preliminary name from the IndexElem */ + if (ielem->indexcolname) + { + origname = ielem->indexcolname; /* caller-specified name */ + } + else if (ielem->name) + { + origname = ielem->name; /* simple column reference */ + } + else + { + origname = "expr"; /* default name for expression */ + } + + /* If it conflicts with any previous column, tweak it */ + curname = origname; + for (i = 1;; i++) + { + ListCell *lc2; + char nbuf[32]; + int nlen; + + foreach(lc2, result) + { + if (strcmp(curname, (char *) lfirst(lc2)) == 0) + { + break; + } + } + if (lc2 == NULL) + { + break; /* found nonconflicting name */ + } + sprintf(nbuf, "%d", i); + + /* Ensure generated names are shorter than NAMEDATALEN */ + nlen = pg_mbcliplen(origname, strlen(origname), + NAMEDATALEN - 1 - strlen(nbuf)); + memcpy(buf, origname, nlen); + strcpy(buf + nlen, nbuf); + curname = buf; + } + + /* And attach to the result list */ + result = lappend(result, pstrdup(curname)); + } + return result; +} + +/* *INDENT-ON* */ diff --git a/src/backend/distributed/metadata/metadata_utility.c b/src/backend/distributed/metadata/metadata_utility.c index 198da2d77..52efc3b83 100644 --- a/src/backend/distributed/metadata/metadata_utility.c +++ b/src/backend/distributed/metadata/metadata_utility.c @@ -512,6 +512,35 @@ LoadShardIntervalList(Oid relationId) } +/* + * LoadShardIntervalWithLongestShardName is a utility function that returns + * the shard interaval with the largest shardId for the given relationId. Note + * that largest shardId implies longest shard name. + */ +ShardInterval * +LoadShardIntervalWithLongestShardName(Oid relationId) +{ + CitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId); + int shardIntervalCount = cacheEntry->shardIntervalArrayLength; + + int maxShardIndex = shardIntervalCount - 1; + uint64 largestShardId = INVALID_SHARD_ID; + + for (int shardIndex = 0; shardIndex <= maxShardIndex; ++shardIndex) + { + ShardInterval *currentShardInterval = + cacheEntry->sortedShardIntervalArray[shardIndex]; + + if (largestShardId < currentShardInterval->shardId) + { + largestShardId = currentShardInterval->shardId; + } + } + + return LoadShardInterval(largestShardId); +} + + /* * ShardIntervalCount returns number of shard intervals for a given distributed table. * The function returns 0 if no shards can be found for the given relation id. diff --git a/src/backend/distributed/utils/multi_partitioning_utils.c b/src/backend/distributed/utils/multi_partitioning_utils.c index 8833ae3ae..fdd14678e 100644 --- a/src/backend/distributed/utils/multi_partitioning_utils.c +++ b/src/backend/distributed/utils/multi_partitioning_utils.c @@ -17,6 +17,7 @@ #include "catalog/pg_inherits.h" #include "distributed/citus_ruleutils.h" #include "distributed/colocation_utils.h" +#include "distributed/listutils.h" #include "distributed/metadata_utility.h" #include "distributed/coordinator_protocol.h" #include "distributed/multi_partitioning_utils.h" @@ -271,6 +272,33 @@ PartitionParentOid(Oid partitionOid) } +/* + * LongestPartitionName is a uitility function that returns the partition + * name which is the longest in terms of number of characters. + */ +char * +LongestPartitionName(Oid parentRelationId) +{ + char *longestName = NULL; + int longestNameLength = 0; + List *partitionList = PartitionList(parentRelationId); + + Oid partitionRelationId = InvalidOid; + foreach_oid(partitionRelationId, partitionList) + { + char *partitionName = get_rel_name(partitionRelationId); + int partitionNameLength = strnlen(partitionName, NAMEDATALEN); + if (partitionNameLength > longestNameLength) + { + longestName = partitionName; + longestNameLength = partitionNameLength; + } + } + + return longestName; +} + + /* * Takes a parent relation and returns Oid list of its partitions. The * function errors out if the given relation is not a parent. diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 9ff5d983b..b39b42109 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -170,6 +170,11 @@ extern List * PreprocessGrantStmt(Node *node, const char *queryString); extern bool IsIndexRenameStmt(RenameStmt *renameStmt); extern List * PreprocessIndexStmt(Node *createIndexStatement, const char *createIndexCommand); +extern char * ChooseIndexName(const char *tabname, Oid namespaceId, + List *colnames, List *exclusionOpNames, + bool primary, bool isconstraint); +extern char * ChooseIndexNameAddition(List *colnames); +extern List * ChooseIndexColumnNames(List *indexElems); extern List * PreprocessReindexStmt(Node *ReindexStatement, const char *ReindexCommand); extern List * PreprocessDropIndexStmt(Node *dropIndexStatement, diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index 6fa392bd1..b7b4e9457 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -99,6 +99,7 @@ extern Datum citus_relation_size(PG_FUNCTION_ARGS); /* Function declarations to read shard and shard placement data */ extern uint32 TableShardReplicationFactor(Oid relationId); extern List * LoadShardIntervalList(Oid relationId); +extern ShardInterval * LoadShardIntervalWithLongestShardName(Oid relationId); extern int ShardIntervalCount(Oid relationId); extern List * LoadShardList(Oid relationId); extern ShardInterval * CopyShardInterval(ShardInterval *srcInterval); diff --git a/src/include/distributed/multi_partitioning_utils.h b/src/include/distributed/multi_partitioning_utils.h index 53ffdf37c..15cf03164 100644 --- a/src/include/distributed/multi_partitioning_utils.h +++ b/src/include/distributed/multi_partitioning_utils.h @@ -19,6 +19,7 @@ extern bool PartitionTableNoLock(Oid relationId); extern bool IsChildTable(Oid relationId); extern bool IsParentTable(Oid relationId); extern Oid PartitionParentOid(Oid partitionOid); +extern char * LongestPartitionName(Oid parentRelationId); extern List * PartitionList(Oid parentRelationId); extern char * GenerateDetachPartitionCommand(Oid partitionTableId); extern char * GenerateAttachShardPartitionCommand(ShardInterval *shardInterval); diff --git a/src/test/regress/expected/multi_index_statements.out b/src/test/regress/expected/multi_index_statements.out index 4d76402a3..10436577c 100644 --- a/src/test/regress/expected/multi_index_statements.out +++ b/src/test/regress/expected/multi_index_statements.out @@ -6,6 +6,8 @@ -- -- CREATE TEST TABLES -- +CREATE SCHEMA multi_index_statements; +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'); @@ -58,13 +60,13 @@ SELECT master_create_empty_shard('index_test_append'); -- 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) +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 lineitem (record_ne(lineitem.*, NULL)); +CREATE INDEX lineitem_colref_index ON public.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 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); @@ -74,18 +76,18 @@ CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON index_test_range(a,b) 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); +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 lineitem(l_orderkey); +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 lineitem(l_orderkey); +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 index_test_hash(a); +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 index_test_hash(a); +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 lineitem (l_orderkey); +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); @@ -100,24 +102,24 @@ 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 + 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) + 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_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) (16 rows) \c - - - :worker_1_port @@ -146,8 +148,9 @@ SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_append%'; (1 row) \c - - - :master_port +SET search_path TO multi_index_statements, public; -- Verify that we error out on unsupported statement types -CREATE UNIQUE INDEX try_index ON lineitem (l_orderkey); +CREATE UNIQUE INDEX try_index ON public.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 @@ -178,24 +181,24 @@ 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 + 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) + 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_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) (16 rows) -- @@ -243,9 +246,9 @@ SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = ( (0 rows) SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname; - schemaname | tablename | indexname | tablespace | indexdef + 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) + 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) (1 row) \c - - - :worker_1_port @@ -255,21 +258,22 @@ SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = ( (0 rows) SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname; - schemaname | tablename | indexname | tablespace | indexdef + 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) + multi_index_statements | index_test_hash_102082 | index_test_hash_index_a_b_c_102082 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102082 ON multi_index_statements.index_test_hash_102082 USING btree (a) INCLUDE (b, c) + multi_index_statements | index_test_hash_102083 | index_test_hash_index_a_b_c_102083 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102083 ON multi_index_statements.index_test_hash_102083 USING btree (a) INCLUDE (b, c) + multi_index_statements | index_test_hash_102084 | index_test_hash_index_a_b_c_102084 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102084 ON multi_index_statements.index_test_hash_102084 USING btree (a) INCLUDE (b, c) + multi_index_statements | index_test_hash_102085 | index_test_hash_index_a_b_c_102085 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102085 ON multi_index_statements.index_test_hash_102085 USING btree (a) INCLUDE (b, c) + multi_index_statements | index_test_hash_102086 | index_test_hash_index_a_b_c_102086 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102086 ON multi_index_statements.index_test_hash_102086 USING btree (a) INCLUDE (b, c) + multi_index_statements | index_test_hash_102087 | index_test_hash_index_a_b_c_102087 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102087 ON multi_index_statements.index_test_hash_102087 USING btree (a) INCLUDE (b, c) + multi_index_statements | index_test_hash_102088 | index_test_hash_index_a_b_c_102088 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102088 ON multi_index_statements.index_test_hash_102088 USING btree (a) INCLUDE (b, c) + multi_index_statements | index_test_hash_102089 | index_test_hash_index_a_b_c_102089 | | CREATE UNIQUE INDEX index_test_hash_index_a_b_c_102089 ON multi_index_statements.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); +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 @@ -292,13 +296,107 @@ SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx':: (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" +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 execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_10311_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 execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_10311_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_10311_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 execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_10311_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 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 execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26_10311_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); +SET citus.force_max_query_parallelization TO OFF; +SET client_min_messages TO ERROR; +\set VERBOSITY terse +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: 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? @@ -308,7 +406,4 @@ SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx':: -- 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; +DROP SCHEMA multi_index_statements CASCADE; diff --git a/src/test/regress/sql/multi_index_statements.sql b/src/test/regress/sql/multi_index_statements.sql index 727482d49..c37dc3ecf 100644 --- a/src/test/regress/sql/multi_index_statements.sql +++ b/src/test/regress/sql/multi_index_statements.sql @@ -9,6 +9,9 @@ -- CREATE TEST TABLES -- +CREATE SCHEMA multi_index_statements; +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); @@ -32,17 +35,17 @@ SELECT master_create_empty_shard('index_test_append'); -- Verify that we can create different types of indexes -CREATE INDEX lineitem_orderkey_index ON lineitem (l_orderkey); +CREATE INDEX lineitem_orderkey_index ON public.lineitem (l_orderkey); -CREATE INDEX lineitem_partkey_desc_index ON lineitem (l_partkey DESC); +CREATE INDEX lineitem_partkey_desc_index ON public.lineitem (l_partkey DESC); -CREATE INDEX lineitem_partial_index ON lineitem (l_shipdate) +CREATE INDEX lineitem_partial_index ON public.lineitem (l_shipdate) WHERE l_shipdate < '1995-01-01'; -CREATE INDEX lineitem_colref_index ON lineitem (record_ne(lineitem.*, NULL)); +CREATE INDEX lineitem_colref_index ON public.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 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); @@ -53,16 +56,16 @@ CREATE UNIQUE INDEX index_test_hash_index_a_b_c ON index_test_hash(a) INCLUDE (b RESET client_min_messages; -- Verify that we handle if not exists statements correctly -CREATE INDEX lineitem_orderkey_index on lineitem(l_orderkey); -CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on lineitem(l_orderkey); -CREATE INDEX IF NOT EXISTS lineitem_orderkey_index_new on lineitem(l_orderkey); +CREATE INDEX lineitem_orderkey_index on public.lineitem(l_orderkey); +CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on public.lineitem(l_orderkey); +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 index_test_hash(a); -CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on index_test_hash(a); +CREATE INDEX lineitem_orderkey_index on public.nation(n_nationkey); +CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on public.nation(n_nationkey); -- Verify that we can create indexes concurrently -CREATE INDEX CONCURRENTLY lineitem_concurrently_index ON lineitem (l_orderkey); +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); @@ -86,10 +89,11 @@ SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_hash%'; SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_range%'; SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_append%'; \c - - - :master_port +SET search_path TO multi_index_statements, public; -- Verify that we error out on unsupported statement types -CREATE UNIQUE INDEX try_index ON lineitem (l_orderkey); +CREATE UNIQUE INDEX try_index ON public.lineitem (l_orderkey); CREATE INDEX try_index ON lineitem (l_orderkey) TABLESPACE newtablespace; CREATE UNIQUE INDEX try_unique_range_index ON index_test_range(b); @@ -162,9 +166,10 @@ SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = ( SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname; -- create index that will conflict with master operations -CREATE INDEX CONCURRENTLY ith_b_idx_102089 ON index_test_hash_102089(b); +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); @@ -178,11 +183,100 @@ 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; \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'); + + +-- 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); +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); + +-- 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); +SET search_path TO multi_index_statements; + +-- we cannot switch to sequential execution +-- after a parallel query +BEGIN; + SELECT count(*) FROM test_index_creation1; + CREATE INDEX ix_test_index_creation4 + ON test_index_creation1 USING btree + (tenant_id, timeperiod); +ROLLBACK; + +-- try inside a sequential block +BEGIN; + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; + SELECT count(*) FROM test_index_creation1; + CREATE INDEX ix_test_index_creation4 + ON test_index_creation1 USING btree + (tenant_id, timeperiod); +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); + +CREATE UNIQUE INDEX ix_test_index_creation6 ON test_index_creation1 + USING btree(tenant_id, timeperiod); + +-- 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); + +SET citus.force_max_query_parallelization TO OFF; + +SET client_min_messages TO ERROR; +\set VERBOSITY terse +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; -- the failure results in an INVALID index @@ -191,7 +285,4 @@ SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx':: -- 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; +DROP SCHEMA multi_index_statements CASCADE;