WL#17024: Activate triggers on referencing tables during foreign key CASCADE
Affects: Server-9.x
—
Status: Complete
Currently, MySQL does not support the execution of triggers on child tables when rows are modified as a result of cascading UPDATE or DELETE foreign key actions. Goal of this WL is to enable trigger(s) execution on child tables during cascading UPDATE or DELETE operations initiated by Foreign key constraints. With the implementation of Foreign Key Handling in SQL layer, which brought foreign enforcement and cascades to the server (SQL) layer, firing triggers during such cascades is now technically feasible. This WL should ensure that both BEFORE and AFTER triggers on child tables are executed for rows modified through cascading action by SQL layer foreign key handling. Note: Trigger execution for CASCADE actions in Storage Engine's Foreign Key handling is out of this WL scope. REFERENCES ========== BUG#11472 - Triggers not executed following foreign key updates/deletes
Functional Requirements:
--------------------------
FR 1: Triggers must be fired for foreign key CASCADE action in SQL Layer
Foreign Key Handling.
FR 1.1: For ON DELETE CASCADE action, during cascaded DELETE operation
on child tables, BEFORE and AFTER triggers for DELETE action defined on
child tables must be fired for each row.
FR 1.2: For ON UPDATE CASCADE action, during cascaded UPDATE operation
on child tables, BEFORE and AFTER triggers for UPDATE action defined on
child tables must be fired for each row.
FR 1.3: For ON DELETE SET NULL action, during cascaded UPDATE operation
to SET NULL on child tables, BEFORE and AFTER triggers for UPDATE action
defined on child tables must be fired for each row.
FR 1.4: For ON UPDATE SET NULL action, during cascaded UPDATE operation
to SET NULL on child tables, BEFORE and AFTER triggers for UPDATE action
defined on child tables must be fired for each row.
FR 2: New system variable "enable_cascade_triggers" must be introduced
to enable or disable triggers execution for foreign key cascade actions.
Properties of variable:
Name: enable_cascade_triggers
Data Type: Boolean
Default value: FALSE
Scope: SESSION
Mutable Type: Dynamic
Command Line Option: Yes
Persisted: Yes
SET_VAR Hint: Not Applicable
BinLog: Yes
Note: This variable must be marked for deprecation and should be removed
in the future releases.
FR 2.1: When innodb_native_foreign_keys = ON, enable_cascade_triggers
variable must not have any effect; foreign keys and cascades remain
handled inside InnoDB with no SQL-layer cascade-trigger firing.
Note:A warning must be reported if enable_cascade_triggers is set
to ON.
FR 2.2: When innodb_native_foreign_keys = OFF and
enable_cascade_triggers = ON, triggers on rows affected by SQL-layer
FK cascades must be fired.
FR 2.3: When innodb_native_foreign_keys = OFF and
enable_cascade_triggers = OFF, child table triggers must not fire
during cascade changes.
FR 3: Parent table containing multiple foreign keys:
If parent table contains multiple foreign keys to different child tables,
corresponding triggers should be fired for all child tables if defined.
FR 4: Child table containing multiple triggers:
FR 4.1: If child table contains multiple triggers, all triggers on
child table must be fired.
FR 4.2: Triggers must be executed in the defined execution order.
FR 5: Trigger execution order in case of multi level foreign keys:
FR 5.1: In case of multi level foreign keys, BEFORE triggers must
be executed in top-down cascade order: BEFORE trigger of parent table
must be executed first, then BEFORE trigger of child must be executed.
FR 5.2: In case of multi level foreign keys, AFTER triggers must be
executed in bottom-up cascade order: AFTER trigger of child table
must be executed first, then AFTER trigger of parent must be executed.
FR 6: NEW and OLD aliases.
FR 6.1: In UPDATE trigger fired due to cascade actions, NEW and OLD
aliases must refer to the post-cascade and pre-cascade values
respectively. This applies to both SET NULL and UPDATE CASCADE actions.
FR 6.2: In DELETE trigger fired due to cascade actions, OLD alias
must refer the value being deleted.
FR 7: Error handling and atomicity
FR 7.1: If any trigger execution (BEFORE/AFTER at any cascade level)
fails or signals an error, the entire statement must fail and all
cascaded changes must be rolled back.
FR 7.2: For multi-level cascades, errors at any depth must roll back
changes across all affected levels.
FR 8: Recursion and loop prevention
FR 8.1: For self-referencing foreign keys, triggers must fire on rows
in the same table affected by the cascade.
FR 8.2: Circular foreign key must succeed and triggers should be fired
only once for tables involved in circular foreign key.
FR 8.3: Cascade-induced trigger execution must not allow infinite loops.
If trigger definition on a child attempts to modify the parent table
(or otherwise re-enters the same cascade path), the server must
detect and report an error.
FR 8.4: If cascades exceed the maximum depth (i.e. 15), then error
ER_FK_DEPTH_EXCEEDED must be reported and the statements executed
must be rolled back.
FR 8.5: If number of tables that can participate collectively in all
cascade chains exceeds max limit (i.e. 30) during a single statement
execution, then an error must be reported.
FR 8.6: Multi-level, trigger-initiated cascades must function
correctly. When a child table trigger fires during a FK cascade,
it may initiate further FK cascades to other related tables. It must
ensure referential integrity and proper execution order for all
triggered and cascading actions.
FR 9: Binary logging
Source must log all the changes performed inside cascade-induced triggers
of child table.
FR 10: Replication behaviour
FR 10.1: In Row Based Replication mode, triggers must not be executed on
the replica as a consequence of cascades applied by the applier thread.
FR 10.2: In Statement Based Replication mode, the binlog applier thread
in replica node must use the source setting of enable_cascade_triggers
variable.
FR 10.2.1:Source(OFF) -> Replica(OFF)
If both the source and replica nodes uses enable_cascade_triggers=OFF,
applier must not execute child table triggers during FK cascade.
FR 10.2.2:Source(OFF) -> Replica(ON)
If source node uses enable_cascade_triggers=OFF and replica uses
enable_cascade_triggers=ON, applier must not execute child triggers
during FK cascade.
FR 10.2.3:Source(ON) -> Replica(OFF)
If source node uses enable_cascade_triggers=ON and replica uses
enable_cascade_triggers=OFF, applier must execute child triggers
during FK cascade.
FR 10.2.4:Source(ON) -> Replica(ON)
If both the source and replica nodes uses enable_cascade_triggers=ON,
applier must execute child table triggers during FK cascade.
FR 11: SQL Statement Interactions
FR 11.1: INSERT...ON DUPLICATE KEY UPDATE on the parent that leads to
UPDATE CASCADE must fire UPDATE triggers on affected child rows.
FR 11.2: REPLACE on the parent that leads to a DELETE followed by INSERT
statement must fire DELETE triggers on affected parent rows
and cascade DELETE triggers on children.
Note: The subsequent parent INSERT does not re-insert child rows unless
the DML explicitly does so (documented behaviour).
FR 11.3: Foreign key checks on tables used by trigger bodies must follow
standard foreign_key_checks semantics.
FR 11.4: LOCK TABLE must work with foreign key cascade trigger firing.
FR 11.5: Child table triggers calling FUNCTION referring to parent table
must return error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG.
Non-functional requirements (NFRs):
-----------------------------------
NFR 1: No performance difference must be observed if triggers are not
defined on the child table or when enable_cascade_triggers=0 or when
using InnoDB native foreign key handling.
The primary goal of this WL is to execute triggers on child tables when
rows are modified as a result of cascading UPDATE and DELETE actions
initiated by foreign key constraint with SQL layer FK handling (i.e with
innodb_native_foreign_keys = OFF).
Executing trigger for FK handling in Storage Engines is out of this WL
scope.
1. Enabling/Disabling cascade-induced trigger execution:
--------------------------------------------------------
A new SESSION scope system variable, 'enable_cascade_triggers' is
introduced as part of this WL. Properties of this variable:
Name: enable_cascade_triggers
Data Type: Boolean
Default value: FALSE
Scope: SESSION
Mutable Type: Dynamic
Command Line Option: Yes
Persisted: Yes
SET_VAR Hint: Not Applicable
BinLog: Yes
With SQL FK handling enabled, cascade-induced child triggers are
not executed by default. Users can set enable_cascade_triggers to
TRUE to ensure that triggers on child tables are executed during
foreign key cascades performed by the SQL engine.
Note: "enable_cascade_triggers" variable is marked for deprecation
and will be removed in the future releases.
Whenever 'enable_cascade_triggers' is set (either true or false), a
deprecation warning will be emitted, indicating that this variable will
be removed in a future release. If set to false (or 0), an additional
warning is reported to inform the user that triggers will not fire on
the child table during foreign key cascades.
2. Cascade-induced Trigger execution Semantics:
-----------------------------------------------
Cascade-induced triggers are executed as part of the same DML statement
resulting in cascade action. The trigger execution sequence on the child
table for various cascade actions is as follows:
ON DELETE CASCADE:
Fire BEFORE DELETE trigger on the child table.
Delete the child row.
Fire AFTER DELETE trigger on the child table.
ON UPDATE CASCADE | SET NULL and ON DELETE SET NULL:
Fire BEFORE UPDATE trigger on the child table.
Update the child row (to the new parent value or NULL).
Fire AFTER UPDATE trigger on the child table.
When BEFORE triggers are fired during cascade, OLD values will be
available in the trigger for DELETE operation. For update, both OLD
and NEW values will be available.
2.1 Multi Level Cascades and Trigger Time(BEFORE/AFTER):
--------------------------------------------------------
For cascades spanning multi-level, the triggers are executed in the
following order:
2.1.a) BEFORE trigger: Triggers with BEFORE time type are executed
in the top-down order. BEFORE trigger defined on the parent table
is executed first and then the BEFORE trigger from child table is
executed.
2.1.b) AFTER trigger: Triggers with AFTER time type execute in the
bottom-up order. AFTER trigger defined on the child table is
executed first and then the AFTER trigger from parent table is
executed.
2.2 Trigger execution order:
----------------------------
If multiple triggers are defined on the child table, then while
executing cascade-induced triggers, triggers are executed in the
defined order.
3. FK_MAX_TABLES_IN_CASCADE_CHAINS:
-----------------------------------
Cascading operations across multiple levels are limited by
FK_MAX_CASCADE_DEPTH (default: 15). However, when triggers are executed
as a result of cascading actions, multiple cascade chains can be
created within a single statement. This can lead to increased memory
usage.
To mitigate excessive memory consumption, FK_MAX_TABLES_IN_CASCADE_CHAINS
is introduced (default: 30). This constant sets the maximum number of
tables that can participate collectively in all cascade chains during
a single statement.
If the total number of tables involved in cascading chains exceeds
this limit in a statement, an error will be reported and the operation
will be aborted.
4. Behaviour with foreign_key_checks variable:
----------------------------------------------
Cascades are performed only when FK checks are enforced for the
statement. If foreign_key_checks=0, neither enforcement nor cascades
occur, and no triggers on child table is fired.
Existing behaviour for skipping foreign key checks on binlog events
generated during cascade operation remains unchanged.
5. Import/Export:
-----------------
No Impact, as cascades are applicable only to UPDATE and DELETE
statements. Importing from logical dump containing mostly INSERT
operations, so it will not be affected.
6. In-place Upgrade:
---------------------
No Impact, as enable_cascade_triggers default value is FALSE.
7. Replication:
---------------
7.1 Row based replication:
--------------------------
On replica nodes, worker threads explicitly set enable_cascade_triggers=0
while applying row binlogs, which ensures triggers do not fire for
cascades resulting from row based replication.
7.2 Statement based replication:
-------------------------------
The binlog applier thread in replica node uses the source setting of
enable_cascade_triggers variable.
With enable_cascade_triggers=ON/OFF, behavior will be as below
Source(OFF) -> Replica(OFF): Child table triggers are not executed
Source(OFF) -> Replica(ON) : Child table triggers are not executed.
Source(ON) -> Replica(OFF): Child table triggers are executed on both
source and replica.
Source(ON) -> Replica(ON) : Child table triggers are executed on both
source and replica.
7.3 Replication Interoperability(SQL FK vs InnoDB FK):
-------------------------------------------------------
Child table triggers on FK cascade operations are executed only with SQL
FK(i.e. innodb_native_foreign_keys=OFF).
If source and replica use different FK handling modes, behavior depends
on replication format.
Row based replication: Replica applies changes using the same FK
handling method as source. When using SQL FK with
enable_cascade_triggers, triggers are not executed on replica.
Events from the source are applied as is on replica. So row based
replication works as expected.
Statement based replication: With statement based replication, applier
threads uses its local FK handling (i.e. innodb_native_foreign_keys)
and source setting of enable_cascade_trigger variable.
With different FK handling between source and replica,
enable_cascade_trigger behavior for statement based replication is as below,
1. Source (SQL FK, enable_cascade_trigger=OFF) -> Replica (InnoDB FK)
Child table triggers are *not* executed on both Source and Replica.
2. Source (InnoDB FK, enable_cascade_trigger = OFF) -> Replica (SQL FK)
Child table triggers are *not* executed on both Source and Replica.
3. Source (InnoDB FK, enable_cascade_trigger=ON) -> Replica (SQL FK)
Child table triggers are *not* executed on both Source and Replica.
Variable enable_cascade_trigger is replicated with value OFF in
this case.
4. Source (SQL FK, enable_cascade_trigger=ON) -> Replica (InnoDB FK)
Child table triggers are executed on Source but *not* executed on
the replica. This is not a recommended configuration.
Case 4 is not a recommended configurations, but if deployment
ends up in this configuration then, following steps should be used
to recover from it.
1. Set source to read-only.
2. Wait until all events are applied at the replica.
3. Restart source and replica with a consistent FK handling mode:
both SQL FK (innodb_native_foreign_keys=OFF), or
both InnoDB FK (innodb_native_foreign_keys=ON).
7.4 OLD→NEW Replication:
------------------------
This replication setup is safe as enable_cascade_triggers=OFF on NEW,
which ensures triggers do not fire for cascades resulting from
replication. So there won't be any data inconsistency.
8. Performance impact:
------------------------
No performance difference is observed if triggers are not defined on the
child table or when enable_cascade_triggers=0 or when using InnoDB native
foreign key handling.
If triggers are definer, then enabling this feature introduces additional
trigger dispatch overhead per cascaded row on affected child tables. The
performance impact is proportional to the number of child rows affected by
cascades and trigger complexity.
Copyright (c) 2000, 2026, Oracle Corporation and/or its affiliates. All rights reserved.