From 89a8d6ab95b133aebf3adcf6c20543ab985e922c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?mehmet=20furkan=20=C5=9Fahin?= Date: Thu, 28 Jun 2018 10:25:39 +0300 Subject: [PATCH] FK from dist to ref is tested for partitioning, MX --- .../foreign_key_to_reference_table.out | 55 +- .../foreign_key_to_reference_table_0.out | 1882 +++++++++++++++++ .../mx_foreign_key_to_reference_table.out | 90 + src/test/regress/multi_mx_schedule | 2 +- .../sql/foreign_key_to_reference_table.sql | 37 +- .../sql/mx_foreign_key_to_reference_table.sql | 45 + 6 files changed, 2107 insertions(+), 4 deletions(-) create mode 100644 src/test/regress/expected/foreign_key_to_reference_table_0.out create mode 100644 src/test/regress/expected/mx_foreign_key_to_reference_table.out create mode 100644 src/test/regress/sql/mx_foreign_key_to_reference_table.sql diff --git a/src/test/regress/expected/foreign_key_to_reference_table.out b/src/test/regress/expected/foreign_key_to_reference_table.out index a463e7bff..093359329 100644 --- a/src/test/regress/expected/foreign_key_to_reference_table.out +++ b/src/test/regress/expected/foreign_key_to_reference_table.out @@ -1581,9 +1581,9 @@ INSERT INTO test_table_2 VALUES (1,1), (2,2), (3,3); -- should succeed ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE bigint; ALTER TABLE test_table_1 ALTER COLUMN id SET DATA TYPE bigint; --- should fail since there is a bigint out of integer range > (2^32 - 1) INSERT INTO test_table_1 VALUES (2147483648,4); INSERT INTO test_table_2 VALUES (4,2147483648); +-- should fail since there is a bigint out of integer range > (2^32 - 1) ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE int; ERROR: integer out of range CONTEXT: while executing command on localhost:57637 @@ -1802,6 +1802,59 @@ ERROR: current transaction is aborted, commands ignored until end of transactio ERROR: current transaction is aborted, commands ignored until end of transaction block COMMIT; DROP TABLE test_table_1, test_table_2, test_table_3; +-- NOTE: Postgres does not support foreign keys on partitioned tables currently. +-- However, we can create foreign keys to/from the partitions themselves. +-- The following tests chech if we create the foreign constraints in partitions properly. +CREATE TABLE referenced_table(id int PRIMARY KEY, test_column int); +CREATE TABLE referencing_table(id int, value_1 int) PARTITION BY RANGE (value_1); +CREATE TABLE referencing_table_0 PARTITION OF referencing_table FOR VALUES FROM (0) TO (2); +CREATE TABLE referencing_table_2 PARTITION OF referencing_table FOR VALUES FROM (2) TO (4); +CREATE TABLE referencing_table_4 PARTITION OF referencing_table FOR VALUES FROM (4) TO (6); +-- partitioned tables are not supported as reference tables +select create_reference_table('referencing_table'); +ERROR: distributing partitioned tables in only supported for hash-distributed tables +-- partitioned tables are supported as hash distributed table +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +-- add foreign constraints in between partitions +ALTER TABLE referencing_table_0 ADD CONSTRAINT pkey PRIMARY KEY (id); +ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey FOREIGN KEY (id) REFERENCES referencing_table_0; +-- add foreign constraint from a partition to reference table +ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey_to_ref FOREIGN KEY (value_1) REFERENCES referenced_table; +-- should fail since the data will flow to partitioning_test_4 and it has a foreign constraint to partitioning_test_0 on id column +INSERT INTO referencing_table VALUES (0, 5); +ERROR: insert or update on table "referencing_table_4_7000533" violates foreign key constraint "fkey_7000533" +DETAIL: Key (id)=(0) is not present in table "referencing_table_0_7000517". +CONTEXT: while executing command on localhost:57638 +-- should succeed on partitioning_test_0 +INSERT INTO referencing_table VALUES (0, 1); +SELECT * FROM referencing_table; + id | value_1 +----+--------- + 0 | 1 +(1 row) + +-- should fail since partitioning_test_4 has foreign constraint to referenced_table on value_1 column +INSERT INTO referencing_table VALUES (0, 5); +ERROR: insert or update on table "referencing_table_4_7000533" violates foreign key constraint "fkey_to_ref_7000533" +DETAIL: Key (value_1)=(5) is not present in table "referenced_table_7000505". +CONTEXT: while executing command on localhost:57638 +INSERT INTO referenced_table VALUES(5,5); +-- should succeed since both of the foreign constraints are positive +INSERT INTO referencing_table VALUES (0, 5); +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_to_ref on table referencing_table_4 +DROP TABLE referencing_table; DROP SCHEMA fkey_reference_table CASCADE; NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to type foreign_details diff --git a/src/test/regress/expected/foreign_key_to_reference_table_0.out b/src/test/regress/expected/foreign_key_to_reference_table_0.out new file mode 100644 index 000000000..991c48a1f --- /dev/null +++ b/src/test/regress/expected/foreign_key_to_reference_table_0.out @@ -0,0 +1,1882 @@ +-- +-- FOREIGN_KEY_TO_REFERENCE_TABLE +-- +CREATE SCHEMA fkey_reference_table; +SET search_path TO 'fkey_reference_table'; +SET citus.shard_replication_factor TO 1; +SET citus.shard_count TO 8; +SET citus.next_shard_id TO 7000000; +SET citus.next_placement_id TO 7000000; +CREATE TYPE foreign_details AS (name text, relid text, refd_relid text); +SELECT run_command_on_workers($$CREATE TYPE foreign_details AS (name text, relid text, refd_relid text)$$); + run_command_on_workers +----------------------------------- + (localhost,57637,t,"CREATE TYPE") + (localhost,57638,t,"CREATE TYPE") +(2 rows) + +CREATE VIEW table_fkeys_in_workers AS +SELECT +(json_populate_record(NULL::foreign_details, + json_array_elements_text((run_command_on_workers( $$ + SELECT + COALESCE(json_agg(row_to_json(d)), '[]'::json) + FROM + ( + SELECT + distinct name, + relid::regclass::text, + refd_relid::regclass::text + FROM + table_fkey_cols + ) + d $$ )).RESULT::json )::json )).* ; +CREATE TABLE referenced_table(id int UNIQUE, test_column int); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +-- we still do not support update/delete operations through foreign constraints if the foreign key includes the distribution column +-- All should fail +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET NULL; +ERROR: cannot create foreign key constraint +DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET NULL); +SELECT create_distributed_table('referencing_table', 'ref_id'); +ERROR: cannot create foreign key constraint +DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT; +ERROR: cannot create foreign key constraint +DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT); +SELECT create_distributed_table('referencing_table', 'ref_id'); +ERROR: cannot create foreign key constraint +DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET NULL; +ERROR: cannot create foreign key constraint +DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. +DROP TABLE referencing_table; +BEGIN; + CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET NULL); + SELECT create_distributed_table('referencing_table', 'ref_id'); +ERROR: cannot create foreign key constraint +DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. +ROLLBACK; +-- try with multiple columns including the distribution column +DROP TABLE referenced_table; +CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id, test_column)); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE SET DEFAULT; +ERROR: cannot create foreign key constraint +DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE SET DEFAULT); +SELECT create_distributed_table('referencing_table', 'ref_id'); +ERROR: cannot create foreign key constraint +DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE CASCADE; +ERROR: cannot create foreign key constraint +DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. +DROP TABLE referencing_table; +BEGIN; + CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE CASCADE); + SELECT create_distributed_table('referencing_table', 'ref_id'); +ERROR: cannot create foreign key constraint +DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. +ROLLBACK; +-- all of the above is supported if the foreign key does not include distribution column +DROP TABLE referenced_table; +CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id)); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET NULL; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +------------------+------------------------------------------------+----------------------------------------------- + fkey_ref_7000043 | fkey_reference_table.referencing_table_7000043 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000044 | fkey_reference_table.referencing_table_7000044 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000045 | fkey_reference_table.referencing_table_7000045 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000046 | fkey_reference_table.referencing_table_7000046 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000047 | fkey_reference_table.referencing_table_7000047 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000048 | fkey_reference_table.referencing_table_7000048 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000049 | fkey_reference_table.referencing_table_7000049 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000050 | fkey_reference_table.referencing_table_7000050 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET NULL); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +-----------------------------------+------------------------------------------------+----------------------------------------------- + referencing_table_id_fkey_7000051 | fkey_reference_table.referencing_table_7000051 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000052 | fkey_reference_table.referencing_table_7000052 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000053 | fkey_reference_table.referencing_table_7000053 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000054 | fkey_reference_table.referencing_table_7000054 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000055 | fkey_reference_table.referencing_table_7000055 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000056 | fkey_reference_table.referencing_table_7000056 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000057 | fkey_reference_table.referencing_table_7000057 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000058 | fkey_reference_table.referencing_table_7000058 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +------------------+------------------------------------------------+----------------------------------------------- + fkey_ref_7000059 | fkey_reference_table.referencing_table_7000059 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000060 | fkey_reference_table.referencing_table_7000060 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000061 | fkey_reference_table.referencing_table_7000061 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000062 | fkey_reference_table.referencing_table_7000062 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000063 | fkey_reference_table.referencing_table_7000063 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000064 | fkey_reference_table.referencing_table_7000064 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000065 | fkey_reference_table.referencing_table_7000065 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000066 | fkey_reference_table.referencing_table_7000066 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +BEGIN; + CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT); + SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +COMMIT; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +-----------------------------------+------------------------------------------------+----------------------------------------------- + referencing_table_id_fkey_7000067 | fkey_reference_table.referencing_table_7000067 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000068 | fkey_reference_table.referencing_table_7000068 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000069 | fkey_reference_table.referencing_table_7000069 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000070 | fkey_reference_table.referencing_table_7000070 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000071 | fkey_reference_table.referencing_table_7000071 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000072 | fkey_reference_table.referencing_table_7000072 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000073 | fkey_reference_table.referencing_table_7000073 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000074 | fkey_reference_table.referencing_table_7000074 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE SET NULL; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +------------------+------------------------------------------------+----------------------------------------------- + fkey_ref_7000075 | fkey_reference_table.referencing_table_7000075 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000076 | fkey_reference_table.referencing_table_7000076 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000077 | fkey_reference_table.referencing_table_7000077 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000078 | fkey_reference_table.referencing_table_7000078 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000079 | fkey_reference_table.referencing_table_7000079 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000080 | fkey_reference_table.referencing_table_7000080 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000081 | fkey_reference_table.referencing_table_7000081 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000082 | fkey_reference_table.referencing_table_7000082 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE SET DEFAULT; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +------------------+------------------------------------------------+----------------------------------------------- + fkey_ref_7000083 | fkey_reference_table.referencing_table_7000083 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000084 | fkey_reference_table.referencing_table_7000084 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000085 | fkey_reference_table.referencing_table_7000085 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000086 | fkey_reference_table.referencing_table_7000086 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000087 | fkey_reference_table.referencing_table_7000087 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000088 | fkey_reference_table.referencing_table_7000088 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000089 | fkey_reference_table.referencing_table_7000089 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000090 | fkey_reference_table.referencing_table_7000090 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE CASCADE; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +------------------+------------------------------------------------+----------------------------------------------- + fkey_ref_7000091 | fkey_reference_table.referencing_table_7000091 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000092 | fkey_reference_table.referencing_table_7000092 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000093 | fkey_reference_table.referencing_table_7000093 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000094 | fkey_reference_table.referencing_table_7000094 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000095 | fkey_reference_table.referencing_table_7000095 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000096 | fkey_reference_table.referencing_table_7000096 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000097 | fkey_reference_table.referencing_table_7000097 | fkey_reference_table.referenced_table_7000042 + fkey_ref_7000098 | fkey_reference_table.referencing_table_7000098 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +-- foreign keys are only supported when the replication factor = 1 +SET citus.shard_replication_factor TO 2; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(id); +ERROR: cannot create foreign key constraint +DETAIL: Citus Community Edition currently supports foreign key constraints only for "citus.shard_replication_factor = 1". +HINT: Please change "citus.shard_replication_factor to 1". To learn more about using foreign keys with other replication factors, please contact us at https://citusdata.com/about/contact_us. +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +------+-------+------------ +(0 rows) + +DROP TABLE referencing_table; +SET citus.shard_replication_factor TO 1; +-- simple create_distributed_table should work in/out transactions on tables with foreign key to reference tables +CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(id)); +SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +-----------------------------------+------------------------------------------------+----------------------------------------------- + referencing_table_id_fkey_7000107 | fkey_reference_table.referencing_table_7000107 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000108 | fkey_reference_table.referencing_table_7000108 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000109 | fkey_reference_table.referencing_table_7000109 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000110 | fkey_reference_table.referencing_table_7000110 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000111 | fkey_reference_table.referencing_table_7000111 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000112 | fkey_reference_table.referencing_table_7000112 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000113 | fkey_reference_table.referencing_table_7000113 | fkey_reference_table.referenced_table_7000042 + referencing_table_id_fkey_7000114 | fkey_reference_table.referencing_table_7000114 | fkey_reference_table.referenced_table_7000042 +(8 rows) + +DROP TABLE referencing_table; +DROP TABLE referenced_table; +BEGIN; + CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id)); + SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + + CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(id)); + SELECT create_distributed_table('referencing_table', 'ref_id'); + create_distributed_table +-------------------------- + +(1 row) + +COMMIT; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +-----------------------------------+------------------------------------------------+----------------------------------------------- + referencing_table_id_fkey_7000116 | fkey_reference_table.referencing_table_7000116 | fkey_reference_table.referenced_table_7000115 + referencing_table_id_fkey_7000117 | fkey_reference_table.referencing_table_7000117 | fkey_reference_table.referenced_table_7000115 + referencing_table_id_fkey_7000118 | fkey_reference_table.referencing_table_7000118 | fkey_reference_table.referenced_table_7000115 + referencing_table_id_fkey_7000119 | fkey_reference_table.referencing_table_7000119 | fkey_reference_table.referenced_table_7000115 + referencing_table_id_fkey_7000120 | fkey_reference_table.referencing_table_7000120 | fkey_reference_table.referenced_table_7000115 + referencing_table_id_fkey_7000121 | fkey_reference_table.referencing_table_7000121 | fkey_reference_table.referenced_table_7000115 + referencing_table_id_fkey_7000122 | fkey_reference_table.referencing_table_7000122 | fkey_reference_table.referenced_table_7000115 + referencing_table_id_fkey_7000123 | fkey_reference_table.referencing_table_7000123 | fkey_reference_table.referenced_table_7000115 +(8 rows) + +DROP TABLE referencing_table; +-- foreign keys are supported either in between distributed tables including the +-- distribution column or from distributed tables to reference tables. +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id', 'append'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(id); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +SELECT * FROM table_fkeys_in_workers WHERE name LIKE 'fkey_ref%' ORDER BY 1,2,3; + name | relid | refd_relid +------+-------+------------ +(0 rows) + +DROP TABLE referencing_table; +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_distributed_table('referencing_table', 'ref_id', 'range'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(id); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +SELECT * FROM table_fkeys_in_workers WHERE name LIKE 'fkey_ref%' ORDER BY 1,2,3; + name | relid | refd_relid +------+-------+------------ +(0 rows) + +DROP TABLE referencing_table; +DROP TABLE referenced_table; +-- test foreign constraint with correct conditions +CREATE TABLE referenced_table(id int UNIQUE, test_column int, PRIMARY KEY(id, test_column)); +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(id); +-- test inserts +-- test insert to referencing table while there is NO corresponding value in referenced table +INSERT INTO referencing_table VALUES(1, 1); +ERROR: insert or update on table "referencing_table_7000125" violates foreign key constraint "fkey_ref_7000125" +DETAIL: Key (ref_id)=(1) is not present in table "referenced_table_7000124". +CONTEXT: while executing command on localhost:57637 +-- test insert to referencing while there is corresponding value in referenced table +INSERT INTO referenced_table SELECT x, x from generate_series(1,1000) as f(x); +INSERT INTO referencing_table SELECT x, x from generate_series(1,500) as f(x); +-- test deletes +-- test delete from referenced table while there is corresponding value in referencing table +DELETE FROM referenced_table WHERE id > 3; +ERROR: update or delete on table "referenced_table_7000124" violates foreign key constraint "fkey_ref_7000127" on table "referencing_table_7000127" +DETAIL: Key (id)=(4) is still referenced from table "referencing_table_7000127". +CONTEXT: while executing command on localhost:57637 +-- test delete from referenced table while there is NO corresponding value in referencing table +DELETE FROM referenced_table WHERE id = 501; +-- test cascading truncate +TRUNCATE referenced_table CASCADE; +NOTICE: truncate cascades to table "referencing_table" +SELECT count(*) FROM referencing_table; + count +------- + 0 +(1 row) + +-- drop table for next tests +DROP TABLE referencing_table; +DROP TABLE referenced_table; +-- self referencing foreign key on reference tables are not allowed +-- TODO try create_reference_table with already created foreign key. +CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id)); +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_reference_table('referencing_table'); + create_reference_table +------------------------ + +(1 row) + +-- self referencing foreign key +ALTER TABLE referenced_table ADD CONSTRAINT fkey_ref FOREIGN KEY (test_column) REFERENCES referenced_table(id); +ERROR: cannot create foreign key constraint because reference tables are not supported as the referencing table of a foreign constraint +DETAIL: Reference tables are only supported as the referenced table of a foreign key when the referencing table is a hash distributed table +-- foreign Keys from reference table to reference table are not allowed +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE CASCADE; +ERROR: cannot create foreign key constraint because reference tables are not supported as the referencing table of a foreign constraint +DETAIL: Reference tables are only supported as the referenced table of a foreign key when the referencing table is a hash distributed table +DROP TABLE referenced_table; +DROP TABLE referencing_table; +-- cascades on delete with different schemas +CREATE SCHEMA referenced_schema; +CREATE SCHEMA referencing_schema; +CREATE TABLE referenced_schema.referenced_table(id int UNIQUE, test_column int, PRIMARY KEY(id, test_column)); +CREATE TABLE referencing_schema.referencing_table(id int, ref_id int); +SELECT create_reference_table('referenced_schema.referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_schema.referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_schema.referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_schema.referenced_table(id) ON DELETE CASCADE; +INSERT INTO referenced_schema.referenced_table SELECT x, x from generate_series(1,1000) as f(x); +INSERT INTO referencing_schema.referencing_table SELECT x, x from generate_series(1,1000) as f(x); +DELETE FROM referenced_schema.referenced_table WHERE id > 800; +SELECT count(*) FROM referencing_schema.referencing_table; + count +------- + 800 +(1 row) + +DROP SCHEMA referenced_schema CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table referenced_schema.referenced_table +drop cascades to constraint fkey_ref on table referencing_schema.referencing_table +DROP SCHEMA referencing_schema CASCADE; +NOTICE: drop cascades to table referencing_schema.referencing_table +-- on delete set update cascades properly +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int, ref_id int DEFAULT 1); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE SET DEFAULT; +INSERT INTO referenced_table SELECT x, x FROM generate_series(1,1000) AS f(x); +INSERT INTO referencing_table SELECT x, x FROM generate_series(1,1000) AS f(x); +DELETE FROM referenced_table WHERE test_column > 800; +SELECT count(*) FROM referencing_table WHERE ref_id = 1; + count +------- + 201 +(1 row) + +DROP TABLE referencing_table; +DROP TABLE referenced_table; +-- foreign key as composite key +CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int); +SELECT run_command_on_workers($$CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int)$$) ORDER BY 1; + run_command_on_workers +----------------------------------- + (localhost,57637,t,"CREATE TYPE") + (localhost,57638,t,"CREATE TYPE") +(2 rows) + +CREATE TABLE referenced_table(test_column composite, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int, referencing_composite composite); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (referencing_composite) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +INSERT INTO referenced_table SELECT (x+1, x+1)::composite FROM generate_series(1,1000) AS f(x); +INSERT INTO referencing_table SELECT x, (x+1, x+1)::composite FROM generate_series(1,1000) AS f(x); +DELETE FROM referenced_table WHERE (test_column).key1 > 900; +SELECT count(*) FROM referencing_table; + count +------- + 899 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +-- In the following test, we'll use a SERIAL column as the referenced column +-- in the foreign constraint. We'll first show that and insert on non-serial +-- column successfully inserts into the serial and referenced column. +-- Accordingly, the inserts into the referencing table which references to the +-- serial column will be successful. +CREATE TABLE referenced_table(test_column SERIAL PRIMARY KEY, test_column2 int); +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +INSERT INTO referenced_table(test_column2) SELECT x FROM generate_series(1,1000) AS f(x); +INSERT INTO referencing_table SELECT x, x FROM generate_series(1,1000) AS f(x); +DELETE FROM referenced_table WHERE test_column2 > 10; +SELECT count(*) FROM referencing_table; + count +------- + 10 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +-- In the following test, we'll use a SERIAL column as the referencing column +-- in the foreign constraint. We'll first show that the values that exist +-- in the referenced tables are successfully generated by the serial column +-- and inserted to the distributed table. However, if the values that are generated +-- by serial column do not exist on the referenced table, the query fails. +CREATE TABLE referenced_table(test_column int PRIMARY KEY, test_column2 int); +CREATE TABLE referencing_table(id int, ref_id SERIAL); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +INSERT INTO referenced_table SELECT x,x FROM generate_series(1,1000) AS f(x); +-- Success for existing inserts +INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,1000) AS f(x); +-- Fails for non existing value inserts (serial is already incremented) +INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,10) AS f(x); +ERROR: insert or update on table "referencing_table_7000172" violates foreign key constraint "fkey_ref_7000172" +DETAIL: Key (ref_id)=(1001) is not present in table "referenced_table_7000171". +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +-- In the following test, we'll use a SERIAL column as the referencing column +-- and referenced columns in a foreign constraint. We'll first show that the +-- the inserts into referenced column will successfully generate and insert +-- data into serial column. Then, we will be successfully insert the same amount +-- of data into referencing table. However, if the values that are generated +-- by serial column do not exist on the referenced table, the query fails. +CREATE TABLE referenced_table(test_column SERIAL PRIMARY KEY, test_column2 int); +CREATE TABLE referencing_table(id int, ref_id SERIAL); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +INSERT INTO referenced_table(test_column2) SELECT x FROM generate_series(1,1000) AS f(x); +-- Success for existing values +INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,1000) AS f(x); +-- Fails for non existing value inserts (serial is already incremented) +INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,10) AS f(x); +ERROR: insert or update on table "referencing_table_7000181" violates foreign key constraint "fkey_ref_7000181" +DETAIL: Key (ref_id)=(1001) is not present in table "referenced_table_7000180". +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +-- In the following test, we use a volatile function in the referencing +-- column in a foreign constraint. We show that if the data exists in the +-- referenced table, we can successfully use volatile functions with +-- foreign constraints. +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE SET DEFAULT; +INSERT INTO referenced_table SELECT x, x FROM generate_series(0,1000) AS f(x); +INSERT INTO referencing_table SELECT x,(random()*1000)::int FROM generate_series(0,1000) AS f(x); +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +-- In the following test, we show that Citus currently does not support +-- VALIDATE command. +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE SET DEFAULT NOT VALID; +-- Even if the foreign constraint is added with "NOT VALID", +-- we make sure that it is still applied to the upcoming inserts. +INSERT INTO referenced_table SELECT x, x FROM generate_series(0,1000) AS f(x); +INSERT INTO referencing_table SELECT x, x FROM generate_series(0,1000) AS f(x); +-- we expect this to fail because of the foreign constraint. +INSERT INTO referencing_table SELECT x, x FROM generate_series(1000,1001) AS f(x); +ERROR: insert or update on table "referencing_table_7000204" violates foreign key constraint "fkey_ref_7000204" +DETAIL: Key (ref_id)=(1001) is not present in table "referenced_table_7000198". +-- currently not supported +ALTER TABLE referencing_table VALIDATE CONSTRAINT fkey_ref; +ERROR: alter table command is currently unsupported +DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT, SET (), RESET (), ATTACH|DETACH PARTITION and TYPE subcommands are supported. +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +-- In the following tests, we create a foreign constraint with +-- ON UPDATE CASCADE and see if it works properly with cascading upsert +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON UPDATE CASCADE; +INSERT INTO referenced_table SELECT x, x FROM generate_series(0,1000) AS f(x); +INSERT INTO referencing_table SELECT x, x FROM generate_series(0,1000) AS f(x); +INSERT INTO referenced_table VALUES (1,2), (2,3), (3,4), (4,5) +ON CONFLICT (test_column) +DO UPDATE + SET test_column = -1 * EXCLUDED.test_column; +SELECT * FROM referencing_table WHERE ref_id < 0 ORDER BY 1; + id | ref_id +----+-------- + 1 | -1 + 2 | -2 + 3 | -3 + 4 | -4 +(4 rows) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +-- create_distributed_table should fail for tables with data if fkey exists to reference table +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1, FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON UPDATE CASCADE); +INSERT INTO referenced_table VALUES (1,1), (2,2), (3,3); +INSERT INTO referencing_table VALUES (1,1), (2,2), (3,3); +SELECT create_reference_table('referenced_table'); +NOTICE: Copying data from local table... + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); +ERROR: cannot distribute "referencing_table" in sequential mode because it is not empty +HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. +BEGIN; + SELECT create_distributed_table('referencing_table', 'id'); +ERROR: cannot distribute "referencing_table" in sequential mode because it is not empty +HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. +COMMIT; +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint referencing_table_ref_id_fkey on table referencing_table +DROP TABLE referencing_table CASCADE; +-- Chained references +-- In the following test, we create foreign keys from one column in a distributed +-- table to two reference tables. We expect to see that even if a data exist in +-- one reference table, it is not going to be inserted in to referencing table +-- because of lack of the key in the other table. Data can only be inserted into +-- referencing table if it exists in both referenced tables. +-- Additionally, delete or update in one referenced table should cascade properly. +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_reference_table('referenced_table2'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +-----------------------+------------------------------------------------+------------------------------------------------ + fkey_ref_7000219 | fkey_reference_table.referencing_table_7000219 | fkey_reference_table.referenced_table_7000217 + fkey_ref_7000220 | fkey_reference_table.referencing_table_7000220 | fkey_reference_table.referenced_table_7000217 + fkey_ref_7000221 | fkey_reference_table.referencing_table_7000221 | fkey_reference_table.referenced_table_7000217 + fkey_ref_7000222 | fkey_reference_table.referencing_table_7000222 | fkey_reference_table.referenced_table_7000217 + fkey_ref_7000223 | fkey_reference_table.referencing_table_7000223 | fkey_reference_table.referenced_table_7000217 + fkey_ref_7000224 | fkey_reference_table.referencing_table_7000224 | fkey_reference_table.referenced_table_7000217 + fkey_ref_7000225 | fkey_reference_table.referencing_table_7000225 | fkey_reference_table.referenced_table_7000217 + fkey_ref_7000226 | fkey_reference_table.referencing_table_7000226 | fkey_reference_table.referenced_table_7000217 + foreign_key_2_7000219 | fkey_reference_table.referencing_table_7000219 | fkey_reference_table.referenced_table2_7000218 + foreign_key_2_7000220 | fkey_reference_table.referencing_table_7000220 | fkey_reference_table.referenced_table2_7000218 + foreign_key_2_7000221 | fkey_reference_table.referencing_table_7000221 | fkey_reference_table.referenced_table2_7000218 + foreign_key_2_7000222 | fkey_reference_table.referencing_table_7000222 | fkey_reference_table.referenced_table2_7000218 + foreign_key_2_7000223 | fkey_reference_table.referencing_table_7000223 | fkey_reference_table.referenced_table2_7000218 + foreign_key_2_7000224 | fkey_reference_table.referencing_table_7000224 | fkey_reference_table.referenced_table2_7000218 + foreign_key_2_7000225 | fkey_reference_table.referencing_table_7000225 | fkey_reference_table.referenced_table2_7000218 + foreign_key_2_7000226 | fkey_reference_table.referencing_table_7000226 | fkey_reference_table.referenced_table2_7000218 +(16 rows) + +INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); +INSERT INTO referenced_table2 SELECT x, x+1 FROM generate_series(500,1500) AS f(x); +-- should fail +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,1500) AS f(x); +ERROR: insert or update on table "referencing_table_7000220" violates foreign key constraint "foreign_key_2_7000220" +DETAIL: Key (id)=(5) is not present in table "referenced_table2_7000218". +-- should fail +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); +ERROR: insert or update on table "referencing_table_7000220" violates foreign key constraint "foreign_key_2_7000220" +DETAIL: Key (id)=(5) is not present in table "referenced_table2_7000218". +-- should fail +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(1000,1400) AS f(x); +ERROR: insert or update on table "referencing_table_7000220" violates foreign key constraint "fkey_ref_7000220" +DETAIL: Key (id)=(1005) is not present in table "referenced_table_7000217". +-- should success +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(600,900) AS f(x); +SELECT count(*) FROM referencing_table; + count +------- + 301 +(1 row) + +DELETE FROM referenced_table WHERE test_column < 700; +SELECT count(*) FROM referencing_table; + count +------- + 201 +(1 row) + +DELETE FROM referenced_table2 WHERE test_column2 > 800; +SELECT count(*) FROM referencing_table; + count +------- + 101 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referenced_table2 CASCADE; +NOTICE: drop cascades to constraint foreign_key_2 on table referencing_table +DROP TABLE referencing_table CASCADE; +-- check if the above fkeys are created with create_distributed_table +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); +CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE, FOREIGN KEY (id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_reference_table('referenced_table2'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 16 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint referencing_table_id_fkey on table referencing_table +DROP TABLE referenced_table2 CASCADE; +NOTICE: drop cascades to constraint referencing_table_id_fkey1 on table referencing_table +DROP TABLE referencing_table CASCADE; +-- In the following test, we create foreign keys from two columns in a distributed +-- table to two reference tables separately. We expect to see that even if a data +-- exist in one reference table for one column, it is not going to be inserted in +-- to referencing table because the other constraint doesn't hold. Data can only +-- be inserted into referencing table if both columns exist in respective columns +-- in referenced tables. +-- Additionally, delete or update in one referenced table should cascade properly. +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); +CREATE TABLE referencing_table(id int, ref_id int); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_reference_table('referenced_table2'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +BEGIN; + ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; + ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (ref_id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; +COMMIT; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +-----------------------+------------------------------------------------+------------------------------------------------ + fkey_ref_7000239 | fkey_reference_table.referencing_table_7000239 | fkey_reference_table.referenced_table_7000237 + fkey_ref_7000240 | fkey_reference_table.referencing_table_7000240 | fkey_reference_table.referenced_table_7000237 + fkey_ref_7000241 | fkey_reference_table.referencing_table_7000241 | fkey_reference_table.referenced_table_7000237 + fkey_ref_7000242 | fkey_reference_table.referencing_table_7000242 | fkey_reference_table.referenced_table_7000237 + fkey_ref_7000243 | fkey_reference_table.referencing_table_7000243 | fkey_reference_table.referenced_table_7000237 + fkey_ref_7000244 | fkey_reference_table.referencing_table_7000244 | fkey_reference_table.referenced_table_7000237 + fkey_ref_7000245 | fkey_reference_table.referencing_table_7000245 | fkey_reference_table.referenced_table_7000237 + fkey_ref_7000246 | fkey_reference_table.referencing_table_7000246 | fkey_reference_table.referenced_table_7000237 + foreign_key_2_7000239 | fkey_reference_table.referencing_table_7000239 | fkey_reference_table.referenced_table2_7000238 + foreign_key_2_7000240 | fkey_reference_table.referencing_table_7000240 | fkey_reference_table.referenced_table2_7000238 + foreign_key_2_7000241 | fkey_reference_table.referencing_table_7000241 | fkey_reference_table.referenced_table2_7000238 + foreign_key_2_7000242 | fkey_reference_table.referencing_table_7000242 | fkey_reference_table.referenced_table2_7000238 + foreign_key_2_7000243 | fkey_reference_table.referencing_table_7000243 | fkey_reference_table.referenced_table2_7000238 + foreign_key_2_7000244 | fkey_reference_table.referencing_table_7000244 | fkey_reference_table.referenced_table2_7000238 + foreign_key_2_7000245 | fkey_reference_table.referencing_table_7000245 | fkey_reference_table.referenced_table2_7000238 + foreign_key_2_7000246 | fkey_reference_table.referencing_table_7000246 | fkey_reference_table.referenced_table2_7000238 +(16 rows) + +INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); +INSERT INTO referenced_table2 SELECT x, x+1 FROM generate_series(500,1500) AS f(x); +-- should fail +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,1500) AS f(x); +ERROR: insert or update on table "referencing_table_7000245" violates foreign key constraint "foreign_key_2_7000245" +DETAIL: Key (ref_id)=(3) is not present in table "referenced_table2_7000238". +-- should fail +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); +ERROR: insert or update on table "referencing_table_7000245" violates foreign key constraint "foreign_key_2_7000245" +DETAIL: Key (ref_id)=(3) is not present in table "referenced_table2_7000238". +-- should fail +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(1000,1400) AS f(x); +ERROR: insert or update on table "referencing_table_7000245" violates foreign key constraint "fkey_ref_7000245" +DETAIL: Key (id)=(1002) is not present in table "referenced_table_7000237". +-- should success +INSERT INTO referencing_table SELECT x, x+501 FROM generate_series(0,1000) AS f(x); +SELECT count(*) FROM referencing_table; + count +------- + 1001 +(1 row) + +DELETE FROM referenced_table WHERE test_column < 700; +SELECT count(*) FROM referencing_table; + count +------- + 301 +(1 row) + +DELETE FROM referenced_table2 WHERE test_column2 > 800; +SELECT count(*) FROM referencing_table; + count +------- + 0 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referenced_table2 CASCADE; +NOTICE: drop cascades to constraint foreign_key_2 on table referencing_table +DROP TABLE referencing_table CASCADE; +-- check if the above fkeys are created when create_distributed_table is used for 1 foreign key and alter table for the other +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); +CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE); +BEGIN; + SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_reference_table('referenced_table2'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (ref_id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; +COMMIT; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 16 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint referencing_table_id_fkey on table referencing_table +DROP TABLE referenced_table2 CASCADE; +NOTICE: drop cascades to constraint foreign_key_2 on table referencing_table +DROP TABLE referencing_table CASCADE; +-- two distributed tables are referencing to one reference table and +-- in the same time the distributed table 2 is referencing to +-- distributed table 1. Thus, we have a triangular +-- distributed table 1 has a foreign key from the distribution column to reference table +-- distributed table 2 has a foreign key from a non-distribution column to reference table +-- distributed table 2 has a foreign key to distributed table 1 on the distribution column +-- We show that inserts into distributed table 2 will fail if the data does not exist in distributed table 1 +-- Delete from reference table cascades to both of the distributed tables properly +CREATE TABLE referenced_table(test_column int, test_column2 int UNIQUE, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int PRIMARY KEY, ref_id int); +CREATE TABLE referencing_table2(id int, ref_id int); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +SELECT create_distributed_table('referencing_table2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +BEGIN; +SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +ALTER TABLE referencing_table2 ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column2) ON DELETE CASCADE; +ALTER TABLE referencing_table2 ADD CONSTRAINT fkey_ref_to_dist FOREIGN KEY (id) REFERENCES referencing_table(id) ON DELETE CASCADE; +COMMIT; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; + name | relid | refd_relid +--------------------------+-------------------------------------------------+------------------------------------------------ + fkey_ref_7000258 | fkey_reference_table.referencing_table_7000258 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000259 | fkey_reference_table.referencing_table_7000259 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000260 | fkey_reference_table.referencing_table_7000260 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000261 | fkey_reference_table.referencing_table_7000261 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000262 | fkey_reference_table.referencing_table_7000262 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000263 | fkey_reference_table.referencing_table_7000263 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000264 | fkey_reference_table.referencing_table_7000264 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000265 | fkey_reference_table.referencing_table_7000265 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000266 | fkey_reference_table.referencing_table2_7000266 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000267 | fkey_reference_table.referencing_table2_7000267 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000268 | fkey_reference_table.referencing_table2_7000268 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000269 | fkey_reference_table.referencing_table2_7000269 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000270 | fkey_reference_table.referencing_table2_7000270 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000271 | fkey_reference_table.referencing_table2_7000271 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000272 | fkey_reference_table.referencing_table2_7000272 | fkey_reference_table.referenced_table_7000257 + fkey_ref_7000273 | fkey_reference_table.referencing_table2_7000273 | fkey_reference_table.referenced_table_7000257 + fkey_ref_to_dist_7000266 | fkey_reference_table.referencing_table2_7000266 | fkey_reference_table.referencing_table_7000258 + fkey_ref_to_dist_7000267 | fkey_reference_table.referencing_table2_7000267 | fkey_reference_table.referencing_table_7000259 + fkey_ref_to_dist_7000268 | fkey_reference_table.referencing_table2_7000268 | fkey_reference_table.referencing_table_7000260 + fkey_ref_to_dist_7000269 | fkey_reference_table.referencing_table2_7000269 | fkey_reference_table.referencing_table_7000261 + fkey_ref_to_dist_7000270 | fkey_reference_table.referencing_table2_7000270 | fkey_reference_table.referencing_table_7000262 + fkey_ref_to_dist_7000271 | fkey_reference_table.referencing_table2_7000271 | fkey_reference_table.referencing_table_7000263 + fkey_ref_to_dist_7000272 | fkey_reference_table.referencing_table2_7000272 | fkey_reference_table.referencing_table_7000264 + fkey_ref_to_dist_7000273 | fkey_reference_table.referencing_table2_7000273 | fkey_reference_table.referencing_table_7000265 +(24 rows) + +INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); +-- should fail +INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(0,100) AS f(x); +ERROR: insert or update on table "referencing_table2_7000272" violates foreign key constraint "fkey_ref_to_dist_7000272" +DETAIL: Key (id)=(2) is not present in table "referencing_table_7000264". +-- should success +INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); +-- should fail +INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(200,500) AS f(x); +ERROR: insert or update on table "referencing_table2_7000272" violates foreign key constraint "fkey_ref_to_dist_7000272" +DETAIL: Key (id)=(404) is not present in table "referencing_table_7000264". +-- should success +INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(0,300) AS f(x); +DELETE FROM referenced_table WHERE test_column < 200; +SELECT count(*) FROM referencing_table; + count +------- + 201 +(1 row) + +SELECT count(*) FROM referencing_table2; + count +------- + 101 +(1 row) + +DELETE FROM referencing_table WHERE id > 200; +SELECT count(*) FROM referencing_table2; + count +------- + 1 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to constraint fkey_ref on table referencing_table2 +drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref_to_dist on table referencing_table2 +DROP TABLE referencing_table2 CASCADE; +-- Check if the above fkeys are created with create_distributed_table +CREATE TABLE referenced_table(test_column int, test_column2 int UNIQUE, PRIMARY KEY(test_column)); +CREATE TABLE referencing_table(id int PRIMARY KEY, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE); +CREATE TABLE referencing_table2(id int, ref_id int, FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column2) ON DELETE CASCADE, FOREIGN KEY (id) REFERENCES referencing_table(id) ON DELETE CASCADE); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +BEGIN; + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; + SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + SELECT create_distributed_table('referencing_table2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +COMMIT; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 24 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to constraint referencing_table2_ref_id_fkey on table referencing_table2 +drop cascades to constraint referencing_table_id_fkey on table referencing_table +DROP TABLE referencing_table CASCADE; +NOTICE: drop cascades to constraint referencing_table2_id_fkey on table referencing_table2 +DROP TABLE referencing_table2 CASCADE; +-- In this test we have a chained relationship in form of +-- distributed table (referencing_referencing_table) has a foreign key with two columns +-- to another distributed table (referencing_table) +-- referencing_table has another foreign key with 2 columns to referenced_table. +-- We will show that a cascading delete on referenced_table reaches to referencing_referencing_table. +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column, test_column2)); +CREATE TABLE referencing_table(id int, ref_id int, ref_id2 int, PRIMARY KEY(id, ref_id)); +CREATE TABLE referencing_referencing_table(id int, ref_id int, FOREIGN KEY (id, ref_id) REFERENCES referencing_table(id, ref_id) ON DELETE CASCADE); +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +SELECT create_distributed_table('referencing_referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id, ref_id2) REFERENCES referenced_table(test_column, test_column2) ON DELETE CASCADE; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.referencing%' ORDER BY 1,2,3; + name | relid | refd_relid +-----------------------------------------------+------------------------------------------------------------+------------------------------------------------ + fkey_ref_7000292 | fkey_reference_table.referencing_table_7000292 | fkey_reference_table.referenced_table_7000291 + fkey_ref_7000293 | fkey_reference_table.referencing_table_7000293 | fkey_reference_table.referenced_table_7000291 + fkey_ref_7000294 | fkey_reference_table.referencing_table_7000294 | fkey_reference_table.referenced_table_7000291 + fkey_ref_7000295 | fkey_reference_table.referencing_table_7000295 | fkey_reference_table.referenced_table_7000291 + fkey_ref_7000296 | fkey_reference_table.referencing_table_7000296 | fkey_reference_table.referenced_table_7000291 + fkey_ref_7000297 | fkey_reference_table.referencing_table_7000297 | fkey_reference_table.referenced_table_7000291 + fkey_ref_7000298 | fkey_reference_table.referencing_table_7000298 | fkey_reference_table.referenced_table_7000291 + fkey_ref_7000299 | fkey_reference_table.referencing_table_7000299 | fkey_reference_table.referenced_table_7000291 + referencing_referencing_table_id_fkey_7000300 | fkey_reference_table.referencing_referencing_table_7000300 | fkey_reference_table.referencing_table_7000292 + referencing_referencing_table_id_fkey_7000301 | fkey_reference_table.referencing_referencing_table_7000301 | fkey_reference_table.referencing_table_7000293 + referencing_referencing_table_id_fkey_7000302 | fkey_reference_table.referencing_referencing_table_7000302 | fkey_reference_table.referencing_table_7000294 + referencing_referencing_table_id_fkey_7000303 | fkey_reference_table.referencing_referencing_table_7000303 | fkey_reference_table.referencing_table_7000295 + referencing_referencing_table_id_fkey_7000304 | fkey_reference_table.referencing_referencing_table_7000304 | fkey_reference_table.referencing_table_7000296 + referencing_referencing_table_id_fkey_7000305 | fkey_reference_table.referencing_referencing_table_7000305 | fkey_reference_table.referencing_table_7000297 + referencing_referencing_table_id_fkey_7000306 | fkey_reference_table.referencing_referencing_table_7000306 | fkey_reference_table.referencing_table_7000298 + referencing_referencing_table_id_fkey_7000307 | fkey_reference_table.referencing_referencing_table_7000307 | fkey_reference_table.referencing_table_7000299 +(16 rows) + +INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(1,1000) AS f(x); +INSERT INTO referencing_table SELECT x, x+1, x+2 FROM generate_series(1,999) AS f(x); +INSERT INTO referencing_referencing_table SELECT x, x+1 FROM generate_series(1,999) AS f(x); +DELETE FROM referenced_table WHERE test_column > 800; +SELECT max(ref_id) FROM referencing_referencing_table; + max +----- + 800 +(1 row) + +DROP TABLE referenced_table CASCADE; +NOTICE: drop cascades to constraint fkey_ref on table referencing_table +DROP TABLE referencing_table CASCADE; +NOTICE: drop cascades to constraint referencing_referencing_table_id_fkey on table referencing_referencing_table +DROP TABLE referencing_referencing_table; +-- test if create_distributed_table works in transactions with some edge cases +-- the following checks if create_distributed_table works on foreign keys when +-- one of them is a self-referencing table of multiple distributed tables +BEGIN; + CREATE TABLE test_table_1(id int PRIMARY KEY); + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES test_table_1(id)); + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + CREATE TABLE test_table_3(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id), FOREIGN KEY(id) REFERENCES test_table_2(id)); + SELECT create_distributed_table('test_table_3', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + DROP TABLE test_table_1 CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to constraint test_table_2_id_fkey on table test_table_2 +drop cascades to constraint test_table_3_value_1_fkey on table test_table_3 +ROLLBACK; +-- create_reference_table, create_distributed_table and ALTER TABLE in the same transaction +-- FIXME: fails for now +BEGIN; + CREATE TABLE test_table_1(id int PRIMARY KEY); + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + ALTER TABLE test_table_2 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_1(id); +ERROR: cannot perform query with placements that were modified over multiple connections + DROP TABLE test_table_1, test_table_2; +ERROR: current transaction is aborted, commands ignored until end of transaction block +COMMIT; +-- the order of create_reference_table and create_distributed_table is changed +-- FIXME: fails for now +BEGIN; + CREATE TABLE test_table_1(id int PRIMARY KEY, value_1 int); + SELECT create_distributed_table('test_table_1', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + CREATE TABLE test_table_2(id int PRIMARY KEY); + SELECT create_reference_table('test_table_2'); + create_reference_table +------------------------ + +(1 row) + + ALTER TABLE test_table_1 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_2(id); +ERROR: cannot perform query with placements that were modified over multiple connections + DROP TABLE test_table_2 CASCADE; +ERROR: current transaction is aborted, commands ignored until end of transaction block +ROLLBACK; +-- make sure that we fail if we need parallel data load +BEGIN; + + CREATE TABLE test_table_1(id int PRIMARY KEY); + INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; + CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); + INSERT INTO test_table_2 SELECT i, i FROM generate_series(0,100) i; + SELECT create_reference_table('test_table_1'); +NOTICE: Copying data from local table... + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); +ERROR: cannot distribute "test_table_2" in sequential mode because it is not empty +HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. + DROP TABLE test_table_2, test_table_1; +ERROR: current transaction is aborted, commands ignored until end of transaction block +COMMIT; +-- make sure that other DDLs/DMLs also work fine +-- FIXME: fails for now +BEGIN; + CREATE TABLE test_table_1(id int PRIMARY KEY); + CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + CREATE INDEX i1 ON test_table_1(id); + ALTER TABLE test_table_2 ADD CONSTRAINT check_val CHECK (id > 0); +ERROR: cannot establish a new connection for placement 7000388, since DDL has been executed on a connection that is in use + DROP TABLE test_table_2, test_table_1; +ERROR: current transaction is aborted, commands ignored until end of transaction block +COMMIT; +-- The following tests check if the DDLs affecting foreign keys work as expected +-- check if we can drop the foreign constraint +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 8 +(1 row) + +ALTER TABLE test_table_2 DROP CONSTRAINT test_table_2_value_1_fkey; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can drop the foreign constraint in a transaction right after ADD CONSTRAINT +-- FIXME: fails for now +BEGIN; + CREATE TABLE test_table_1(id int PRIMARY KEY); + CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + ALTER TABLE test_table_2 ADD CONSTRAINT foreign_key FOREIGN KEY(value_1) REFERENCES test_table_1(id); +ERROR: cannot perform query with placements that were modified over multiple connections + ALTER TABLE test_table_2 DROP CONSTRAINT test_table_2_value_1_fkey; +ERROR: current transaction is aborted, commands ignored until end of transaction block +COMMIT; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +ERROR: table "test_table_1" does not exist +-- check if we can drop the primary key which cascades to the foreign key +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE test_table_1 DROP CONSTRAINT test_table_1_pkey CASCADE; +NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can drop the primary key which cascades to the foreign key in a transaction block +BEGIN; + CREATE TABLE test_table_1(id int PRIMARY KEY); + CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + ALTER TABLE test_table_1 DROP CONSTRAINT test_table_1_pkey CASCADE; +NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 +COMMIT; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can drop the column which foreign key is referencing from +CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE test_table_2 DROP COLUMN value_1; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can drop the column which foreign key is referencing from in a transaction block +CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +BEGIN; + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + ALTER TABLE test_table_2 DROP COLUMN value_1; +COMMIT; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can drop the column which foreign key is referencing to +CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +ALTER TABLE test_table_1 DROP COLUMN id CASCADE; +NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can drop the column which foreign key is referencing from in a transaction block +CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +BEGIN; + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + ALTER TABLE test_table_1 DROP COLUMN id CASCADE; +NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 +COMMIT; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can alter the column type which foreign key is referencing to +CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +INSERT INTO test_table_1 VALUES (1,1), (2,2), (3,3); +INSERT INTO test_table_2 VALUES (1,1), (2,2), (3,3); +-- should succeed +ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE bigint; +ALTER TABLE test_table_1 ALTER COLUMN id SET DATA TYPE bigint; +INSERT INTO test_table_1 VALUES (2147483648,4); +INSERT INTO test_table_2 VALUES (4,2147483648); +-- should fail since there is a bigint out of integer range > (2^32 - 1) +ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE int; +ERROR: integer out of range +CONTEXT: while executing command on localhost:57637 +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 8 +(1 row) + +DROP TABLE test_table_1 CASCADE; +NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 +DROP TABLE test_table_2; +-- check if we can alter the column type and drop it which foreign key is referencing to in a transaction block +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +BEGIN; + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE bigint; + ALTER TABLE test_table_1 DROP COLUMN id CASCADE; +NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 +COMMIT; +SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; + count +------- + 0 +(1 row) + +DROP TABLE test_table_1, test_table_2; +-- check if we can TRUNCATE the referenced table +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +INSERT INTO test_table_1 VALUES (1),(2),(3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +TRUNCATE test_table_1 CASCADE; +NOTICE: truncate cascades to table "test_table_2" +SELECT * FROM test_table_2; + id | value_1 +----+--------- +(0 rows) + +DROP TABLE test_table_1, test_table_2; +-- check if we can TRUNCATE the referenced table in a transaction +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +INSERT INTO test_table_1 VALUES (1),(2),(3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +BEGIN; + TRUNCATE test_table_1 CASCADE; +NOTICE: truncate cascades to table "test_table_2" +COMMIT; +SELECT * FROM test_table_2; + id | value_1 +----+--------- +(0 rows) + +DROP TABLE test_table_1, test_table_2; +-- check if we can TRUNCATE the referenced table in a transaction after inserts +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +BEGIN; + SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + + SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + + INSERT INTO test_table_1 VALUES (1),(2),(3); + INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); + TRUNCATE test_table_1 CASCADE; +NOTICE: truncate cascades to table "test_table_2" +COMMIT; +SELECT * FROM test_table_2; + id | value_1 +----+--------- +(0 rows) + +DROP TABLE test_table_1, test_table_2; +-- check if we can TRUNCATE the referencing table +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +INSERT INTO test_table_1 VALUES (1),(2),(3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +TRUNCATE test_table_2 CASCADE; +SELECT * FROM test_table_2; + id | value_1 +----+--------- +(0 rows) + +SELECT * FROM test_table_1; + id +---- + 1 + 2 + 3 +(3 rows) + +DROP TABLE test_table_1, test_table_2; +-- check if we can TRUNCATE the referencing table in a transaction +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +INSERT INTO test_table_1 VALUES (1),(2),(3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +BEGIN; + TRUNCATE test_table_2 CASCADE; +COMMIT; +SELECT * FROM test_table_2; + id | value_1 +----+--------- +(0 rows) + +SELECT * FROM test_table_1; + id +---- + 1 + 2 + 3 +(3 rows) + +DROP TABLE test_table_1, test_table_2; +-- check if we successfuly set multi_shard_modify_mode to sequential after sequentially running DDLs +-- in transaction since the upcoming DDLs need to run sequentially. +-- FIXME: fails for now +CREATE TABLE test_table_1(id int PRIMARY KEY); +CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); +CREATE TABLE test_table_3(id int PRIMARY KEY, value_1 int); +SELECT create_reference_table('test_table_1'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('test_table_2', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +SELECT create_distributed_table('test_table_3', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +BEGIN; + ALTER TABLE test_table_2 ADD CONSTRAINT fkey FOREIGN KEY (value_1) REFERENCES test_table_1(id); + ALTER TABLE test_table_3 ADD COLUMN test_column int; +ERROR: cannot establish a new connection for placement 7000556, since DDL has been executed on a connection that is in use + ALTER TABLE test_table_1 DROP COLUMN id CASCADE; +ERROR: current transaction is aborted, commands ignored until end of transaction block + ALTER TABLE test_table_1 ADD COLUMN id int; +ERROR: current transaction is aborted, commands ignored until end of transaction block +COMMIT; +DROP TABLE test_table_1, test_table_2, test_table_3; +-- NOTE: Postgres does not support foreign keys on partitioned tables currently. +-- However, we can create foreign keys to/from the partitions themselves. +-- The following tests chech if we create the foreign constraints in partitions properly. +CREATE TABLE referenced_table(id int PRIMARY KEY, test_column int); +CREATE TABLE referencing_table(id int, value_1 int) PARTITION BY RANGE (value_1); +ERROR: syntax error at or near "PARTITION" +LINE 1: ...EATE TABLE referencing_table(id int, value_1 int) PARTITION ... + ^ +CREATE TABLE referencing_table_0 PARTITION OF referencing_table FOR VALUES FROM (0) TO (2); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE TABLE referencing_table_0 PARTITION OF referencing_ta... + ^ +CREATE TABLE referencing_table_2 PARTITION OF referencing_table FOR VALUES FROM (2) TO (4); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE TABLE referencing_table_2 PARTITION OF referencing_ta... + ^ +CREATE TABLE referencing_table_4 PARTITION OF referencing_table FOR VALUES FROM (4) TO (6); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE TABLE referencing_table_4 PARTITION OF referencing_ta... + ^ +-- partitioned tables are not supported as reference tables +select create_reference_table('referencing_table'); +ERROR: relation "referencing_table" does not exist +LINE 1: select create_reference_table('referencing_table'); + ^ +-- partitioned tables are supported as hash distributed table +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); +ERROR: relation "referencing_table" does not exist +LINE 1: SELECT create_distributed_table('referencing_table', 'id'); + ^ +-- add foreign constraints in between partitions +ALTER TABLE referencing_table_0 ADD CONSTRAINT pkey PRIMARY KEY (id); +ERROR: relation "referencing_table_0" does not exist +ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey FOREIGN KEY (id) REFERENCES referencing_table_0; +ERROR: relation "referencing_table_4" does not exist +-- add foreign constraint from a partition to reference table +ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey_to_ref FOREIGN KEY (value_1) REFERENCES referenced_table; +ERROR: relation "referencing_table_4" does not exist +-- should fail since the data will flow to partitioning_test_4 and it has a foreign constraint to partitioning_test_0 on id column +INSERT INTO referencing_table VALUES (0, 5); +ERROR: relation "referencing_table" does not exist +LINE 1: INSERT INTO referencing_table VALUES (0, 5); + ^ +-- should succeed on partitioning_test_0 +INSERT INTO referencing_table VALUES (0, 1); +ERROR: relation "referencing_table" does not exist +LINE 1: INSERT INTO referencing_table VALUES (0, 1); + ^ +SELECT * FROM referencing_table; +ERROR: relation "referencing_table" does not exist +LINE 1: SELECT * FROM referencing_table; + ^ +-- should fail since partitioning_test_4 has foreign constraint to referenced_table on value_1 column +INSERT INTO referencing_table VALUES (0, 5); +ERROR: relation "referencing_table" does not exist +LINE 1: INSERT INTO referencing_table VALUES (0, 5); + ^ +INSERT INTO referenced_table VALUES(5,5); +-- should succeed since both of the foreign constraints are positive +INSERT INTO referencing_table VALUES (0, 5); +ERROR: relation "referencing_table" does not exist +LINE 1: INSERT INTO referencing_table VALUES (0, 5); + ^ +DROP TABLE referenced_table CASCADE; +DROP TABLE referencing_table; +ERROR: table "referencing_table" does not exist +DROP SCHEMA fkey_reference_table CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to type foreign_details +drop cascades to view table_fkeys_in_workers +drop cascades to type composite +SET search_path TO DEFAULT; diff --git a/src/test/regress/expected/mx_foreign_key_to_reference_table.out b/src/test/regress/expected/mx_foreign_key_to_reference_table.out new file mode 100644 index 000000000..d0d3058f4 --- /dev/null +++ b/src/test/regress/expected/mx_foreign_key_to_reference_table.out @@ -0,0 +1,90 @@ +CREATE SCHEMA fkey_reference_table; +SET search_path TO 'fkey_reference_table'; +SET citus.shard_replication_factor TO 1; +SET citus.shard_count TO 8; +SET citus.next_shard_id TO 7000000; +SET citus.next_placement_id TO 7000000; +SET citus.replication_model TO streaming; +-- Setup the view so that we can check if the foreign keys are created properly +CREATE TYPE foreign_details AS (name text, relid text, refd_relid text); +SELECT run_command_on_workers($$CREATE TYPE foreign_details AS (name text, relid text, refd_relid text)$$); + run_command_on_workers +----------------------------------- + (localhost,57637,t,"CREATE TYPE") + (localhost,57638,t,"CREATE TYPE") +(2 rows) + +CREATE VIEW table_fkeys_in_workers AS +SELECT +(json_populate_record(NULL::foreign_details, + json_array_elements_text((run_command_on_workers( $$ + SELECT + COALESCE(json_agg(row_to_json(d)), '[]'::json) + FROM + ( + SELECT + distinct name, + relid::regclass::text, + refd_relid::regclass::text + FROM + table_fkey_cols + ) + d $$ )).RESULT::json )::json )).* ; +-- Check if MX can create foreign keys properly on foreign keys from distributed to reference tables +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); +CREATE TABLE referencing_table(id int, ref_id int); +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; +SELECT create_reference_table('referenced_table'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_reference_table('referenced_table2'); + create_reference_table +------------------------ + +(1 row) + +SELECT create_distributed_table('referencing_table', 'id'); + create_distributed_table +-------------------------- + +(1 row) + +SET search_path TO 'fkey_reference_table'; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1, 2; + name | relid | refd_relid +-----------------------+------------------------------------------------+------------------------------------------------ + fkey_ref | fkey_reference_table.referencing_table | fkey_reference_table.referenced_table + fkey_ref | fkey_reference_table.referencing_table | fkey_reference_table.referenced_table + fkey_ref_7000002 | fkey_reference_table.referencing_table_7000002 | fkey_reference_table.referenced_table_7000000 + fkey_ref_7000003 | fkey_reference_table.referencing_table_7000003 | fkey_reference_table.referenced_table_7000000 + fkey_ref_7000004 | fkey_reference_table.referencing_table_7000004 | fkey_reference_table.referenced_table_7000000 + fkey_ref_7000005 | fkey_reference_table.referencing_table_7000005 | fkey_reference_table.referenced_table_7000000 + fkey_ref_7000006 | fkey_reference_table.referencing_table_7000006 | fkey_reference_table.referenced_table_7000000 + fkey_ref_7000007 | fkey_reference_table.referencing_table_7000007 | fkey_reference_table.referenced_table_7000000 + fkey_ref_7000008 | fkey_reference_table.referencing_table_7000008 | fkey_reference_table.referenced_table_7000000 + fkey_ref_7000009 | fkey_reference_table.referencing_table_7000009 | fkey_reference_table.referenced_table_7000000 + foreign_key_2 | fkey_reference_table.referencing_table | fkey_reference_table.referenced_table2 + foreign_key_2 | fkey_reference_table.referencing_table | fkey_reference_table.referenced_table2 + foreign_key_2_7000002 | fkey_reference_table.referencing_table_7000002 | fkey_reference_table.referenced_table2_7000001 + foreign_key_2_7000003 | fkey_reference_table.referencing_table_7000003 | fkey_reference_table.referenced_table2_7000001 + foreign_key_2_7000004 | fkey_reference_table.referencing_table_7000004 | fkey_reference_table.referenced_table2_7000001 + foreign_key_2_7000005 | fkey_reference_table.referencing_table_7000005 | fkey_reference_table.referenced_table2_7000001 + foreign_key_2_7000006 | fkey_reference_table.referencing_table_7000006 | fkey_reference_table.referenced_table2_7000001 + foreign_key_2_7000007 | fkey_reference_table.referencing_table_7000007 | fkey_reference_table.referenced_table2_7000001 + foreign_key_2_7000008 | fkey_reference_table.referencing_table_7000008 | fkey_reference_table.referenced_table2_7000001 + foreign_key_2_7000009 | fkey_reference_table.referencing_table_7000009 | fkey_reference_table.referenced_table2_7000001 +(20 rows) + +DROP SCHEMA fkey_reference_table CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to type foreign_details +drop cascades to view table_fkeys_in_workers +drop cascades to table referenced_table +drop cascades to table referenced_table2 +drop cascades to table referencing_table +SET search_path TO DEFAULT; diff --git a/src/test/regress/multi_mx_schedule b/src/test/regress/multi_mx_schedule index a746e7d58..11be43432 100644 --- a/src/test/regress/multi_mx_schedule +++ b/src/test/regress/multi_mx_schedule @@ -25,7 +25,7 @@ test: multi_mx_tpch_query12 multi_mx_tpch_query14 multi_mx_tpch_query19 test: multi_mx_tpch_query3 multi_mx_tpch_query6 multi_mx_tpch_query7 test: multi_mx_tpch_query7_nested multi_mx_ddl test: recursive_dml_queries_mx -test: multi_mx_repartition_udt_prepare +test: multi_mx_repartition_udt_prepare mx_foreign_key_to_reference_table test: multi_mx_repartition_join_w1 multi_mx_repartition_join_w2 multi_mx_repartition_udt_w1 multi_mx_repartition_udt_w2 test: multi_mx_metadata test: multi_mx_modifications diff --git a/src/test/regress/sql/foreign_key_to_reference_table.sql b/src/test/regress/sql/foreign_key_to_reference_table.sql index c9f5f39b6..f79ef0469 100644 --- a/src/test/regress/sql/foreign_key_to_reference_table.sql +++ b/src/test/regress/sql/foreign_key_to_reference_table.sql @@ -795,11 +795,10 @@ INSERT INTO test_table_2 VALUES (1,1), (2,2), (3,3); ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE bigint; ALTER TABLE test_table_1 ALTER COLUMN id SET DATA TYPE bigint; --- should fail since there is a bigint out of integer range > (2^32 - 1) INSERT INTO test_table_1 VALUES (2147483648,4); INSERT INTO test_table_2 VALUES (4,2147483648); +-- should fail since there is a bigint out of integer range > (2^32 - 1) ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE int; - SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; DROP TABLE test_table_1 CASCADE; DROP TABLE test_table_2; @@ -909,6 +908,40 @@ BEGIN; ALTER TABLE test_table_1 ADD COLUMN id int; COMMIT; DROP TABLE test_table_1, test_table_2, test_table_3; +-- NOTE: Postgres does not support foreign keys on partitioned tables currently. +-- However, we can create foreign keys to/from the partitions themselves. +-- The following tests chech if we create the foreign constraints in partitions properly. +CREATE TABLE referenced_table(id int PRIMARY KEY, test_column int); +CREATE TABLE referencing_table(id int, value_1 int) PARTITION BY RANGE (value_1); +CREATE TABLE referencing_table_0 PARTITION OF referencing_table FOR VALUES FROM (0) TO (2); +CREATE TABLE referencing_table_2 PARTITION OF referencing_table FOR VALUES FROM (2) TO (4); +CREATE TABLE referencing_table_4 PARTITION OF referencing_table FOR VALUES FROM (4) TO (6); + +-- partitioned tables are not supported as reference tables +select create_reference_table('referencing_table'); + +-- partitioned tables are supported as hash distributed table +SELECT create_reference_table('referenced_table'); +SELECT create_distributed_table('referencing_table', 'id'); + +-- add foreign constraints in between partitions +ALTER TABLE referencing_table_0 ADD CONSTRAINT pkey PRIMARY KEY (id); +ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey FOREIGN KEY (id) REFERENCES referencing_table_0; +-- add foreign constraint from a partition to reference table +ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey_to_ref FOREIGN KEY (value_1) REFERENCES referenced_table; +-- should fail since the data will flow to partitioning_test_4 and it has a foreign constraint to partitioning_test_0 on id column +INSERT INTO referencing_table VALUES (0, 5); +-- should succeed on partitioning_test_0 +INSERT INTO referencing_table VALUES (0, 1); +SELECT * FROM referencing_table; +-- should fail since partitioning_test_4 has foreign constraint to referenced_table on value_1 column +INSERT INTO referencing_table VALUES (0, 5); +INSERT INTO referenced_table VALUES(5,5); +-- should succeed since both of the foreign constraints are positive +INSERT INTO referencing_table VALUES (0, 5); + +DROP TABLE referenced_table CASCADE; +DROP TABLE referencing_table; DROP SCHEMA fkey_reference_table CASCADE; SET search_path TO DEFAULT; diff --git a/src/test/regress/sql/mx_foreign_key_to_reference_table.sql b/src/test/regress/sql/mx_foreign_key_to_reference_table.sql new file mode 100644 index 000000000..8b19a2efe --- /dev/null +++ b/src/test/regress/sql/mx_foreign_key_to_reference_table.sql @@ -0,0 +1,45 @@ +CREATE SCHEMA fkey_reference_table; +SET search_path TO 'fkey_reference_table'; +SET citus.shard_replication_factor TO 1; +SET citus.shard_count TO 8; +SET citus.next_shard_id TO 7000000; +SET citus.next_placement_id TO 7000000; +SET citus.replication_model TO streaming; + +-- Setup the view so that we can check if the foreign keys are created properly +CREATE TYPE foreign_details AS (name text, relid text, refd_relid text); +SELECT run_command_on_workers($$CREATE TYPE foreign_details AS (name text, relid text, refd_relid text)$$); + +CREATE VIEW table_fkeys_in_workers AS +SELECT +(json_populate_record(NULL::foreign_details, + json_array_elements_text((run_command_on_workers( $$ + SELECT + COALESCE(json_agg(row_to_json(d)), '[]'::json) + FROM + ( + SELECT + distinct name, + relid::regclass::text, + refd_relid::regclass::text + FROM + table_fkey_cols + ) + d $$ )).RESULT::json )::json )).* ; + +-- Check if MX can create foreign keys properly on foreign keys from distributed to reference tables +CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); +CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); +CREATE TABLE referencing_table(id int, ref_id int); +ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; +ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; + +SELECT create_reference_table('referenced_table'); +SELECT create_reference_table('referenced_table2'); +SELECT create_distributed_table('referencing_table', 'id'); + +SET search_path TO 'fkey_reference_table'; +SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1, 2; + +DROP SCHEMA fkey_reference_table CASCADE; +SET search_path TO DEFAULT;