mirror of https://github.com/citusdata/citus.git
Merge pull request #1061 from citusdata/error_out_fk_on_reference_tables
Error out on foreign keys with reference tablespull/1938/head
commit
1c58ec1d3d
|
@ -408,12 +408,20 @@ ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod,
|
||||||
List *indexOidList = NULL;
|
List *indexOidList = NULL;
|
||||||
ListCell *indexOidCell = NULL;
|
ListCell *indexOidCell = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Citus supports any kind of uniqueness constraints for reference tables
|
* 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
|
* given that they only consist of a single shard and we can simply rely on
|
||||||
* Postgres.
|
* Postgres.
|
||||||
* TODO: Here we should be erroring out if there exists any foreign keys
|
|
||||||
* from/to a reference table.
|
|
||||||
*/
|
*/
|
||||||
if (distributionMethod == DISTRIBUTE_BY_NONE)
|
if (distributionMethod == DISTRIBUTE_BY_NONE)
|
||||||
{
|
{
|
||||||
|
@ -499,10 +507,6 @@ ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod,
|
||||||
|
|
||||||
index_close(indexDesc, NoLock);
|
index_close(indexDesc, NoLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we also perform check for foreign constraints */
|
|
||||||
ErrorIfNotSupportedForeignConstraint(relation, distributionMethod, distributionColumn,
|
|
||||||
colocationId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -542,6 +546,7 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod,
|
||||||
bool isNull = false;
|
bool isNull = false;
|
||||||
int attrIdx = 0;
|
int attrIdx = 0;
|
||||||
bool foreignConstraintOnPartitionColumn = false;
|
bool foreignConstraintOnPartitionColumn = false;
|
||||||
|
bool selfReferencingTable = false;
|
||||||
|
|
||||||
pgConstraint = heap_open(ConstraintRelationId, AccessShareLock);
|
pgConstraint = heap_open(ConstraintRelationId, AccessShareLock);
|
||||||
ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ,
|
ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
@ -560,6 +565,27 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
referencedTableId = constraintForm->confrelid;
|
||||||
|
selfReferencingTable = relation->rd_id == referencedTableId;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 ||
|
||||||
|
(!selfReferencingTable &&
|
||||||
|
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
|
* 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.
|
* not want to set partition column to NULL or default value.
|
||||||
|
@ -589,13 +615,11 @@ ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod,
|
||||||
" supported in ON UPDATE operation.")));
|
" supported in ON UPDATE operation.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
referencedTableId = constraintForm->confrelid;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some checks are not meaningful if foreign key references the table itself.
|
* Some checks are not meaningful if foreign key references the table itself.
|
||||||
* Therefore we will skip those checks.
|
* Therefore we will skip those checks.
|
||||||
*/
|
*/
|
||||||
if (referencedTableId != relation->rd_id)
|
if (!selfReferencingTable)
|
||||||
{
|
{
|
||||||
if (!IsDistributedTable(referencedTableId))
|
if (!IsDistributedTable(referencedTableId))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1494,6 +1494,23 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
||||||
errhint("You can issue each subcommand separately")));
|
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
|
* 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.
|
* 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 */
|
/* 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))
|
if (!TablesColocated(referencingTableId, referencedTableId))
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
|
|
@ -773,3 +773,122 @@ SELECT * FROM self_referencing_table2;
|
||||||
|
|
||||||
-- we no longer need those tables
|
-- we no longer need those tables
|
||||||
DROP TABLE self_referencing_table2;
|
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_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.
|
||||||
|
-- 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_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_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_table_second, referenced_local_table, self_referencing_reference_table;
|
||||||
|
|
|
@ -449,3 +449,78 @@ SELECT * FROM self_referencing_table2;
|
||||||
|
|
||||||
-- we no longer need those tables
|
-- we no longer need those tables
|
||||||
DROP TABLE self_referencing_table2;
|
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_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
|
||||||
|
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_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_table_second, referenced_local_table, self_referencing_reference_table;
|
||||||
|
|
Loading…
Reference in New Issue