Fix GetForeignKey*Triggers for self-referential FKs
authorAlvaro Herrera <[email protected]>
Fri, 9 Sep 2022 10:22:20 +0000 (12:22 +0200)
committerAlvaro Herrera <[email protected]>
Fri, 9 Sep 2022 10:22:20 +0000 (12:22 +0200)
Because of inadequate filtering, the check triggers were confusing the
search for action triggers in GetForeignKeyActionTriggers and vice-versa
in GetForeignKeyCheckTriggers; this confusion results in seemingly
random assertion failures, and can have real impact in non-asserting
builds depending on catalog order.  Change these functions so that they
correctly ignore triggers that are not relevant to each side.

To reduce the odds of further problems, do not break out of the
searching loop in assertion builds.  This break is likely to hide bugs;
without it, we would have detected this bug immediately.

This problem was introduced by f4566345cf40, so back to 15 where
that commit first appeared.

Author: Amit Langote <[email protected]>
Discussion: https://postgr.es/m/20220908172029[email protected]
Discussion: https://postgr.es/m/4104619.1662663056@sss.pgh.pa.us

src/backend/commands/tablecmds.c

index 53b0f3a9c1e03b005d6103ccef8111d1f76d7833..e3233a8f385557791f2d179dec50d3f49a9672eb 100644 (file)
@@ -10581,6 +10581,9 @@ GetForeignKeyActionTriggers(Relation trigrel,
            continue;
        if (trgform->tgrelid != confrelid)
            continue;
+       /* Only ever look at "action" triggers on the PK side. */
+       if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
+           continue;
        if (TRIGGER_FOR_DELETE(trgform->tgtype))
        {
            Assert(*deleteTriggerOid == InvalidOid);
@@ -10591,8 +10594,11 @@ GetForeignKeyActionTriggers(Relation trigrel,
            Assert(*updateTriggerOid == InvalidOid);
            *updateTriggerOid = trgform->oid;
        }
+#ifndef USE_ASSERT_CHECKING
+       /* In an assert-enabled build, continue looking to find duplicates */
        if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
            break;
+#endif
    }
 
    if (!OidIsValid(*deleteTriggerOid))
@@ -10636,6 +10642,9 @@ GetForeignKeyCheckTriggers(Relation trigrel,
            continue;
        if (trgform->tgrelid != conrelid)
            continue;
+       /* Only ever look at "check" triggers on the FK side. */
+       if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
+           continue;
        if (TRIGGER_FOR_INSERT(trgform->tgtype))
        {
            Assert(*insertTriggerOid == InvalidOid);
@@ -10646,8 +10655,11 @@ GetForeignKeyCheckTriggers(Relation trigrel,
            Assert(*updateTriggerOid == InvalidOid);
            *updateTriggerOid = trgform->oid;
        }
+#ifndef USE_ASSERT_CHECKING
+       /* In an assert-enabled build, continue looking to find duplicates. */
        if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
            break;
+#endif
    }
 
    if (!OidIsValid(*insertTriggerOid))