]> 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:48 +0000 (13:44 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 25 Nov 2016 18:44:48 +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 4854385cf1cda26ffbdaa7b621f9db1c06c49ca4..97e7d140ba7c5c5508205a86ce944da1d7c151a8 100644 (file)
@@ -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
                 */
index ac7af78330f2739ddb5857658b14cbf272e94038..0ab1708979e7f72f65cc6e69782ce205c26a74cf 100644 (file)
@@ -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;
index 37b1f2c46ab6b6ce0c5e2e032821b6de331cfe17..abab04c8baca9acacc509c982032e58aa6308e3a 100644 (file)
@@ -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;