From: Tom Lane Date: Fri, 25 Nov 2016 18:44:48 +0000 (-0500) Subject: Check for pending trigger events on far end when dropping an FK constraint. X-Git-Tag: REL9_2_20~61 X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=6a363a4c252329f1f97d80526eb55b3301abf1f2;p=postgresql.git Check for pending trigger events on far end when dropping an FK constraint. When dropping a foreign key constraint with ALTER TABLE DROP CONSTRAINT, we refuse the drop if there are any pending trigger events on the named table; this ensures that we won't remove the pg_trigger row that will be consulted by those events. But we should make the same check for the referenced relation, else we might remove a due-to-be-referenced pg_trigger row for that relation too, resulting in "could not find trigger NNN" or "relation NNN has no triggers" errors at commit. Per bug #14431 from Benjie Gillam. Back-patch to all supported branches. Report: <20161124114911.6530.31200@wrigleys.postgresql.org> --- diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 4854385cf1c..97e7d140ba7 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -6989,6 +6989,24 @@ ATExecDropConstraint(Relation rel, const char *constrName, if (con->contype != CONSTRAINT_CHECK) is_no_inherit_constraint = true; + /* + * If it's a foreign-key constraint, we'd better lock the referenced + * table and check that that's not in use, just as we've already done + * for the constrained table (else we might, eg, be dropping a trigger + * that has unfired events). But we can/must skip that in the + * self-referential case. + */ + if (con->contype == CONSTRAINT_FOREIGN && + con->confrelid != RelationGetRelid(rel)) + { + Relation frel; + + /* Must match lock taken by RemoveTriggerById: */ + frel = heap_open(con->confrelid, AccessExclusiveLock); + CheckTableNotInUse(frel, "ALTER TABLE"); + heap_close(frel, NoLock); + } + /* * Perform the actual constraint deletion */ diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index ac7af78330f..0ab1708979e 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -1340,4 +1340,17 @@ rollback to x; commit; -- fail ERROR: insert or update on table "fktable2" violates foreign key constraint "fktable2_f1_fkey" DETAIL: Key (f1)=(2) is not present in table "pktable2". +-- +-- Test that we prevent dropping FK constraint with pending trigger events +-- +begin; +insert into fktable2 values(2); +alter table fktable2 drop constraint fktable2_f1_fkey; +ERROR: cannot ALTER TABLE "fktable2" because it has pending trigger events +commit; +begin; +delete from pktable2 where f1 = 1; +alter table fktable2 drop constraint fktable2_f1_fkey; +ERROR: cannot ALTER TABLE "pktable2" because it has pending trigger events +commit; drop table pktable2, fktable2; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 37b1f2c46ab..abab04c8bac 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -965,4 +965,17 @@ delete from fktable2; rollback to x; commit; -- fail +-- +-- Test that we prevent dropping FK constraint with pending trigger events +-- +begin; +insert into fktable2 values(2); +alter table fktable2 drop constraint fktable2_f1_fkey; +commit; + +begin; +delete from pktable2 where f1 = 1; +alter table fktable2 drop constraint fktable2_f1_fkey; +commit; + drop table pktable2, fktable2;