diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 1f28ead9b..d609f9703 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -408,12 +408,14 @@ ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod, List *indexOidList = NULL; ListCell *indexOidCell = NULL; + /* we first perform check for foreign constraints */ + ErrorIfNotSupportedForeignConstraint(relation, distributionMethod, distributionColumn, + colocationId); + /* * Citus supports any kind of uniqueness constraints for reference tables * given that they only consist of a single shard and we can simply rely on * Postgres. - * TODO: Here we should be erroring out if there exists any foreign keys - * from/to a reference table. */ if (distributionMethod == DISTRIBUTE_BY_NONE) { @@ -499,10 +501,6 @@ ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod, index_close(indexDesc, NoLock); } - - /* we also perform check for foreign constraints */ - ErrorIfNotSupportedForeignConstraint(relation, distributionMethod, distributionColumn, - colocationId); } @@ -560,6 +558,19 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod, continue; } + referencedTableId = constraintForm->confrelid; + + /* we do not support foreign keys for reference tables */ + if (distributionMethod == DISTRIBUTE_BY_NONE || + (relation->rd_id != referencedTableId && + PartitionMethod(referencedTableId) == DISTRIBUTE_BY_NONE)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create foreign key constraint"), + errdetail("Foreign key constraints are not allowed from or " + "to reference tables."))); + } + /* * ON DELETE SET NULL and ON DELETE SET DEFAULT is not supported. Because we do * not want to set partition column to NULL or default value. @@ -589,8 +600,6 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod, " supported in ON UPDATE operation."))); } - referencedTableId = constraintForm->confrelid; - /* * Some checks are not meaningful if foreign key references the table itself. * Therefore we will skip those checks. diff --git a/src/backend/distributed/executor/multi_utility.c b/src/backend/distributed/executor/multi_utility.c index 9bab7b39b..b7250746d 100644 --- a/src/backend/distributed/executor/multi_utility.c +++ b/src/backend/distributed/executor/multi_utility.c @@ -1494,6 +1494,23 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) errhint("You can issue each subcommand separately"))); } + referencingTableId = RangeVarGetRelid(alterTableStatement->relation, + lockmode, + alterTableStatement->missing_ok); + referencedTableId = RangeVarGetRelid(constraint->pktable, lockmode, + alterTableStatement->missing_ok); + + /* we do not support foreign keys for reference tables */ + if (PartitionMethod(referencingTableId) == DISTRIBUTE_BY_NONE || + PartitionMethod(referencedTableId) == DISTRIBUTE_BY_NONE) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create foreign key constraint"), + errdetail( + "Foreign key constraints are not allowed from or " + "to reference tables."))); + } + /* * ON DELETE SET NULL and ON DELETE SET DEFAULT is not supported. Because * we do not want to set partition column to NULL or default value. @@ -1538,12 +1555,6 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) } /* to enforce foreign constraints, tables must be co-located */ - referencingTableId = RangeVarGetRelid(alterTableStatement->relation, - lockmode, - alterTableStatement->missing_ok); - referencedTableId = RangeVarGetRelid(constraint->pktable, lockmode, - alterTableStatement->missing_ok); - if (!TablesColocated(referencingTableId, referencedTableId)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), diff --git a/src/test/regress/expected/multi_foreign_key.out b/src/test/regress/expected/multi_foreign_key.out index 970d47c37..00d35cbd3 100644 --- a/src/test/regress/expected/multi_foreign_key.out +++ b/src/test/regress/expected/multi_foreign_key.out @@ -773,3 +773,64 @@ SELECT * FROM self_referencing_table2; -- we no longer need those tables DROP TABLE self_referencing_table2; +-- test reference tables +-- test foreign key creation on CREATE TABLE from reference table +CREATE TABLE referenced_by_reference_table(id int PRIMARY KEY, other_column int); +SELECT create_distributed_table('referenced_by_reference_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_by_reference_table(id)); +SELECT create_reference_table('reference_table'); +ERROR: cannot create foreign key constraint +DETAIL: Foreign key constraints are not allowed from or to reference tables. +-- test foreign key creation on CREATE TABLE to reference table +DROP TABLE reference_table; +CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int); +SELECT create_reference_table('reference_table'); + create_reference_table +------------------------ + +(1 row) + +CREATE TABLE references_to_reference_table(id int, referencing_column int REFERENCES reference_table(id)); +SELECT create_distributed_table('references_to_reference_table', 'referencing_column'); +ERROR: cannot create foreign key constraint +DETAIL: Foreign key constraints are not allowed from or to reference tables. +-- test foreign key creation on CREATE TABLE from + to reference table +CREATE TABLE reference_table2(id int, referencing_column int REFERENCES reference_table(id)); +SELECT create_reference_table('reference_table2'); +ERROR: cannot create foreign key constraint +DETAIL: Foreign key constraints are not allowed from or to reference tables. +-- test foreign key creation on ALTER TABLE from reference table +ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_by_reference_table(id); +ERROR: cannot create foreign key constraint +DETAIL: Foreign key constraints are not allowed from or to reference tables. +-- test foreign key creation on ALTER TABLE to reference table +DROP TABLE references_to_reference_table; +CREATE TABLE references_to_reference_table(id int, referencing_column int); +SELECT create_distributed_table('references_to_reference_table', 'referencing_column'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE references_to_reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id); +ERROR: cannot create foreign key constraint +DETAIL: Foreign key constraints are not allowed from or to reference tables. +-- test foreign key creation on ALTER TABLE from + to reference table +DROP TABLE reference_table2; +CREATE TABLE reference_table2(id int, referencing_column int); +SELECT create_reference_table('reference_table2'); + create_reference_table +------------------------ + +(1 row) + +ALTER TABLE reference_table2 ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id); +ERROR: cannot create foreign key constraint +DETAIL: Foreign key constraints are not allowed from or to reference tables. +-- we no longer need those tables +DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table2; diff --git a/src/test/regress/sql/multi_foreign_key.sql b/src/test/regress/sql/multi_foreign_key.sql index 9690b0e26..0601a7fe8 100644 --- a/src/test/regress/sql/multi_foreign_key.sql +++ b/src/test/regress/sql/multi_foreign_key.sql @@ -449,3 +449,42 @@ SELECT * FROM self_referencing_table2; -- we no longer need those tables DROP TABLE self_referencing_table2; + + +-- test reference tables +-- test foreign key creation on CREATE TABLE from reference table +CREATE TABLE referenced_by_reference_table(id int PRIMARY KEY, other_column int); +SELECT create_distributed_table('referenced_by_reference_table', 'id'); + +CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_by_reference_table(id)); +SELECT create_reference_table('reference_table'); + +-- test foreign key creation on CREATE TABLE to reference table +DROP TABLE reference_table; +CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int); +SELECT create_reference_table('reference_table'); + +CREATE TABLE references_to_reference_table(id int, referencing_column int REFERENCES reference_table(id)); +SELECT create_distributed_table('references_to_reference_table', 'referencing_column'); + +-- test foreign key creation on CREATE TABLE from + to reference table +CREATE TABLE reference_table2(id int, referencing_column int REFERENCES reference_table(id)); +SELECT create_reference_table('reference_table2'); + +-- test foreign key creation on ALTER TABLE from reference table +ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_by_reference_table(id); + +-- test foreign key creation on ALTER TABLE to reference table +DROP TABLE references_to_reference_table; +CREATE TABLE references_to_reference_table(id int, referencing_column int); +SELECT create_distributed_table('references_to_reference_table', 'referencing_column'); +ALTER TABLE references_to_reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id); + +-- test foreign key creation on ALTER TABLE from + to reference table +DROP TABLE reference_table2; +CREATE TABLE reference_table2(id int, referencing_column int); +SELECT create_reference_table('reference_table2'); +ALTER TABLE reference_table2 ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id); + +-- we no longer need those tables +DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table2;