diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index 2610c18b6..6f7527601 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -955,6 +955,8 @@ InvalidateForeignKeyGraphForDDL(void) { if (shouldInvalidateForeignKeyGraph) { + ereport(DEBUG1, (errmsg("DDL command invalidates foreign key graph"))); + InvalidateForeignKeyGraph(); shouldInvalidateForeignKeyGraph = false; diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index dcc7e20b0..dd5bee26a 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -176,6 +176,11 @@ s/(->.*Scan on\ +)(.*)(_[0-9]+)(_[0-9]+) \2(_[0-9]+|_xxx)?/\1\2\3\4 \2_xxx/g s/(partitioning_hash_join_test)(_[0-9]|_xxx)?(\.[a-zA-Z]+)/\1_xxx\3/g s/(partitioning_hash_test)(_[0-9]|_xxx)?(\.[a-zA-Z]+)/\1_xxx\3/g +# multi_foreign_key_relation_graph.out +# suppress shardId's in drop constraint logs as the order of cascaded objects might be flaky +# e.g: drop cascades to constraint fkey_6_3000081 on table fkey_graph.distributed_table_1_3000081 +s/(drop cascades to constraint fkey_[0-9]+_)([0-9]+)( on table fkey_graph\..+_)\2/\1xxx\3xxx/g + # Errors with binary decoding where OIDs should be normalized s/wrong data type: [0-9]+, expected [0-9]+/wrong data type: XXXX, expected XXXX/g diff --git a/src/test/regress/expected/foreign_key_restriction_enforcement.out b/src/test/regress/expected/foreign_key_restriction_enforcement.out index 79a599f4f..efec55f58 100644 --- a/src/test/regress/expected/foreign_key_restriction_enforcement.out +++ b/src/test/regress/expected/foreign_key_restriction_enforcement.out @@ -306,6 +306,7 @@ BEGIN; (1 row) ALTER TABLE on_update_fkey_table ADD COLUMN X INT; +DEBUG: DDL command invalidates foreign key graph ERROR: cannot execute parallel DDL on table "on_update_fkey_table" after SELECT command on reference table "reference_table" because there is a foreign key between them and "reference_table" has been accessed in this transaction DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" @@ -1399,6 +1400,7 @@ ADD CONSTRAINT fkey_delete FOREIGN KEY(value_1) REFERENCES reference_table(id) ON DELETE CASCADE; +DEBUG: DDL command invalidates foreign key graph INSERT INTO reference_table SELECT i FROM generate_series(0, 10) i; DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator diff --git a/src/test/regress/expected/multi_foreign_key_relation_graph.out b/src/test/regress/expected/multi_foreign_key_relation_graph.out index 41de227c7..18c408613 100644 --- a/src/test/regress/expected/multi_foreign_key_relation_graph.out +++ b/src/test/regress/expected/multi_foreign_key_relation_graph.out @@ -711,15 +711,17 @@ ORDER BY tablename; CREATE TABLE local_table_1 (col int unique); CREATE TABLE local_table_2 (col int unique); --- show that we do not trigger updating foreign key graph when +-- show that we trigger updating foreign key graph when -- defining/dropping foreign keys between postgres tables ALTER TABLE local_table_1 ADD CONSTRAINT fkey_1 FOREIGN KEY (col) REFERENCES local_table_2(col); SELECT oid::regclass::text AS tablename FROM get_foreign_key_connected_relations('local_table_2') AS f(oid oid) ORDER BY tablename; - tablename + tablename --------------------------------------------------------------------- -(0 rows) + local_table_1 + local_table_2 +(2 rows) ALTER TABLE local_table_1 DROP CONSTRAINT fkey_1; SELECT oid::regclass::text AS tablename @@ -734,6 +736,195 @@ SELECT oid::regclass::text AS tablename FROM get_foreign_key_connected_relations('non_existent_table') AS f(oid oid) ORDER BY tablename; ERROR: relation "non_existent_table" does not exist +\set VERBOSITY TERSE +SET client_min_messages TO DEBUG1; +BEGIN; + ALTER TABLE distributed_table_2 DROP CONSTRAINT distributed_table_2_col_key CASCADE; +NOTICE: drop cascades to constraint fkey_4 on table distributed_table_3 +DEBUG: DDL command invalidates foreign key graph +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx + ALTER TABLE distributed_table_3 DROP CONSTRAINT distributed_table_3_col_key CASCADE; +NOTICE: drop cascades to constraint fkey_6 on table distributed_table_1 +DEBUG: DDL command invalidates foreign key graph +DEBUG: drop cascades to constraint fkey_6_xxx on table fkey_graph.distributed_table_1_xxx +DEBUG: drop cascades to constraint fkey_6_xxx on table fkey_graph.distributed_table_1_xxx +DEBUG: drop cascades to constraint fkey_6_xxx on table fkey_graph.distributed_table_1_xxx +DEBUG: drop cascades to constraint fkey_6_xxx on table fkey_graph.distributed_table_1_xxx + -- show that we process drop constraint commands that are dropping uniquness + -- constraints and then invalidate fkey graph. So we shouldn't see + -- distributed_table_3 as it was split via above drop constraint commands + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('distributed_table_2') AS f(oid oid) + ORDER BY tablename; + tablename +--------------------------------------------------------------------- + distributed_table_1 + distributed_table_2 + reference_table_1 + reference_table_2 +(4 rows) + +ROLLBACK; +-- Below two commands normally would invalidate foreign key graph. +-- But as they fail due to lacking of CASCADE, we don't invalidate +-- foreign key graph. +ALTER TABLE distributed_table_2 DROP CONSTRAINT distributed_table_2_col_key; +ERROR: cannot drop constraint distributed_table_2_col_key on table distributed_table_2 because other objects depend on it +ALTER TABLE reference_table_2 DROP COLUMN col; +ERROR: cannot drop column col of table reference_table_2 because other objects depend on it +-- but we invalidate foreign key graph in below two transaction blocks +BEGIN; + ALTER TABLE distributed_table_2 DROP CONSTRAINT distributed_table_2_col_key CASCADE; +NOTICE: drop cascades to constraint fkey_4 on table distributed_table_3 +DEBUG: DDL command invalidates foreign key graph +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +ROLLBACK; +BEGIN; + ALTER TABLE reference_table_2 DROP COLUMN col CASCADE; +NOTICE: drop cascades to constraint fkey_5 on table distributed_table_2 +DEBUG: DDL command invalidates foreign key graph +DEBUG: drop cascades to 2 other objects +DETAIL: drop cascades to constraint fkey_5_xxx on table fkey_graph.distributed_table_2_xxx +drop cascades to constraint fkey_5_xxx on table fkey_graph.distributed_table_2_xxx +DEBUG: drop cascades to 2 other objects +DETAIL: drop cascades to constraint fkey_5_xxx on table fkey_graph.distributed_table_2_xxx +drop cascades to constraint fkey_5_xxx on table fkey_graph.distributed_table_2_xxx +ROLLBACK; +-- now we should see distributed_table_2 as well since we rollback'ed +SELECT oid::regclass::text AS tablename +FROM get_foreign_key_connected_relations('distributed_table_2') AS f(oid oid) +ORDER BY tablename; + tablename +--------------------------------------------------------------------- + distributed_table_1 + distributed_table_2 + distributed_table_3 + reference_table_1 + reference_table_2 +(5 rows) + +BEGIN; + DROP TABLE distributed_table_2 CASCADE; +NOTICE: drop cascades to constraint fkey_4 on table distributed_table_3 +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: drop cascades to constraint fkey_4_xxx on table fkey_graph.distributed_table_3_xxx +DEBUG: DDL command invalidates foreign key graph + -- should only see reference_table_1 & reference_table_2 + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('reference_table_1') AS f(oid oid) + ORDER BY tablename; + tablename +--------------------------------------------------------------------- + reference_table_1 + reference_table_2 +(2 rows) + +ROLLBACK; +BEGIN; + ALTER TABLE distributed_table_2 ADD CONSTRAINT fkey_55 FOREIGN KEY (col) REFERENCES reference_table_2(col); +DEBUG: DDL command invalidates foreign key graph + ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_66 FOREIGN KEY (col) REFERENCES distributed_table_3(col); +DEBUG: DDL command invalidates foreign key graph + -- show that we handle multiple edges between nodes in foreign key graph + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('reference_table_1') AS f(oid oid) + ORDER BY tablename; + tablename +--------------------------------------------------------------------- + distributed_table_1 + distributed_table_2 + distributed_table_3 + reference_table_1 + reference_table_2 +(5 rows) + +ROLLBACK; +BEGIN; + -- hide "verifying table" log because the order we print it changes + -- in different pg versions + set client_min_messages to error; + ALTER TABLE distributed_table_2 ADD CONSTRAINT pkey PRIMARY KEY (col); + set client_min_messages to debug1; + -- show that droping a constraint not involved in any foreign key + -- constraint doesn't invalidate foreign key graph + ALTER TABLE distributed_table_2 DROP CONSTRAINT pkey; +ROLLBACK; +BEGIN; + CREATE TABLE local_table_3 (col int PRIMARY KEY); +DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "local_table_3_pkey" for table "local_table_3" + ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_3(col); +DEBUG: DDL command invalidates foreign key graph + CREATE TABLE local_table_4 (col int PRIMARY KEY REFERENCES local_table_3 (col)); +DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "local_table_4_pkey" for table "local_table_4" +DEBUG: DDL command invalidates foreign key graph + -- we invalidate foreign key graph for add column & create table + -- commands defining foreign keys too + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('local_table_3') AS f(oid oid) + ORDER BY tablename; + tablename +--------------------------------------------------------------------- + local_table_1 + local_table_3 + local_table_4 +(3 rows) + +ROLLBACK; +BEGIN; + CREATE TABLE local_table_3 (col int PRIMARY KEY); +DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "local_table_3_pkey" for table "local_table_3" + ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_3(col); +DEBUG: DDL command invalidates foreign key graph + ALTER TABLE local_table_1 DROP COLUMN another_col; +DEBUG: DDL command invalidates foreign key graph + -- we invalidate foreign key graph for drop column commands dropping + -- referencing columns, should not print anything + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('local_table_1') AS f(oid oid) + ORDER BY tablename; + tablename +--------------------------------------------------------------------- +(0 rows) + +ROLLBACK; +BEGIN; + CREATE TABLE local_table_3 (col int PRIMARY KEY); +DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "local_table_3_pkey" for table "local_table_3" + ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_3(col); +DEBUG: DDL command invalidates foreign key graph + ALTER TABLE local_table_3 DROP COLUMN col CASCADE; +NOTICE: drop cascades to constraint local_table_1_another_col_fkey on table local_table_1 +DEBUG: DDL command invalidates foreign key graph + -- we invalidate foreign key graph for drop column commands dropping + -- referenced columns, should not print anything + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('local_table_1') AS f(oid oid) + ORDER BY tablename; + tablename +--------------------------------------------------------------------- +(0 rows) + +ROLLBACK; +CREATE TABLE local_table_4 (col int); +-- Normally, we would decide to invalidate foreign key graph for below +-- ddl. But as it fails, we won't invalidate foreign key graph. +ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_4(col); +ERROR: there is no unique constraint matching given keys for referenced table "local_table_4" +-- When ddl command fails, we reset flag that we use to invalidate +-- foreign key graph so that next command doesn't invalidate foreign +-- key graph. +ALTER TABLE local_table_1 ADD COLUMN unrelated_column int; +-- show that droping a table not referenced and not referencing to any table +-- does not invalidate foreign key graph +DROP TABLE local_table_4; set client_min_messages to error; SET search_path TO public; DROP SCHEMA fkey_graph CASCADE; diff --git a/src/test/regress/sql/multi_foreign_key_relation_graph.sql b/src/test/regress/sql/multi_foreign_key_relation_graph.sql index 818b5b34f..1b8334e6f 100644 --- a/src/test/regress/sql/multi_foreign_key_relation_graph.sql +++ b/src/test/regress/sql/multi_foreign_key_relation_graph.sql @@ -287,7 +287,7 @@ ORDER BY tablename; CREATE TABLE local_table_1 (col int unique); CREATE TABLE local_table_2 (col int unique); --- show that we do not trigger updating foreign key graph when +-- show that we trigger updating foreign key graph when -- defining/dropping foreign keys between postgres tables ALTER TABLE local_table_1 ADD CONSTRAINT fkey_1 FOREIGN KEY (col) REFERENCES local_table_2(col); @@ -307,6 +307,122 @@ SELECT oid::regclass::text AS tablename FROM get_foreign_key_connected_relations('non_existent_table') AS f(oid oid) ORDER BY tablename; +\set VERBOSITY TERSE +SET client_min_messages TO DEBUG1; + +BEGIN; + ALTER TABLE distributed_table_2 DROP CONSTRAINT distributed_table_2_col_key CASCADE; + ALTER TABLE distributed_table_3 DROP CONSTRAINT distributed_table_3_col_key CASCADE; + -- show that we process drop constraint commands that are dropping uniquness + -- constraints and then invalidate fkey graph. So we shouldn't see + -- distributed_table_3 as it was split via above drop constraint commands + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('distributed_table_2') AS f(oid oid) + ORDER BY tablename; +ROLLBACK; + + +-- Below two commands normally would invalidate foreign key graph. +-- But as they fail due to lacking of CASCADE, we don't invalidate +-- foreign key graph. +ALTER TABLE distributed_table_2 DROP CONSTRAINT distributed_table_2_col_key; +ALTER TABLE reference_table_2 DROP COLUMN col; + +-- but we invalidate foreign key graph in below two transaction blocks +BEGIN; + ALTER TABLE distributed_table_2 DROP CONSTRAINT distributed_table_2_col_key CASCADE; +ROLLBACK; + +BEGIN; + ALTER TABLE reference_table_2 DROP COLUMN col CASCADE; +ROLLBACK; + +-- now we should see distributed_table_2 as well since we rollback'ed +SELECT oid::regclass::text AS tablename +FROM get_foreign_key_connected_relations('distributed_table_2') AS f(oid oid) +ORDER BY tablename; + +BEGIN; + DROP TABLE distributed_table_2 CASCADE; + -- should only see reference_table_1 & reference_table_2 + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('reference_table_1') AS f(oid oid) + ORDER BY tablename; +ROLLBACK; + +BEGIN; + ALTER TABLE distributed_table_2 ADD CONSTRAINT fkey_55 FOREIGN KEY (col) REFERENCES reference_table_2(col); + ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_66 FOREIGN KEY (col) REFERENCES distributed_table_3(col); + -- show that we handle multiple edges between nodes in foreign key graph + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('reference_table_1') AS f(oid oid) + ORDER BY tablename; +ROLLBACK; + +BEGIN; + -- hide "verifying table" log because the order we print it changes + -- in different pg versions + set client_min_messages to error; + ALTER TABLE distributed_table_2 ADD CONSTRAINT pkey PRIMARY KEY (col); + set client_min_messages to debug1; + + -- show that droping a constraint not involved in any foreign key + -- constraint doesn't invalidate foreign key graph + ALTER TABLE distributed_table_2 DROP CONSTRAINT pkey; +ROLLBACK; + +BEGIN; + CREATE TABLE local_table_3 (col int PRIMARY KEY); + ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_3(col); + + CREATE TABLE local_table_4 (col int PRIMARY KEY REFERENCES local_table_3 (col)); + + -- we invalidate foreign key graph for add column & create table + -- commands defining foreign keys too + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('local_table_3') AS f(oid oid) + ORDER BY tablename; +ROLLBACK; + +BEGIN; + CREATE TABLE local_table_3 (col int PRIMARY KEY); + ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_3(col); + + ALTER TABLE local_table_1 DROP COLUMN another_col; + + -- we invalidate foreign key graph for drop column commands dropping + -- referencing columns, should not print anything + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('local_table_1') AS f(oid oid) + ORDER BY tablename; +ROLLBACK; + +BEGIN; + CREATE TABLE local_table_3 (col int PRIMARY KEY); + ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_3(col); + + ALTER TABLE local_table_3 DROP COLUMN col CASCADE; + + -- we invalidate foreign key graph for drop column commands dropping + -- referenced columns, should not print anything + SELECT oid::regclass::text AS tablename + FROM get_foreign_key_connected_relations('local_table_1') AS f(oid oid) + ORDER BY tablename; +ROLLBACK; + +CREATE TABLE local_table_4 (col int); +-- Normally, we would decide to invalidate foreign key graph for below +-- ddl. But as it fails, we won't invalidate foreign key graph. +ALTER TABLE local_table_1 ADD COLUMN another_col int REFERENCES local_table_4(col); +-- When ddl command fails, we reset flag that we use to invalidate +-- foreign key graph so that next command doesn't invalidate foreign +-- key graph. +ALTER TABLE local_table_1 ADD COLUMN unrelated_column int; + +-- show that droping a table not referenced and not referencing to any table +-- does not invalidate foreign key graph +DROP TABLE local_table_4; + set client_min_messages to error; SET search_path TO public;