]> granicus.if.org Git - postgresql/commitdiff
Check for pending trigger events on far end when dropping an FK constraint.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 25 Nov 2016 18:44:47 +0000 (13:44 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 25 Nov 2016 18:44:47 +0000 (13:44 -0500)
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>

src/backend/commands/tablecmds.c
src/test/regress/expected/foreign_key.out
src/test/regress/sql/foreign_key.sql

index 41ef36beea2a8a0970b7dc65949cc4edf22db004..2f5bde60539993e4dd89f29daa79c7c6af1f89f3 100644 (file)
@@ -7718,6 +7718,24 @@ ATExecDropConstraint(Relation rel, const char *constrName,
 
                is_no_inherit_constraint = con->connoinherit;
 
+               /*
+                * 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
                 */
index 46a30b44289965ccf4f4ef16b9c9479fda6500e3..fef072eddfa5e3870197a091cc415a2e9d573791 100644 (file)
@@ -1401,4 +1401,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;
index cc36ab5bafcbd36d241a923f949898d2f626bf26..5f19dad03cd7c3122268345ef1480988a27e33f0 100644 (file)
@@ -1041,4 +1041,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;