diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 8101dc964..5f24dcacc 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -29,6 +29,7 @@ #include "distributed/deparse_shard_query.h" #include "distributed/distribution_column.h" #include "distributed/foreign_key_relationship.h" +#include "distributed/local_executor.h" #include "distributed/listutils.h" #include "distributed/metadata_sync.h" #include "distributed/metadata/dependency.h" @@ -700,6 +701,129 @@ PostprocessAlterTableSchemaStmt(Node *node, const char *queryString) } +/* + * SwitchToSequentialAndLocalExecutionIfPrimaryKeyNameTooLong generates the longest primary key name + * among the shards of the partitions, and if exceeds the limit switches to sequential and + * local execution to prevent self-deadlocks. + */ +static void +SwitchToSequentialAndLocalExecutionIfPrimaryKeyNameTooLong(Oid relationId) +{ + 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; + } + + Oid longestNamePartitionId = PartitionWithLongestNameRelationId(relationId); + + if (!OidIsValid(longestNamePartitionId)) + { + /* no partitions have been created yet */ + return; + } + + char *longestPartitionShardName = get_rel_name(longestNamePartitionId); + ShardInterval *shardInterval = LoadShardIntervalWithLongestShardName( + longestNamePartitionId); + + AppendShardIdToName(&longestPartitionShardName, shardInterval->shardId); + + + Relation rel = RelationIdGetRelation(longestNamePartitionId); + Oid namespaceOid = RelationGetNamespace(rel); + RelationClose(rel); + + char *primaryKeyName = ChooseIndexName(longestPartitionShardName, + namespaceOid, + NULL, NULL, true, true); + + + if (primaryKeyName && strnlen(primaryKeyName, 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 primary key name (%s) on a shard is too long and could lead " + "to deadlocks when executed in a transaction " + "block after a parallel query", primaryKeyName), + errhint("Try re-running the transaction with " + "\"SET LOCAL citus.multi_shard_modify_mode TO " + "\'sequential\';\""))); + } + else + { + elog(DEBUG1, "the primary key name on the shards of the partition " + "is too long, switching to sequential and local execution " + "mode to prevent self deadlocks: %s", primaryKeyName); + + SetLocalMultiShardModifyModeToSequential(); + SetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED); + } + } +} + + +/* + * PreprocessAlterTableAddPrimaryKey creates a new primary key constraint name changing the original alterTableCommand run by the utility hook. + * Then converts the ALTER TABLE ... ADD PRIMARY KEY ... command + * into ALTER TABLE ... ADD CONSTRAINT PRIMARY KEY format and returns the DDLJob + * to run this command in the workers. + */ +static List * +PreprocessAlterTableAddPrimaryKey(AlterTableStmt *alterTableStatement, Oid relationId, + Constraint *constraint) +{ + /* We should only preprocess an ADD PRIMARY KEY command if we are changing the it. + * This only happens when we have to create a primary key name ourselves in the case that the client does + * not specify a name. + */ + Assert(constraint->conname == NULL); + + bool primary = true; + bool isconstraint = true; + + Relation rel = RelationIdGetRelation(relationId); + + /* + * Change the alterTableCommand so that the standard utility + * hook runs it with the name we created. + */ + constraint->conname = ChooseIndexName(RelationGetRelationName(rel), + RelationGetNamespace(rel), + NULL, NULL, primary, + isconstraint); + RelationClose(rel); + + char *ddlCommand = DeparseTreeNode((Node *) alterTableStatement); + + SwitchToSequentialAndLocalExecutionIfPrimaryKeyNameTooLong(relationId); + + DDLJob *ddlJob = palloc0(sizeof(DDLJob)); + + ObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId); + ddlJob->startNewTransaction = false; + ddlJob->metadataSyncCommand = ddlCommand; + ddlJob->taskList = DDLTaskList(relationId, ddlCommand); + + return list_make1(ddlJob); +} + + /* * PreprocessAlterTableStmt determines whether a given ALTER TABLE statement * involves a distributed table. If so (and if the statement does not use @@ -939,6 +1063,19 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand, */ constraint->skip_validation = true; } + else if (constraint->contype == CONSTR_PRIMARY) + { + if (constraint->conname == NULL) + { + /* + * Create a constraint name. Convert ALTER TABLE ... ADD PRIMARY ... command into + * ALTER TABLE ... ADD CONSTRAINT PRIMARY KEY ... form and create the ddl jobs + * for running this form of the command on the workers. + */ + return PreprocessAlterTableAddPrimaryKey(alterTableStatement, + leftRelationId, constraint); + } + } } else if (alterTableType == AT_DropConstraint) { @@ -2883,9 +3020,18 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) */ if (constraint->conname == NULL) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create constraint without a name on a " - "distributed table"))); + /* + * We support ALTER TABLE ... ADD PRIMARY ... commands by creating a constraint name + * and changing the command into the following form. + * ALTER TABLE ... ADD CONSTRAINT PRIMARY KEY ... + */ + if (constraint->contype != CONSTR_PRIMARY) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg( + "cannot create constraint without a name on a " + "distributed table"))); + } } break; diff --git a/src/backend/distributed/deparser/deparse_table_stmts.c b/src/backend/distributed/deparser/deparse_table_stmts.c index 0232621e4..dd6b665f6 100644 --- a/src/backend/distributed/deparser/deparse_table_stmts.c +++ b/src/backend/distributed/deparser/deparse_table_stmts.c @@ -119,6 +119,48 @@ AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd) break; } + case AT_AddConstraint: + { + Constraint *constraint = (Constraint *) alterTableCmd->def; + + /* We need to deparse ALTER TABLE ... PRIMARY KEY commands into + * ALTER TABLE ... ADD CONSTRAINT PRIMARY KEY ... to be able + * add a constraint name. + */ + if (constraint->contype == CONSTR_PRIMARY) + { + /* Need to deparse PRIMARY KEY constraint commands only if adding a name.*/ + if (constraint->conname == NULL) + { + ereport(ERROR, (errmsg( + "Constraint name can not be NULL when constraint type is PRIMARY KEY"))); + } + + appendStringInfoString(buf, " ADD CONSTRAINT "); + appendStringInfo(buf, "%s ", quote_identifier(constraint->conname)); + appendStringInfoString(buf, " PRIMARY KEY ("); + + ListCell *lc; + bool firstkey = true; + + foreach(lc, constraint->keys) + { + if (firstkey == false) + { + appendStringInfoString(buf, ", "); + } + + appendStringInfo(buf, "%s", quote_identifier(strVal(lfirst(lc)))); + firstkey = false; + } + + appendStringInfoString(buf, ")"); + break; + } + } + + /* fallthrough */ + default: { ereport(ERROR, (errmsg("unsupported subtype for alter table command"), diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index faa226f39..856b7d74d 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -165,6 +165,9 @@ class CitusDefaultClusterConfig(CitusBaseClusterConfig): } self.settings.update(new_settings) self.add_coordinator_to_metadata = True + self.skip_tests = [ + # Alter Table statement cannot be run from an arbitrary node so this test will fail + "arbitrary_configs_alter_table_add_constraint_without_name_create", "arbitrary_configs_alter_table_add_constraint_without_name"] class CitusUpgradeConfig(CitusBaseClusterConfig): @@ -187,8 +190,9 @@ class PostgresConfig(CitusDefaultClusterConfig): self.new_settings = { "citus.use_citus_managed_tables": False, } - self.skip_tests = ["nested_execution"] - + self.skip_tests = ["nested_execution", + # Alter Table statement cannot be run from an arbitrary node so this test will fail + "arbitrary_configs_alter_table_add_constraint_without_name_create", "arbitrary_configs_alter_table_add_constraint_without_name"] class CitusSingleNodeClusterConfig(CitusDefaultClusterConfig): def __init__(self, arguments): @@ -303,8 +307,9 @@ class CitusUnusualQuerySettingsConfig(CitusDefaultClusterConfig): # requires the table with the fk to be converted to a citus_local_table. # As of c11, there is no way to do that through remote execution so this test # will fail - "arbitrary_configs_truncate_cascade_create", "arbitrary_configs_truncate_cascade"] - + "arbitrary_configs_truncate_cascade_create", "arbitrary_configs_truncate_cascade", + # Alter Table statement cannot be run from an arbitrary node so this test will fail + "arbitrary_configs_alter_table_add_constraint_without_name_create", "arbitrary_configs_alter_table_add_constraint_without_name"] class CitusSingleNodeSingleShardClusterConfig(CitusDefaultClusterConfig): def __init__(self, arguments): @@ -329,7 +334,9 @@ class CitusShardReplicationFactorClusterConfig(CitusDefaultClusterConfig): "arbitrary_configs_truncate_cascade_create", "arbitrary_configs_truncate_cascade", # citus does not support colocating functions with distributed tables when # citus.shard_replication_factor >= 2 - "function_create", "functions"] + "function_create", "functions", + # Alter Table statement cannot be run from an arbitrary node so this test will fail + "arbitrary_configs_alter_table_add_constraint_without_name_create", "arbitrary_configs_alter_table_add_constraint_without_name"] class CitusSingleShardClusterConfig(CitusDefaultClusterConfig): diff --git a/src/test/regress/create_schedule b/src/test/regress/create_schedule index 0ba1a7f05..82dfa2475 100644 --- a/src/test/regress/create_schedule +++ b/src/test/regress/create_schedule @@ -12,3 +12,4 @@ test: function_create test: arbitrary_configs_truncate_create test: arbitrary_configs_truncate_cascade_create test: arbitrary_configs_truncate_partition_create +test: arbitrary_configs_alter_table_add_constraint_without_name_create diff --git a/src/test/regress/expected/arbitrary_configs_alter_table_add_constraint_without_name.out b/src/test/regress/expected/arbitrary_configs_alter_table_add_constraint_without_name.out new file mode 100644 index 000000000..e97031e30 --- /dev/null +++ b/src/test/regress/expected/arbitrary_configs_alter_table_add_constraint_without_name.out @@ -0,0 +1,76 @@ +-- +-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS_WITHOUT_NAME +-- +-- Test checks whether constraints of distributed tables can be adjusted using +-- the ALTER TABLE ... ADD without specifying a name. +SET search_path TO sc1; +-- Check "ADD PRIMARY KEY" +ALTER TABLE products ADD PRIMARY KEY(product_no); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products'; + conname +--------------------------------------------------------------------- + products_pkey +(1 row) + +ALTER TABLE products DROP CONSTRAINT products_pkey; +ALTER TABLE products ADD PRIMARY KEY(product_no); +-- Check "ADD PRIMARY KEY" with reference table +-- Check for name collisions +ALTER TABLE products_ref_3 ADD CONSTRAINT products_ref_pkey PRIMARY KEY(name); +ALTER TABLE products_ref_2 ADD CONSTRAINT products_ref_pkey1 PRIMARY KEY(name); +ALTER TABLE products_ref ADD PRIMARY KEY(name); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products_ref'; + conname +--------------------------------------------------------------------- + products_ref_pkey2 +(1 row) + +ALTER TABLE products_ref DROP CONSTRAINT products_ref_pkey2; +-- Check with max table name (63 chars) +ALTER TABLE verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon ADD PRIMARY KEY(product_no); +-- Constraint should be created on the coordinator with a shortened name +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%'; + conname +--------------------------------------------------------------------- + verylonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey +(1 row) + +ALTER TABLE verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon DROP CONSTRAINT verylonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey; +-- Test the scenario where a partitioned distributed table has a child with max allowed name +-- Verify that we switch to sequential execution mode to avoid deadlock in this scenario +ALTER TABLE dist_partitioned_table ADD PRIMARY KEY(partition_col); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'dist_partitioned_table'; + conname +--------------------------------------------------------------------- + dist_partitioned_table_pkey +(1 row) + +ALTER TABLE dist_partitioned_table DROP CONSTRAINT dist_partitioned_table_pkey; +-- Test primary key name is generated by postgres for citus local table. +ALTER TABLE citus_local_table ADD PRIMARY KEY(id); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'citus_local_table'; + conname +--------------------------------------------------------------------- + citus_local_table_pkey +(1 row) + diff --git a/src/test/regress/expected/arbitrary_configs_alter_table_add_constraint_without_name_create.out b/src/test/regress/expected/arbitrary_configs_alter_table_add_constraint_without_name_create.out new file mode 100644 index 000000000..435abebb8 --- /dev/null +++ b/src/test/regress/expected/arbitrary_configs_alter_table_add_constraint_without_name_create.out @@ -0,0 +1,71 @@ +-- +-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS_WITHOUT_NAME +-- +-- Test checks whether constraints of distributed tables can be adjusted using +-- the ALTER TABLE ... ADD without specifying a name. +-- Check "ADD PRIMARY KEY" +CREATE SCHEMA sc1; +SET search_path TO sc1; +CREATE TABLE products ( + product_no integer, + name text, + price numeric +); +SELECT create_distributed_table('products', 'product_no'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE sc1.products_ref ( + product_no integer, + name text, + price numeric +); +CREATE TABLE products_ref_2 ( + product_no integer, + name text, + price numeric +); +CREATE TABLE products_ref_3 ( + product_no integer, + name text, + price numeric +); +SELECT create_reference_table('products_ref'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('products_ref_3'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- Check with max table name (63 chars) +CREATE TABLE verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonger ( + product_no integer, + name text, + price numeric + ); +SELECT create_distributed_table('verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon', 'product_no'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Test the scenario where a partitioned distributed table has a child with max allowed name +-- Verify that we switch to sequential execution mode to avoid deadlock in this scenario +CREATE TABLE dist_partitioned_table (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +CREATE TABLE p1 PARTITION OF dist_partitioned_table FOR VALUES FROM ('2021-01-01') TO ('2022-01-01'); +CREATE TABLE longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc PARTITION OF dist_partitioned_table FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'); +SELECT create_distributed_table('dist_partitioned_table', 'partition_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Test primary key name is generated by postgres for citus local table. +CREATE TABLE citus_local_table(id int, other_column int); diff --git a/src/test/regress/expected/multi_alter_table_add_constraints.out b/src/test/regress/expected/multi_alter_table_add_constraints.out index a88074e41..83d2de7f8 100644 --- a/src/test/regress/expected/multi_alter_table_add_constraints.out +++ b/src/test/regress/expected/multi_alter_table_add_constraints.out @@ -437,7 +437,6 @@ HINT: You can issue each subcommand separately ALTER TABLE products ADD UNIQUE(product_no); ERROR: cannot create constraint without a name on a distributed table ALTER TABLE products ADD PRIMARY KEY(product_no); -ERROR: cannot create constraint without a name on a distributed table ALTER TABLE products ADD CHECK(product_no <> 0); ERROR: cannot create constraint without a name on a distributed table ALTER TABLE products ADD EXCLUDE USING btree (product_no with =); diff --git a/src/test/regress/expected/multi_alter_table_add_constraints_without_name.out b/src/test/regress/expected/multi_alter_table_add_constraints_without_name.out new file mode 100644 index 000000000..9e76a8255 --- /dev/null +++ b/src/test/regress/expected/multi_alter_table_add_constraints_without_name.out @@ -0,0 +1,396 @@ +-- +-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS_WITHOUT_NAME +-- +-- Test checks whether constraints of distributed tables can be adjusted using +-- the ALTER TABLE ... ADD without specifying a name. +ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 5410000; +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART 5410000; +CREATE SCHEMA AT_AddConstNoName; +-- Check "ADD PRIMARY KEY" +CREATE TABLE AT_AddConstNoName.products ( + product_no integer, + name text, + price numeric +); +SELECT create_distributed_table('AT_AddConstNoName.products', 'product_no'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE AT_AddConstNoName.products ADD PRIMARY KEY(product_no); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products'; + conname +--------------------------------------------------------------------- + products_pkey +(1 row) + +-- Check that the primary key name created on the coordinator is sent to workers and +-- the constraints created for the shard tables conform to the _shardid scheme. +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products_5410000'; + conname +--------------------------------------------------------------------- + products_pkey_5410000 +(1 row) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.products DROP CONSTRAINT products_pkey; +ALTER TABLE AT_AddConstNoName.products ADD PRIMARY KEY(product_no); +DROP TABLE AT_AddConstNoName.products; +-- Check "ADD PRIMARY KEY" with reference table +CREATE TABLE AT_AddConstNoName.products_ref ( + product_no integer, + name text, + price numeric +); +CREATE TABLE AT_AddConstNoName.products_ref_2 ( + product_no integer, + name text, + price numeric +); +CREATE TABLE AT_AddConstNoName.products_ref_3 ( + product_no integer, + name text, + price numeric +); +SELECT create_reference_table('AT_AddConstNoName.products_ref'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('AT_AddConstNoName.products_ref_3'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- Check for name collisions +ALTER TABLE AT_AddConstNoName.products_ref_3 ADD CONSTRAINT products_ref_pkey PRIMARY KEY(name); +ALTER TABLE AT_AddConstNoName.products_ref_2 ADD CONSTRAINT products_ref_pkey1 PRIMARY KEY(name); +ALTER TABLE AT_AddConstNoName.products_ref ADD PRIMARY KEY(name); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products_ref'; + conname +--------------------------------------------------------------------- + products_ref_pkey2 +(1 row) + +ALTER TABLE AT_AddConstNoName.products_ref DROP CONSTRAINT products_ref_pkey2; +DROP TABLE AT_AddConstNoName.products_ref; +-- Check with max table name (63 chars) +CREATE TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonger ( + product_no integer, + name text, + price numeric + ); +NOTICE: identifier "verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonger" will be truncated to "verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon" +SELECT create_distributed_table('AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon', 'product_no'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon ADD PRIMARY KEY(product_no); +-- Constraint should be created on the coordinator with a shortened name +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%'; + conname +--------------------------------------------------------------------- + verylonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey +(1 row) + +-- Constraints for the main table and the shards should be created on the worker with a shortened name +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + verylonglonglonglonglonglonglonglonglonglonglo_559ab79d_5410006 + verylonglonglonglonglonglonglonglonglonglonglo_559ab79d_5410007 + verylonglonglonglonglonglonglonglonglonglonglo_559ab79d_5410008 + verylonglonglonglonglonglonglonglonglonglonglo_559ab79d_5410009 + verylonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey +(5 rows) + +-- Constraint can be deleted via the coordinator +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon DROP CONSTRAINT verylonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey; +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%'; + conname +--------------------------------------------------------------------- +(0 rows) + +-- Test the scenario where a partitioned distributed table has a child with max allowed name +-- Verify that we switch to sequential execution mode to avoid deadlock in this scenario +\c - - :master_host :master_port +CREATE TABLE AT_AddConstNoName.dist_partitioned_table (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +CREATE TABLE AT_AddConstNoName.p1 PARTITION OF AT_AddConstNoName.dist_partitioned_table FOR VALUES FROM ('2021-01-01') TO ('2022-01-01'); +CREATE TABLE AT_AddConstNoName.longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc PARTITION OF AT_AddConstNoName.dist_partitioned_table FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'); +SELECT create_distributed_table('AT_AddConstNoName.dist_partitioned_table', 'partition_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD PRIMARY KEY(partition_col); +DEBUG: the primary key name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: longlonglonglonglonglonglonglonglonglonglonglo_537570f5_54_pkey +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "dist_partitioned_table_pkey" for table "dist_partitioned_table" +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "longlonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey" for table "longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc" +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "p1_pkey" for table "p1" +DEBUG: verifying table "p1" +DEBUG: verifying table "longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc" +RESET client_min_messages; +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'dist_partitioned_table'; + conname +--------------------------------------------------------------------- + dist_partitioned_table_pkey +(1 row) + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'longlonglonglonglonglonglonglonglong%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410014 + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410015 + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410016 + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410017 + longlonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey +(5 rows) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.dist_partitioned_table DROP CONSTRAINT dist_partitioned_table_pkey; +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'longlonglonglonglonglonglonglonglong%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- +(0 rows) + +-- Test we error out when creating a primary key on a partition table with a long name if we cannot +-- switch to sequential execution +\c - - :master_host :master_port +BEGIN; + SELECT count(*) FROM AT_AddConstNoName.dist_partitioned_table; + count +--------------------------------------------------------------------- + 0 +(1 row) + + ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD PRIMARY KEY(partition_col); +ERROR: The primary key name (longlonglonglonglonglonglonglonglonglonglonglo_537570f5_54_pkey) 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 AT_AddConstNoName.dist_partitioned_table; + count +--------------------------------------------------------------------- + 0 +(1 row) + + ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD PRIMARY KEY(partition_col); + ROLLBACK; +DROP TABLE AT_AddConstNoName.dist_partitioned_table; +-- Test primary key name is generated by postgres for citus local table. +\c - - :master_host :master_port +SET client_min_messages to ERROR; +SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET client_min_messages; +CREATE TABLE AT_AddConstNoName.citus_local_table(id int, other_column int); +SELECT citus_add_local_table_to_metadata('AT_AddConstNoName.citus_local_table'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE AT_AddConstNoName.citus_local_table ADD PRIMARY KEY(id); +-- Check the primary key is created for the local table and its shard. +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'citus_local_table%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + citus_local_table_pkey + citus_local_table_pkey_5410022 +(2 rows) + +SELECT create_distributed_table('AT_AddConstNoName.citus_local_table','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'citus_local_table%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + citus_local_table_pkey +(1 row) + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'citus_local_table%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + citus_local_table_pkey + citus_local_table_pkey_5410023 + citus_local_table_pkey_5410024 + citus_local_table_pkey_5410025 + citus_local_table_pkey_5410026 +(5 rows) + +\c - - :master_host :master_port +DROP TABLE AT_AddConstNoName.citus_local_table; +-- Test with partitioned citus local table +CREATE TABLE AT_AddConstNoName.citus_local_partitioned_table (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +CREATE TABLE AT_AddConstNoName.p1 PARTITION OF AT_AddConstNoName.citus_local_partitioned_table FOR VALUES FROM ('2021-01-01') TO ('2022-01-01'); +CREATE TABLE AT_AddConstNoName.longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc PARTITION OF AT_AddConstNoName.citus_local_partitioned_table FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'); +SELECT citus_add_local_table_to_metadata('AT_AddConstNoName.citus_local_partitioned_table'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD PRIMARY KEY(partition_col); +SELECT create_distributed_table('AT_AddConstNoName.citus_local_partitioned_table', 'partition_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table DROP CONSTRAINT citus_local_partitioned_table_pkey; +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD PRIMARY KEY(partition_col); +DEBUG: the primary key name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: longlonglonglonglonglonglonglonglonglonglonglo_537570f5_54_pkey +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "citus_local_partitioned_table_pkey" for table "citus_local_partitioned_table" +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "longlonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey" for table "longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc" +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "p1_pkey" for table "p1" +RESET client_min_messages; +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'citus_local_partitioned_table'; + conname +--------------------------------------------------------------------- + citus_local_partitioned_table_pkey +(1 row) + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'longlonglonglonglonglonglonglonglong%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410034 + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410035 + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410036 + longlonglonglonglonglonglonglonglonglonglonglo_9e4e3069_5410037 + longlonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey +(5 rows) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table DROP CONSTRAINT citus_local_partitioned_table_pkey; +SELECT 1 FROM master_remove_node('localhost', :master_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +-- Test with unusual table and column names +CREATE TABLE AT_AddConstNoName."2nd table" ( "2nd id" INTEGER, "3rd id" INTEGER); +SELECT create_distributed_table('AT_AddConstNoName."2nd table"','2nd id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE AT_AddConstNoName."2nd table" ADD PRIMARY KEY ("2nd id", "3rd id"); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = '2nd table'; + conname +--------------------------------------------------------------------- + 2nd table_pkey +(1 row) + +-- Check if a primary key constraint is created for the shard tables on the workers +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE '2nd table%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + 2nd table_pkey + 2nd table_pkey_5410042 + 2nd table_pkey_5410043 + 2nd table_pkey_5410044 + 2nd table_pkey_5410045 +(5 rows) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName."2nd table" DROP CONSTRAINT "2nd table_pkey"; +DROP SCHEMA AT_AddConstNoName CASCADE; +NOTICE: drop cascades to 6 other objects +DETAIL: drop cascades to table at_addconstnoname.products_ref_2 +drop cascades to table at_addconstnoname.products_ref_3 +drop cascades to table at_addconstnoname.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon +drop cascades to table at_addconstnoname.products_ref_3_5410005 +drop cascades to table at_addconstnoname.citus_local_partitioned_table +drop cascades to table at_addconstnoname."2nd table" diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 477d55098..0fb230699 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -151,6 +151,7 @@ test: with_executors with_join with_partitioning with_transactions with_dml test: multi_index_statements test: multi_alter_table_statements test: multi_alter_table_add_constraints +test: multi_alter_table_add_constraints_without_name # ---------- # Tests to check if we inform the user about potential caveats of creating new diff --git a/src/test/regress/sql/arbitrary_configs_alter_table_add_constraint_without_name.sql b/src/test/regress/sql/arbitrary_configs_alter_table_add_constraint_without_name.sql new file mode 100644 index 000000000..1032b0d04 --- /dev/null +++ b/src/test/regress/sql/arbitrary_configs_alter_table_add_constraint_without_name.sql @@ -0,0 +1,66 @@ +-- +-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS_WITHOUT_NAME +-- +-- Test checks whether constraints of distributed tables can be adjusted using +-- the ALTER TABLE ... ADD without specifying a name. +SET search_path TO sc1; + +-- Check "ADD PRIMARY KEY" +ALTER TABLE products ADD PRIMARY KEY(product_no); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products'; + +ALTER TABLE products DROP CONSTRAINT products_pkey; + +ALTER TABLE products ADD PRIMARY KEY(product_no); + + +-- Check "ADD PRIMARY KEY" with reference table +-- Check for name collisions +ALTER TABLE products_ref_3 ADD CONSTRAINT products_ref_pkey PRIMARY KEY(name); +ALTER TABLE products_ref_2 ADD CONSTRAINT products_ref_pkey1 PRIMARY KEY(name); +ALTER TABLE products_ref ADD PRIMARY KEY(name); + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products_ref'; + +ALTER TABLE products_ref DROP CONSTRAINT products_ref_pkey2; + +-- Check with max table name (63 chars) +ALTER TABLE verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon ADD PRIMARY KEY(product_no); + +-- Constraint should be created on the coordinator with a shortened name +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%'; + +ALTER TABLE verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon DROP CONSTRAINT verylonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey; + +-- Test the scenario where a partitioned distributed table has a child with max allowed name +-- Verify that we switch to sequential execution mode to avoid deadlock in this scenario +ALTER TABLE dist_partitioned_table ADD PRIMARY KEY(partition_col); + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'dist_partitioned_table'; + +ALTER TABLE dist_partitioned_table DROP CONSTRAINT dist_partitioned_table_pkey; + +-- Test primary key name is generated by postgres for citus local table. +ALTER TABLE citus_local_table ADD PRIMARY KEY(id); + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'citus_local_table'; diff --git a/src/test/regress/sql/arbitrary_configs_alter_table_add_constraint_without_name_create.sql b/src/test/regress/sql/arbitrary_configs_alter_table_add_constraint_without_name_create.sql new file mode 100644 index 000000000..549d9923c --- /dev/null +++ b/src/test/regress/sql/arbitrary_configs_alter_table_add_constraint_without_name_create.sql @@ -0,0 +1,57 @@ +-- +-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS_WITHOUT_NAME +-- +-- Test checks whether constraints of distributed tables can be adjusted using +-- the ALTER TABLE ... ADD without specifying a name. + +-- Check "ADD PRIMARY KEY" +CREATE SCHEMA sc1; +SET search_path TO sc1; + +CREATE TABLE products ( + product_no integer, + name text, + price numeric +); + +SELECT create_distributed_table('products', 'product_no'); + +CREATE TABLE sc1.products_ref ( + product_no integer, + name text, + price numeric +); + +CREATE TABLE products_ref_2 ( + product_no integer, + name text, + price numeric +); + +CREATE TABLE products_ref_3 ( + product_no integer, + name text, + price numeric +); + +SELECT create_reference_table('products_ref'); +SELECT create_reference_table('products_ref_3'); + +-- Check with max table name (63 chars) +CREATE TABLE verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonger ( + product_no integer, + name text, + price numeric + ); + +SELECT create_distributed_table('verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon', 'product_no'); + +-- Test the scenario where a partitioned distributed table has a child with max allowed name +-- Verify that we switch to sequential execution mode to avoid deadlock in this scenario +CREATE TABLE dist_partitioned_table (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +CREATE TABLE p1 PARTITION OF dist_partitioned_table FOR VALUES FROM ('2021-01-01') TO ('2022-01-01'); +CREATE TABLE longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc PARTITION OF dist_partitioned_table FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'); +SELECT create_distributed_table('dist_partitioned_table', 'partition_col'); + +-- Test primary key name is generated by postgres for citus local table. +CREATE TABLE citus_local_table(id int, other_column int); diff --git a/src/test/regress/sql/multi_alter_table_add_constraints_without_name.sql b/src/test/regress/sql/multi_alter_table_add_constraints_without_name.sql new file mode 100644 index 000000000..75bcb1bbb --- /dev/null +++ b/src/test/regress/sql/multi_alter_table_add_constraints_without_name.sql @@ -0,0 +1,261 @@ +-- +-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS_WITHOUT_NAME +-- +-- Test checks whether constraints of distributed tables can be adjusted using +-- the ALTER TABLE ... ADD without specifying a name. + +ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 5410000; +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART 5410000; + +CREATE SCHEMA AT_AddConstNoName; + +-- Check "ADD PRIMARY KEY" +CREATE TABLE AT_AddConstNoName.products ( + product_no integer, + name text, + price numeric +); + +SELECT create_distributed_table('AT_AddConstNoName.products', 'product_no'); + +ALTER TABLE AT_AddConstNoName.products ADD PRIMARY KEY(product_no); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products'; + +-- Check that the primary key name created on the coordinator is sent to workers and +-- the constraints created for the shard tables conform to the _shardid scheme. +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products_5410000'; + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.products DROP CONSTRAINT products_pkey; + +ALTER TABLE AT_AddConstNoName.products ADD PRIMARY KEY(product_no); + +DROP TABLE AT_AddConstNoName.products; + +-- Check "ADD PRIMARY KEY" with reference table +CREATE TABLE AT_AddConstNoName.products_ref ( + product_no integer, + name text, + price numeric +); + +CREATE TABLE AT_AddConstNoName.products_ref_2 ( + product_no integer, + name text, + price numeric +); + +CREATE TABLE AT_AddConstNoName.products_ref_3 ( + product_no integer, + name text, + price numeric +); + +SELECT create_reference_table('AT_AddConstNoName.products_ref'); +SELECT create_reference_table('AT_AddConstNoName.products_ref_3'); + +-- Check for name collisions +ALTER TABLE AT_AddConstNoName.products_ref_3 ADD CONSTRAINT products_ref_pkey PRIMARY KEY(name); +ALTER TABLE AT_AddConstNoName.products_ref_2 ADD CONSTRAINT products_ref_pkey1 PRIMARY KEY(name); +ALTER TABLE AT_AddConstNoName.products_ref ADD PRIMARY KEY(name); + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'products_ref'; + +ALTER TABLE AT_AddConstNoName.products_ref DROP CONSTRAINT products_ref_pkey2; + +DROP TABLE AT_AddConstNoName.products_ref; + +-- Check with max table name (63 chars) +CREATE TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonger ( + product_no integer, + name text, + price numeric + ); + +SELECT create_distributed_table('AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon', 'product_no'); + +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon ADD PRIMARY KEY(product_no); + +-- Constraint should be created on the coordinator with a shortened name +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%'; + +-- Constraints for the main table and the shards should be created on the worker with a shortened name +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%' ORDER BY con.conname ASC; + +-- Constraint can be deleted via the coordinator +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon DROP CONSTRAINT verylonglonglonglonglonglonglonglonglonglonglonglonglonglo_pkey; + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'very%'; + +-- Test the scenario where a partitioned distributed table has a child with max allowed name +-- Verify that we switch to sequential execution mode to avoid deadlock in this scenario +\c - - :master_host :master_port +CREATE TABLE AT_AddConstNoName.dist_partitioned_table (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +CREATE TABLE AT_AddConstNoName.p1 PARTITION OF AT_AddConstNoName.dist_partitioned_table FOR VALUES FROM ('2021-01-01') TO ('2022-01-01'); +CREATE TABLE AT_AddConstNoName.longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc PARTITION OF AT_AddConstNoName.dist_partitioned_table FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'); +SELECT create_distributed_table('AT_AddConstNoName.dist_partitioned_table', 'partition_col'); + +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD PRIMARY KEY(partition_col); +RESET client_min_messages; + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'dist_partitioned_table'; + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'longlonglonglonglonglonglonglonglong%' ORDER BY con.conname ASC; + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.dist_partitioned_table DROP CONSTRAINT dist_partitioned_table_pkey; + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'longlonglonglonglonglonglonglonglong%' ORDER BY con.conname ASC; + +-- Test we error out when creating a primary key on a partition table with a long name if we cannot +-- switch to sequential execution +\c - - :master_host :master_port +BEGIN; + SELECT count(*) FROM AT_AddConstNoName.dist_partitioned_table; + ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD PRIMARY KEY(partition_col); +ROLLBACK; +-- try inside a sequential block +BEGIN; + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; + SELECT count(*) FROM AT_AddConstNoName.dist_partitioned_table; + ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD PRIMARY KEY(partition_col); + ROLLBACK; + +DROP TABLE AT_AddConstNoName.dist_partitioned_table; + +-- Test primary key name is generated by postgres for citus local table. +\c - - :master_host :master_port +SET client_min_messages to ERROR; +SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); +RESET client_min_messages; + +CREATE TABLE AT_AddConstNoName.citus_local_table(id int, other_column int); +SELECT citus_add_local_table_to_metadata('AT_AddConstNoName.citus_local_table'); + +ALTER TABLE AT_AddConstNoName.citus_local_table ADD PRIMARY KEY(id); + +-- Check the primary key is created for the local table and its shard. +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'citus_local_table%' ORDER BY con.conname ASC; + +SELECT create_distributed_table('AT_AddConstNoName.citus_local_table','id'); + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'citus_local_table%' ORDER BY con.conname ASC; + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'citus_local_table%' ORDER BY con.conname ASC; + +\c - - :master_host :master_port +DROP TABLE AT_AddConstNoName.citus_local_table; + +-- Test with partitioned citus local table +CREATE TABLE AT_AddConstNoName.citus_local_partitioned_table (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +CREATE TABLE AT_AddConstNoName.p1 PARTITION OF AT_AddConstNoName.citus_local_partitioned_table FOR VALUES FROM ('2021-01-01') TO ('2022-01-01'); +CREATE TABLE AT_AddConstNoName.longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc PARTITION OF AT_AddConstNoName.citus_local_partitioned_table FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'); +SELECT citus_add_local_table_to_metadata('AT_AddConstNoName.citus_local_partitioned_table'); + +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD PRIMARY KEY(partition_col); + +SELECT create_distributed_table('AT_AddConstNoName.citus_local_partitioned_table', 'partition_col'); + +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table DROP CONSTRAINT citus_local_partitioned_table_pkey; + +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD PRIMARY KEY(partition_col); +RESET client_min_messages; + +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = 'citus_local_partitioned_table'; + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE 'longlonglonglonglonglonglonglonglong%' ORDER BY con.conname ASC; + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table DROP CONSTRAINT citus_local_partitioned_table_pkey; + +SELECT 1 FROM master_remove_node('localhost', :master_port); + +-- Test with unusual table and column names +CREATE TABLE AT_AddConstNoName."2nd table" ( "2nd id" INTEGER, "3rd id" INTEGER); +SELECT create_distributed_table('AT_AddConstNoName."2nd table"','2nd id'); + +ALTER TABLE AT_AddConstNoName."2nd table" ADD PRIMARY KEY ("2nd id", "3rd id"); +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname = '2nd table'; + +-- Check if a primary key constraint is created for the shard tables on the workers +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace + WHERE rel.relname LIKE '2nd table%' ORDER BY con.conname ASC; + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName."2nd table" DROP CONSTRAINT "2nd table_pkey"; + +DROP SCHEMA AT_AddConstNoName CASCADE; diff --git a/src/test/regress/sql_schedule b/src/test/regress/sql_schedule index ca7f32f1a..f07f7af9a 100644 --- a/src/test/regress/sql_schedule +++ b/src/test/regress/sql_schedule @@ -13,3 +13,4 @@ test: functions test: arbitrary_configs_truncate test: arbitrary_configs_truncate_cascade test: arbitrary_configs_truncate_partition +test: arbitrary_configs_alter_table_add_constraint_without_name