I have an after update trigger (Trigger A) on table A which can make changes to table B.
I also have an after update trigger (Trigger B) on table B, which makes no changes, but queries table A to some sanity checking on a denormalization.
So Trigger B can fire one of two ways:
In case 2, I get an ORA-04091: table name is mutating, trigger/function may not see it error. This seems correct.
I want to check within Trigger B if table A is "in a bad state" and early exit (the sanity checks wouldn't need to run in this case).
What is the best way to test this within my trigger? Just add an exception handler which swallows the exception? Is there anything more graceful?
You could have the trigger on A do something to alert the trigger on B that it doesn't need to fire. There are various wants to set up some state for a session. The simplest possible approach would be to do something like create a package with a boolean variable bypass_checks_on_b
that you set to TRUE
before you do the UPDATE
on A, set to FALSE
once the UPDATE
completes, and then check the state of this variable in your trigger on B before doing your validations. You could do something similar with a temporary table or a context as well rather than using a package. Less efficiently, you could potentially parse the call stack inside your trigger on B to see if the trigger on A is in the call stack but that would tend to be rather ugly.
I would be very cautious about this whole architecture, though. When you find that you have triggers on A that cause triggers on B to fire that would like to query A, it is almost always the case that you've put way too much logic in triggers and that you'd be much better served moving that logic into a stored procedure layer that can be called rather than the applications doing direct inserts or updates. When you push too much logic into triggers, you end up with a system that is very difficult to understand because it's not obvious from looking at the application code what sort of side effects various statements have. And you end up with very stateful code where you have many paths through a single piece of code depending on the caller. That almost certainly means that there will be states that you don't test or don't think of where you'll discover that your code does something unexpected. Between having a ton of state and having a code base with a ton of side-effects, you very quickly can build a code base that is essentially unmaintainable.