diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index e6606e5e57a..592a12fa95b 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1975,13 +1975,19 @@ ExecuteTruncateGuts(List *explicit_rels, * Check foreign key references. In CASCADE mode, this should be * unnecessary since we just pulled in all the references; but as a * cross-check, do it anyway if in an Assert-enabled build. + * + * Skip foreign key checks when `session_replication_role = replica` to + * match the behaviour of disabling FK triggers in the same situation */ + if (SessionReplicationRole != SESSION_REPLICATION_ROLE_REPLICA) + { #ifdef USE_ASSERT_CHECKING - heap_truncate_check_FKs(rels, false); -#else - if (behavior == DROP_RESTRICT) heap_truncate_check_FKs(rels, false); +#else + if (behavior == DROP_RESTRICT) + heap_truncate_check_FKs(rels, false); #endif + } /* * If we are asked to restart sequences, find all the sequences, lock them @@ -5918,49 +5924,55 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, * theoretically possible that we have changed both relations of the * foreign key, and we'd better have finished both rewrites before we try * to read the tables. + * + * Skip the check when `session_replication_mode = replica` to save time + * and to match the FK trigger behaviour in the same situation */ - foreach(ltab, *wqueue) + if (SessionReplicationRole != SESSION_REPLICATION_ROLE_REPLICA) { - AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab); - Relation rel = NULL; - ListCell *lcon; - - /* Relations without storage may be ignored here too */ - if (!RELKIND_HAS_STORAGE(tab->relkind)) - continue; - - foreach(lcon, tab->constraints) + foreach(ltab, *wqueue) { - NewConstraint *con = lfirst(lcon); + AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab); + Relation rel = NULL; + ListCell *lcon; - if (con->contype == CONSTR_FOREIGN) + /* Relations without storage may be ignored here too */ + if (!RELKIND_HAS_STORAGE(tab->relkind)) + continue; + + foreach(lcon, tab->constraints) { - Constraint *fkconstraint = (Constraint *) con->qual; - Relation refrel; + NewConstraint *con = lfirst(lcon); - if (rel == NULL) + if (con->contype == CONSTR_FOREIGN) { - /* Long since locked, no need for another */ - rel = table_open(tab->relid, NoLock); - } + Constraint *fkconstraint = (Constraint *) con->qual; + Relation refrel; - refrel = table_open(con->refrelid, RowShareLock); + if (rel == NULL) + { + /* Long since locked, no need for another */ + rel = table_open(tab->relid, NoLock); + } - validateForeignKeyConstraint(fkconstraint->conname, rel, refrel, - con->refindid, - con->conid); + refrel = table_open(con->refrelid, RowShareLock); - /* - * No need to mark the constraint row as validated, we did - * that when we inserted the row earlier. - */ + validateForeignKeyConstraint(fkconstraint->conname, rel, refrel, + con->refindid, + con->conid); - table_close(refrel, NoLock); + /* + * No need to mark the constraint row as validated, we did + * that when we inserted the row earlier. + */ + + table_close(refrel, NoLock); + } } - } - if (rel) - table_close(rel, NoLock); + if (rel) + table_close(rel, NoLock); + } } /* Finally, run any afterStmts that were queued up */ diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 8cbad245217..b6d41e6df11 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -3073,6 +3073,41 @@ ALTER TABLE fk_r DROP CONSTRAINT fk_r_p_id_p_jd_fkey1; ERROR: cannot drop inherited constraint "fk_r_p_id_p_jd_fkey1" of relation "fk_r" ALTER TABLE fk_r_2 DROP CONSTRAINT fk_r_p_id_p_jd_fkey; ERROR: cannot drop inherited constraint "fk_r_p_id_p_jd_fkey" of relation "fk_r_2" +-- tests for SET session_replication_role = replica; +RESET session_replication_role; +-- disabling FK checks +CREATE TABLE pkt(id int PRIMARY KEY); +CREATE TABLE fkt(fk int REFERENCES pkt(id)); +INSERT INTO fkt VALUES(1); -- should fail +ERROR: insert or update on table "fkt" violates foreign key constraint "fkt_fk_fkey" +DETAIL: Key (fk)=(1) is not present in table "pkt". +SET session_replication_role=replica; +INSERT INTO fkt VALUES(1); -- should succeed now +DROP TABLE fkt, pkt; +RESET session_replication_role; +-- skipping FK validation during ALTER TABLE ... ADD FOREIGN KEY +CREATE TABLE pkt(id int PRIMARY KEY); +CREATE TABLE fkt(fk int); +INSERT INTO fkt VALUES(1); +ALTER TABLE fkt ADD FOREIGN KEY (fk) REFERENCES pkt(id); -- should fail +ERROR: insert or update on table "fkt" violates foreign key constraint "fkt_fk_fkey" +DETAIL: Key (fk)=(1) is not present in table "pkt". +SET session_replication_role=replica; +ALTER TABLE fkt ADD FOREIGN KEY (fk) REFERENCES pkt(id); -- should succeed now +DROP TABLE fkt, pkt; +RESET session_replication_role; +-- skipping FK existence checks during TRUNCATE +CREATE TABLE pkt(id int PRIMARY KEY); +CREATE TABLE fkt(fk int REFERENCES pkt(id)); +TRUNCATE pkt; -- should fail +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "fkt" references "pkt". +HINT: Truncate table "fkt" at the same time, or use TRUNCATE ... CASCADE. +SET session_replication_role=replica; +TRUNCATE pkt; -- should succeed now +DROP TABLE fkt, pkt; +RESET session_replication_role; +-- end of tests for SET session_replication_role = replica; SET client_min_messages TO warning; DROP SCHEMA fkpart12 CASCADE; RESET client_min_messages; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index ea08fd5a6f6..b24277a901f 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -1151,6 +1151,10 @@ commit; drop table pktable2, fktable2; +-- +-- +-- + -- -- Test keys that "look" different but compare as equal -- @@ -2177,7 +2181,54 @@ ALTER TABLE fk_r_1 DROP CONSTRAINT fk_r_p_id_p_jd_fkey; ALTER TABLE fk_r DROP CONSTRAINT fk_r_p_id_p_jd_fkey1; ALTER TABLE fk_r_2 DROP CONSTRAINT fk_r_p_id_p_jd_fkey; +-- tests for SET session_replication_role = replica; + +RESET session_replication_role; + +-- disabling FK checks + +CREATE TABLE pkt(id int PRIMARY KEY); +CREATE TABLE fkt(fk int REFERENCES pkt(id)); + +INSERT INTO fkt VALUES(1); -- should fail + +SET session_replication_role=replica; +INSERT INTO fkt VALUES(1); -- should succeed now + +DROP TABLE fkt, pkt; +RESET session_replication_role; + +-- skipping FK validation during ALTER TABLE ... ADD FOREIGN KEY + +CREATE TABLE pkt(id int PRIMARY KEY); +CREATE TABLE fkt(fk int); +INSERT INTO fkt VALUES(1); + +ALTER TABLE fkt ADD FOREIGN KEY (fk) REFERENCES pkt(id); -- should fail + +SET session_replication_role=replica; +ALTER TABLE fkt ADD FOREIGN KEY (fk) REFERENCES pkt(id); -- should succeed now + +DROP TABLE fkt, pkt; +RESET session_replication_role; + +-- skipping FK existence checks during TRUNCATE + +CREATE TABLE pkt(id int PRIMARY KEY); +CREATE TABLE fkt(fk int REFERENCES pkt(id)); + +TRUNCATE pkt; -- should fail + +SET session_replication_role=replica; +TRUNCATE pkt; -- should succeed now + +DROP TABLE fkt, pkt; +RESET session_replication_role; + +-- end of tests for SET session_replication_role = replica; + SET client_min_messages TO warning; DROP SCHEMA fkpart12 CASCADE; RESET client_min_messages; RESET search_path; +