diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index d609f9703..93d1689a2 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -408,7 +408,13 @@ ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod, List *indexOidList = NULL; ListCell *indexOidCell = NULL; - /* we first perform check for foreign constraints */ + /* + * We first perform check for foreign constraints. It is important to do this check + * before next check, because other types of constraints are allowed on reference + * tables and we return early for those constraints thanks to next check. Therefore, + * for reference tables, we first check for foreing constraints and if they are OK, + * we do not error out for other types of constraints. + */ ErrorIfNotSupportedForeignConstraint(relation, distributionMethod, distributionColumn, colocationId); @@ -540,6 +546,7 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod, bool isNull = false; int attrIdx = 0; bool foreignConstraintOnPartitionColumn = false; + bool selfReferencingTable = false; pgConstraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, @@ -559,10 +566,18 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod, } referencedTableId = constraintForm->confrelid; + selfReferencingTable = relation->rd_id == referencedTableId; - /* we do not support foreign keys for reference tables */ + /* + * We do not support foreign keys for reference tables. Here we skip the second + * part of check if the table is a self referencing table because; + * - PartitionMethod only works for distributed tables and this table is not + * distributed yet. + * - Since referencing and referenced tables are same, it is OK to not checking + * distribution method twice. + */ if (distributionMethod == DISTRIBUTE_BY_NONE || - (relation->rd_id != referencedTableId && + (!selfReferencingTable && PartitionMethod(referencedTableId) == DISTRIBUTE_BY_NONE)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -604,7 +619,7 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod, * Some checks are not meaningful if foreign key references the table itself. * Therefore we will skip those checks. */ - if (referencedTableId != relation->rd_id) + if (!selfReferencingTable) { if (!IsDistributedTable(referencedTableId)) { diff --git a/src/test/regress/expected/multi_foreign_key.out b/src/test/regress/expected/multi_foreign_key.out index 00d35cbd3..20058b869 100644 --- a/src/test/regress/expected/multi_foreign_key.out +++ b/src/test/regress/expected/multi_foreign_key.out @@ -800,11 +800,40 @@ SELECT create_distributed_table('references_to_reference_table', 'referencing_co 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'); +CREATE TABLE reference_table_second(id int, referencing_column int REFERENCES reference_table(id)); +SELECT create_reference_table('reference_table_second'); +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 reference table to local table +CREATE TABLE referenced_local_table(id int PRIMARY KEY, other_column int); +DROP TABLE reference_table CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to constraint references_to_reference_table_referencing_column_fkey on table references_to_reference_table +drop cascades to constraint reference_table_second_referencing_column_fkey on table reference_table_second +CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_local_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 on self referencing reference table +CREATE TABLE self_referencing_reference_table( + id int, + other_column int, + other_column_ref int, + PRIMARY KEY(id, other_column), + FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column) +); +SELECT create_reference_table('self_referencing_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 ALTER TABLE from 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) + 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. @@ -821,16 +850,45 @@ ALTER TABLE references_to_reference_table ADD CONSTRAINT fk FOREIGN KEY(referenc 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'); +DROP TABLE reference_table_second; +CREATE TABLE reference_table_second(id int, referencing_column int); +SELECT create_reference_table('reference_table_second'); create_reference_table ------------------------ (1 row) -ALTER TABLE reference_table2 ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id); +ALTER TABLE reference_table_second 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 reference table to local 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) + +ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_local_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 on self referencing reference table +DROP TABLE self_referencing_reference_table; +CREATE TABLE self_referencing_reference_table( + id int, + other_column int, + other_column_ref int, + PRIMARY KEY(id, other_column) +); +SELECT create_reference_table('self_referencing_reference_table'); + create_reference_table +------------------------ + +(1 row) + +ALTER TABLE self_referencing_reference_table ADD CONSTRAINT fk FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column); 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; +DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table_second, referenced_local_table, self_referencing_reference_table; diff --git a/src/test/regress/sql/multi_foreign_key.sql b/src/test/regress/sql/multi_foreign_key.sql index 0601a7fe8..f7258521d 100644 --- a/src/test/regress/sql/multi_foreign_key.sql +++ b/src/test/regress/sql/multi_foreign_key.sql @@ -468,10 +468,29 @@ CREATE TABLE references_to_reference_table(id int, referencing_column int REFERE 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'); +CREATE TABLE reference_table_second(id int, referencing_column int REFERENCES reference_table(id)); +SELECT create_reference_table('reference_table_second'); + +-- test foreign key creation on CREATE TABLE from reference table to local table +CREATE TABLE referenced_local_table(id int PRIMARY KEY, other_column int); +DROP TABLE reference_table CASCADE; +CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_local_table(id)); +SELECT create_reference_table('reference_table'); + +-- test foreign key creation on CREATE TABLE on self referencing reference table +CREATE TABLE self_referencing_reference_table( + id int, + other_column int, + other_column_ref int, + PRIMARY KEY(id, other_column), + FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column) +); +SELECT create_reference_table('self_referencing_reference_table'); -- test foreign key creation on ALTER TABLE from reference table +DROP TABLE reference_table; +CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int); +SELECT create_reference_table('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 @@ -481,10 +500,27 @@ SELECT create_distributed_table('references_to_reference_table', 'referencing_co 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); +DROP TABLE reference_table_second; +CREATE TABLE reference_table_second(id int, referencing_column int); +SELECT create_reference_table('reference_table_second'); +ALTER TABLE reference_table_second ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id); + +-- test foreign key creation on ALTER TABLE from reference table to local table +DROP TABLE reference_table; +CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int); +SELECT create_reference_table('reference_table'); +ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_local_table(id); + +-- test foreign key creation on ALTER TABLE on self referencing reference table +DROP TABLE self_referencing_reference_table; +CREATE TABLE self_referencing_reference_table( + id int, + other_column int, + other_column_ref int, + PRIMARY KEY(id, other_column) +); +SELECT create_reference_table('self_referencing_reference_table'); +ALTER TABLE self_referencing_reference_table ADD CONSTRAINT fk FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column); -- we no longer need those tables -DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table2; +DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table_second, referenced_local_table, self_referencing_reference_table;