diff --git a/src/backend/distributed/commands/policy.c b/src/backend/distributed/commands/policy.c index 5250f9580..0d66e150e 100644 --- a/src/backend/distributed/commands/policy.c +++ b/src/backend/distributed/commands/policy.c @@ -29,7 +29,6 @@ static const char * unparse_policy_command(const char aclchar); -static void AddRangeTableEntryToQueryCompat(ParseState *parseState, Relation relation); static RowSecurityPolicy * GetPolicyByName(Oid relationId, const char *policyName); static List * GetPolicyListForRelation(Oid relationId); static char * CreatePolicyCommandForPolicy(Oid relationId, RowSecurityPolicy *policy); @@ -287,7 +286,7 @@ PostprocessCreatePolicyStmt(Node *node, const char *queryString) * AddRangeTableEntryToQueryCompat adds the given relation to query. * This method is a compatibility wrapper. */ -static void +void AddRangeTableEntryToQueryCompat(ParseState *parseState, Relation relation) { ParseNamespaceItem *rte = addRangeTableEntryForRelation(parseState, relation, diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index e38e1af53..9a207dfa0 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -762,6 +762,13 @@ GenerateConstraintName(const char *tabname, Oid namespaceId, Constraint *constra break; } + case CONSTR_CHECK: + { + conname = ChooseConstraintName(tabname, NULL, "check", namespaceId, NULL); + + break; + } + default: { ereport(ERROR, (errmsg( @@ -1906,7 +1913,8 @@ ConstrTypeCitusCanDefaultName(ConstrType constrType) { return constrType == CONSTR_PRIMARY || constrType == CONSTR_UNIQUE || - constrType == CONSTR_EXCLUSION; + constrType == CONSTR_EXCLUSION || + constrType == CONSTR_CHECK; } diff --git a/src/backend/distributed/deparser/deparse_table_stmts.c b/src/backend/distributed/deparser/deparse_table_stmts.c index 5cd810f52..0ad0748bb 100644 --- a/src/backend/distributed/deparser/deparse_table_stmts.c +++ b/src/backend/distributed/deparser/deparse_table_stmts.c @@ -16,12 +16,20 @@ #include "distributed/version_compat.h" #include "nodes/nodes.h" #include "nodes/parsenodes.h" +#include "parser/parse_expr.h" #include "parser/parse_type.h" +#include "parser/parse_relation.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/ruleutils.h" + +#include "distributed/namespace_utils.h" +#include "commands/tablecmds.h" static void AppendAlterTableSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt); static void AppendAlterTableStmt(StringInfo buf, AlterTableStmt *stmt); -static void AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd); +static void AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd, + AlterTableStmt *stmt); static void AppendAlterTableCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd); char * @@ -97,7 +105,7 @@ AppendAlterTableStmt(StringInfo buf, AlterTableStmt *stmt) } AlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(cmdCell)); - AppendAlterTableCmd(buf, alterTableCmd); + AppendAlterTableCmd(buf, alterTableCmd, stmt); } appendStringInfoString(buf, ";"); @@ -109,7 +117,8 @@ AppendAlterTableStmt(StringInfo buf, AlterTableStmt *stmt) * in the ADD CONSTRAINT {PRIMARY KEY, UNIQUE, EXCLUSION} form and appends it to the buf. */ static void -AppendAlterTableCmdAddConstraint(StringInfo buf, Constraint *constraint) +AppendAlterTableCmdAddConstraint(StringInfo buf, Constraint *constraint, + AlterTableStmt *stmt) { /* Need to deparse the alter table constraint command only if we are adding a constraint name.*/ if (constraint->conname == NULL) @@ -229,6 +238,41 @@ AppendAlterTableCmdAddConstraint(StringInfo buf, Constraint *constraint) appendStringInfoString(buf, " )"); } + else if (constraint->contype == CONSTR_CHECK) + { + LOCKMODE lockmode = AlterTableGetLockLevel(stmt->cmds); + Oid leftRelationId = AlterTableLookupRelation(stmt, lockmode); + + /* To be able to use deparse_expression function, which creates an expression string, + * the expression should be provided in its cooked form. We transform the raw expression + * to cooked form. + */ + ParseState *pstate = make_parsestate(NULL); + Relation relation = table_open(leftRelationId, AccessShareLock); + + /* Add table name to the name space in parse state. Otherwise column names + * cannot be found. + */ + AddRangeTableEntryToQueryCompat(pstate, relation); + + Node *exprCooked = transformExpr(pstate, constraint->raw_expr, + + EXPR_KIND_CHECK_CONSTRAINT); + + char *relationName = get_rel_name(leftRelationId); + List *relationCtx = deparse_context_for(relationName, leftRelationId); + + char *exprSql = deparse_expression(exprCooked, relationCtx, false, false); + + relation_close(relation, NoLock); + + appendStringInfo(buf, " CHECK (%s)", exprSql); + + if (constraint->is_no_inherit) + { + appendStringInfo(buf, " NO INHERIT"); + } + } if (constraint->deferrable) { @@ -248,7 +292,7 @@ AppendAlterTableCmdAddConstraint(StringInfo buf, Constraint *constraint) * AT_AddColumn, AT_SetNotNull and AT_AddConstraint {PRIMARY KEY, UNIQUE, EXCLUDE}. */ static void -AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd) +AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd, AlterTableStmt *stmt) { switch (alterTableCmd->subtype) { @@ -268,7 +312,7 @@ AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd) */ if (ConstrTypeCitusCanDefaultName(constraint->contype)) { - AppendAlterTableCmdAddConstraint(buf, constraint); + AppendAlterTableCmdAddConstraint(buf, constraint, stmt); break; } } diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index add0937ec..87a24aebf 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -408,6 +408,7 @@ extern void RenamePolicyEventExtendNames(RenameStmt *stmt, const char *schemaNam extern void DropPolicyEventExtendNames(DropStmt *stmt, const char *schemaName, uint64 shardId); +extern void AddRangeTableEntryToQueryCompat(ParseState *parseState, Relation relation); /* rename.c - forward declarations*/ extern List * PreprocessRenameStmt(Node *renameStmt, const char *renameCommand, 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 c9d9103ce..367231668 100644 --- a/src/test/regress/expected/multi_alter_table_add_constraints.out +++ b/src/test/regress/expected/multi_alter_table_add_constraints.out @@ -432,10 +432,6 @@ SELECT create_distributed_table('products', 'product_no'); ALTER TABLE products ADD CONSTRAINT unn_1 UNIQUE(product_no, price), ADD CONSTRAINT unn_2 UNIQUE(product_no, name); ERROR: cannot execute ADD CONSTRAINT command with other subcommands HINT: You can issue each subcommand separately --- Tests for constraints without name --- Commands below should error out since constraints do not have the name -ALTER TABLE products ADD CHECK(product_no <> 0); -ERROR: cannot create constraint without a name on a distributed table -- ... with names, we can add/drop the constraints just fine ALTER TABLE products ADD CONSTRAINT nonzero_product_no CHECK(product_no <> 0); ALTER TABLE products ADD CONSTRAINT uniq_product_no 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 index 3c1cd64c7..72bd738f6 100644 --- 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 @@ -230,6 +230,56 @@ SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred \c - - :master_host :master_port ALTER TABLE AT_AddConstNoName.products DROP CONSTRAINT products_name_product_no_excl; +-- Check "ADD CHECK" +ALTER TABLE AT_AddConstNoName.products ADD CHECK (product_no > 0 AND price > 0); +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_check +(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 = 'products_5410000'; + conname +--------------------------------------------------------------------- + products_check_5410000 +(1 row) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.products DROP CONSTRAINT products_check; +-- Check "ADD CHECK ... NOINHERIT" +ALTER TABLE AT_AddConstNoName.products ADD CHECK (product_no > 0 AND price > 0) NO INHERIT; +SELECT con.conname, con.connoinherit + 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 | connoinherit +--------------------------------------------------------------------- + products_check | t +(1 row) + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname, connoinherit + 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 | connoinherit +--------------------------------------------------------------------- + products_check_5410000 | t +(1 row) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.products DROP CONSTRAINT products_check; DROP TABLE AT_AddConstNoName.products; -- Check "ADD PRIMARY KEY" with reference table CREATE TABLE AT_AddConstNoName.products_ref ( @@ -303,6 +353,22 @@ SELECT con.conname products_ref_product_no_excl2 (1 row) +ALTER TABLE AT_AddConstNoName.products_ref DROP CONSTRAINT products_ref_product_no_excl2; +-- Check that name collisions are handled for CHECK +ALTER TABLE AT_AddConstNoName.products_ref_3 ADD CONSTRAINT products_ref_check CHECK (product_no > 0); +ALTER TABLE AT_AddConstNoName.products_ref_2 ADD CONSTRAINT products_ref_check1 CHECK (product_no > 0); +ALTER TABLE AT_AddConstNoName.products_ref ADD CHECK (product_no > 0); +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_check2 +(1 row) + +ALTER TABLE AT_AddConstNoName.products_ref DROP CONSTRAINT products_ref_check2; DROP TABLE AT_AddConstNoName.products_ref; -- Check "ADD PRIMARY KEY" with max table name (63 chars) CREATE TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonger ( @@ -444,6 +510,49 @@ SELECT con.conname --------------------------------------------------------------------- (0 rows) +-- Check "ADD CHECK" with max table name (63 chars) +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon ADD CHECK (product_no > 0); +-- 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 +--------------------------------------------------------------------- + verylonglonglonglonglonglonglonglonglonglonglonglonglongl_check +(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_d943e063_5410006 + verylonglonglonglonglonglonglonglonglonglonglo_d943e063_5410007 + verylonglonglonglonglonglonglonglonglonglonglo_d943e063_5410008 + verylonglonglonglonglonglonglonglonglonglonglo_d943e063_5410009 + verylonglonglonglonglonglonglonglonglonglonglonglonglongl_check +(5 rows) + +-- CHECK constraint can be deleted via the coordinator +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon DROP CONSTRAINT verylonglonglonglonglonglonglonglonglonglonglonglonglongl_check; +\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 @@ -539,6 +648,40 @@ SELECT con.conname \c - - :master_host :master_port ALTER TABLE AT_AddConstNoName.dist_partitioned_table DROP CONSTRAINT dist_partitioned_table_partition_col_key; +-- Check "ADD CHECK" +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD CHECK(dist_col >= another_col); +DEBUG: the constraint name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: longlonglonglonglonglonglonglonglonglonglonglo_537570f5_5_check +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_check +(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 +--------------------------------------------------------------------- + dist_partitioned_table_check + dist_partitioned_table_check + dist_partitioned_table_check + dist_partitioned_table_check + dist_partitioned_table_check +(5 rows) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.dist_partitioned_table DROP CONSTRAINT dist_partitioned_table_check; \c - - :public_worker_1_host :worker_1_port SELECT con.conname FROM pg_catalog.pg_constraint con @@ -599,6 +742,30 @@ BEGIN; ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD UNIQUE(partition_col); ROLLBACK; +-- Check "ADD CHECK" +\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 CHECK(dist_col > another_col); +ERROR: The constraint name (longlonglonglonglonglonglonglonglonglonglonglo_537570f5_5_check) 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 CHECK(dist_col > another_col); + ROLLBACK; DROP TABLE AT_AddConstNoName.dist_partitioned_table; -- Test with Citus Local Tables -- Test "ADD PRIMARY KEY" @@ -735,6 +902,48 @@ SELECT con.conname (5 rows) \c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_table DROP CONSTRAINT citus_local_table_id_excl; +-- Check "ADD CHECK" +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_table ADD CHECK(id > 100); +-- Check the CHECK constraint 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_check +(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_check +(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_check + citus_local_table_check_5410023 + citus_local_table_check_5410024 + citus_local_table_check_5410025 + citus_local_table_check_5410026 +(5 rows) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_table DROP CONSTRAINT citus_local_table_check; 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); @@ -829,6 +1038,51 @@ ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table DROP CONSTRAINT citu -- Check "ADD EXCLUDE" errors out for partitioned table since the postgres does not allow it ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD EXCLUDE(partition_col WITH =); ERROR: exclusion constraints are not supported on partitioned tables +-- Check "ADD CHECK" +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD CHECK (dist_col > 0); +DEBUG: the constraint name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: longlonglonglonglonglonglonglonglonglonglonglo_537570f5_5_check +DEBUG: verifying table "longlonglonglonglonglonglonglonglonglonglonglonglonglonglongabc" +DEBUG: verifying 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_check +(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 +--------------------------------------------------------------------- + citus_local_partitioned_table_check + citus_local_partitioned_table_check + citus_local_partitioned_table_check + citus_local_partitioned_table_check + citus_local_partitioned_table_check +(5 rows) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table DROP CONSTRAINT citus_local_partitioned_table_check; +\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) + +\c - - :master_host :master_port SELECT 1 FROM master_remove_node('localhost', :master_port); ?column? --------------------------------------------------------------------- @@ -933,6 +1187,36 @@ SELECT con.conname \c - - :master_host :master_port ALTER TABLE AT_AddConstNoName."2nd table" DROP CONSTRAINT "2nd table_2nd id_excl"; +-- Check "ADD CHECK" +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName."2nd table" ADD CHECK ("2nd id" > 0 ); +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_check +(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 '2nd table%' ORDER BY con.conname ASC; + conname +--------------------------------------------------------------------- + 2nd table_check + 2nd table_check_5410042 + 2nd table_check_5410043 + 2nd table_check_5410044 + 2nd table_check_5410045 +(5 rows) + +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName."2nd table" DROP CONSTRAINT "2nd table_check"; DROP EXTENSION btree_gist; DROP SCHEMA AT_AddConstNoName CASCADE; NOTICE: drop cascades to 6 other objects diff --git a/src/test/regress/expected/multi_name_lengths.out b/src/test/regress/expected/multi_name_lengths.out index 32e483033..40f892f14 100644 --- a/src/test/regress/expected/multi_name_lengths.out +++ b/src/test/regress/expected/multi_name_lengths.out @@ -67,9 +67,6 @@ SELECT create_distributed_table('name_lengths', 'col1', 'hash'); ALTER TABLE name_lengths ADD COLUMN float_col_12345678901234567890123456789012345678901234567890 FLOAT; ALTER TABLE name_lengths ADD COLUMN date_col_12345678901234567890123456789012345678901234567890 DATE; ALTER TABLE name_lengths ADD COLUMN int_col_12345678901234567890123456789012345678901234567890 INTEGER DEFAULT 1; --- Placeholders for unsupported ALTER TABLE to add constraints with implicit names that are likely too long -ALTER TABLE name_lengths ADD CHECK (date_col_12345678901234567890123456789012345678901234567890 > '2014-01-01'::date); -ERROR: cannot create constraint without a name on a distributed table \c - - :public_worker_1_host :worker_1_port SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.name_lengths_225002'::regclass ORDER BY 1 DESC, 2 DESC; Column | Type | Modifiers diff --git a/src/test/regress/sql/multi_alter_table_add_constraints.sql b/src/test/regress/sql/multi_alter_table_add_constraints.sql index f553c9231..a82bb64ca 100644 --- a/src/test/regress/sql/multi_alter_table_add_constraints.sql +++ b/src/test/regress/sql/multi_alter_table_add_constraints.sql @@ -376,10 +376,6 @@ SELECT create_distributed_table('products', 'product_no'); -- Should error out since add constraint is not the single subcommand ALTER TABLE products ADD CONSTRAINT unn_1 UNIQUE(product_no, price), ADD CONSTRAINT unn_2 UNIQUE(product_no, name); --- Tests for constraints without name --- Commands below should error out since constraints do not have the name -ALTER TABLE products ADD CHECK(product_no <> 0); - -- ... with names, we can add/drop the constraints just fine ALTER TABLE products ADD CONSTRAINT nonzero_product_no CHECK(product_no <> 0); ALTER TABLE products ADD CONSTRAINT uniq_product_no EXCLUDE USING btree (product_no with =); 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 index 0f8263f7a..b532577fa 100644 --- 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 @@ -184,6 +184,43 @@ SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred \c - - :master_host :master_port ALTER TABLE AT_AddConstNoName.products DROP CONSTRAINT products_name_product_no_excl; +-- Check "ADD CHECK" +ALTER TABLE AT_AddConstNoName.products ADD CHECK (product_no > 0 AND price > 0); +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'; + +\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_check; + +-- Check "ADD CHECK ... NOINHERIT" +ALTER TABLE AT_AddConstNoName.products ADD CHECK (product_no > 0 AND price > 0) NO INHERIT; + +SELECT con.conname, con.connoinherit + 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'; + +\c - - :public_worker_1_host :worker_1_port +SELECT con.conname, connoinherit + 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_check; + DROP TABLE AT_AddConstNoName.products; -- Check "ADD PRIMARY KEY" with reference table @@ -245,6 +282,21 @@ SELECT con.conname 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_product_no_excl2; + +-- Check that name collisions are handled for CHECK +ALTER TABLE AT_AddConstNoName.products_ref_3 ADD CONSTRAINT products_ref_check CHECK (product_no > 0); +ALTER TABLE AT_AddConstNoName.products_ref_2 ADD CONSTRAINT products_ref_check1 CHECK (product_no > 0); +ALTER TABLE AT_AddConstNoName.products_ref ADD CHECK (product_no > 0); + +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_check2; + DROP TABLE AT_AddConstNoName.products_ref; -- Check "ADD PRIMARY KEY" with max table name (63 chars) @@ -337,6 +389,35 @@ SELECT con.conname \c - - :master_host :master_port ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon DROP CONSTRAINT verylonglonglonglonglonglonglonglonglonglonglon_product_no_excl; +\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%'; + +-- Check "ADD CHECK" with max table name (63 chars) +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon ADD CHECK (product_no > 0); +-- 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; + +-- CHECK constraint can be deleted via the coordinator +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon DROP CONSTRAINT verylonglonglonglonglonglonglonglonglonglonglonglonglongl_check; + \c - - :public_worker_1_host :worker_1_port SELECT con.conname FROM pg_catalog.pg_constraint con @@ -402,6 +483,27 @@ SELECT con.conname \c - - :master_host :master_port ALTER TABLE AT_AddConstNoName.dist_partitioned_table DROP CONSTRAINT dist_partitioned_table_partition_col_key; +-- Check "ADD CHECK" +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD CHECK(dist_col >= another_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_check; + \c - - :public_worker_1_host :worker_1_port SELECT con.conname FROM pg_catalog.pg_constraint con @@ -438,6 +540,19 @@ BEGIN; ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD UNIQUE(partition_col); ROLLBACK; +-- Check "ADD CHECK" +\c - - :master_host :master_port +BEGIN; + SELECT count(*) FROM AT_AddConstNoName.dist_partitioned_table; + ALTER TABLE AT_AddConstNoName.dist_partitioned_table ADD CHECK(dist_col > another_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 CHECK(dist_col > another_col); + ROLLBACK; + DROP TABLE AT_AddConstNoName.dist_partitioned_table; -- Test with Citus Local Tables @@ -524,6 +639,35 @@ SELECT con.conname WHERE rel.relname LIKE 'citus_local_table%' ORDER BY con.conname ASC; \c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_table DROP CONSTRAINT citus_local_table_id_excl; + +-- Check "ADD CHECK" +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName.citus_local_table ADD CHECK(id > 100); + +-- Check the CHECK constraint 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 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 +ALTER TABLE AT_AddConstNoName.citus_local_table DROP CONSTRAINT citus_local_table_check; + DROP TABLE AT_AddConstNoName.citus_local_table; -- Test with partitioned citus local table @@ -587,6 +731,35 @@ ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table DROP CONSTRAINT citu -- Check "ADD EXCLUDE" errors out for partitioned table since the postgres does not allow it ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD EXCLUDE(partition_col WITH =); +-- Check "ADD CHECK" +SET client_min_messages TO DEBUG1; +ALTER TABLE AT_AddConstNoName.citus_local_partitioned_table ADD CHECK (dist_col > 0); +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_check; + +\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 SELECT 1 FROM master_remove_node('localhost', :master_port); -- Test with unusual table and column names @@ -652,5 +825,25 @@ SELECT con.conname \c - - :master_host :master_port ALTER TABLE AT_AddConstNoName."2nd table" DROP CONSTRAINT "2nd table_2nd id_excl"; +-- Check "ADD CHECK" +\c - - :master_host :master_port +ALTER TABLE AT_AddConstNoName."2nd table" ADD CHECK ("2nd id" > 0 ); + +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'; + +\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_check"; + DROP EXTENSION btree_gist; DROP SCHEMA AT_AddConstNoName CASCADE; diff --git a/src/test/regress/sql/multi_name_lengths.sql b/src/test/regress/sql/multi_name_lengths.sql index 3da879157..7aff3b33e 100644 --- a/src/test/regress/sql/multi_name_lengths.sql +++ b/src/test/regress/sql/multi_name_lengths.sql @@ -45,9 +45,6 @@ ALTER TABLE name_lengths ADD COLUMN float_col_1234567890123456789012345678901234 ALTER TABLE name_lengths ADD COLUMN date_col_12345678901234567890123456789012345678901234567890 DATE; ALTER TABLE name_lengths ADD COLUMN int_col_12345678901234567890123456789012345678901234567890 INTEGER DEFAULT 1; --- Placeholders for unsupported ALTER TABLE to add constraints with implicit names that are likely too long -ALTER TABLE name_lengths ADD CHECK (date_col_12345678901234567890123456789012345678901234567890 > '2014-01-01'::date); - \c - - :public_worker_1_host :worker_1_port SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='public.name_lengths_225002'::regclass ORDER BY 1 DESC, 2 DESC; \c - - :master_host :master_port