Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 SQL Server Development (2000)
 Errors in Triggers - catch them if you can

Author  Topic 

Ogreite
Starting Member

5 Posts

Posted - 2006-04-11 : 05:53:12
Hi all, I have a question about recovering from errors in a trigger, I hope you can help me.

I have a situation where I need to record an audit for every modification (delete, update and insert) on certain tables. A stored procedure called from triggers seem to be ideal, but the catch is I dont want to interfere whatsoever with the original data modifications.

The problem arises when an error occurs in my trigger. If the trigger crashes, I loose the original data modifications! I dont want that to happen...if the trigger (or the SP that it calls) crashes, then too bad. I can live with loosing the audit trail, but I dont want it to affect anything else.

I havent been able to successfully try and catch the errors without the original data changes (that starts the trigger) getting lost or rolled back. I use the AFTER trigger type, and apparantly this is only fired after the triggering SQL statement is executed successfully. So basically at this point I want this stuff to get commited before I attempt auditting it, if possible, but I have read on the net that all errors terminate a trigger batch and roll back the transaction on the spot. I believe this will be tricky to do because I have also read that "each INSERT, UPDATE and DELETE statement is its own transaction in SQL Server, and the trigger is part of that transaction".

This is my basic layout of my tigger and the SP that it calls:

CREATE TRIGGER [TestTrigger] ON [dbo].[someTable]

AFTER insert, update, delete AS
BEGIN

SET NOCOUNT ON;

BEGIN TRY

EXEC [dbo].[TestProcedure] 'someValue'

END TRY

BEGIN CATCH

raiserror('Error in Trigger!', 16, -1)

END CATCH;

END;


CREATE PROCEDURE [dbo].[TestProcedure]
(
@someValue varchar(50)
)
AS

BEGIN

BEGIN TRY


END TRY

BEGIN CATCH

raiserror('Error in TestProcedure!', 16, -1)

END CATCH;

END;


Does anyone have any ideas how I can solve this?

rmason
Starting Member

12 Posts

Posted - 2006-04-11 : 08:33:25
If I've got this right, an After trigger will execute the trigger code AFTER the insert/update/delete on the parent table has occured but you are still within the same transaction. Therefore if your trigger code fails for some reason then the WHOLE transaction has to roll back including the insert/update/delete. The only way you could avoid this is if you were to submit the statement without it being in a transaction, that way the insert/update/delete should remain even if there is a subsequent error.

Personally I would keep the whole thing ina transaction. The likelyhood of an error occuring while writing to an audit table has to be very slight and in my opinion if that was to error then it show's there's something majorly wrong with my app and I'd want the whole thing to rollback.

Rob.
Go to Top of Page

Ogreite
Starting Member

5 Posts

Posted - 2006-04-11 : 08:54:40
Thanks for the reply, Rob.

quote:
Originally posted by rmason

If I've got this right, an After trigger will execute the trigger code AFTER the insert/update/delete on the parent table has occured but you are still within the same transaction. Therefore if your trigger code fails for some reason then the WHOLE transaction has to roll back including the insert/update/delete. The only way you could avoid this is if you were to submit the statement without it being in a transaction, that way the insert/update/delete should remain even if there is a subsequent error.



What do you mean by submitting the statement without it being in a transaction? Which statement?

quote:

Personally I would keep the whole thing ina transaction. The likelyhood of an error occuring while writing to an audit table has to be very slight and in my opinion if that was to error then it show's there's something majorly wrong with my app and I'd want the whole thing to rollback.
Rob.



Well, indeed I hope that the likelyhood of an error occuring while writing to my audit will be slight. But you cant predict everything...they invented try - catch blocks for a reason. My problem is that even by catching most errors, the whole transaction still gets either rolled back or aborted and I loose the data changes made by the user.

Perhaps there is no way around it, which is a shame realy because triggers make things like auditing very quick and easy and low maintenance.
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-04-11 : 13:01:49
So why is your trigger crashing? Triggers should be simple code, and thus relatively easy to debug. I have to say, I dislike the idea of calling a sproc from a trigger. A scalar function, maybe, but not a sproc. You end up with spaghetti code. All the logic for a trigger should be encapsulated within the trigger, making the table somewhat like an OOP object which is sufficient and complete unto itself.
Go to Top of Page

Ogreite
Starting Member

5 Posts

Posted - 2006-04-11 : 19:16:15
quote:
Originally posted by blindman

So why is your trigger crashing?



Its not. I have try / catch blocks in the trigger and the nested stored procedure to make sure if there IS a crash, it is handled and does not affect the modification of the table. To test it I have done stuff like insert a devide by zero to force a crash, and this is how I know my transaction batch is getting mashed, thereby loosing the table modification, regardless of my try catch blocks.

quote:

Triggers should be simple code, and thus relatively easy to debug.



I agree!! But then you say:

quote:

All the logic for a trigger should be encapsulated within the trigger, making the table somewhat like an OOP object which is sufficient and complete unto itself.



Hmmm. One of the core priniciples of Object Orientation design is encapsulation and code re-use. However you are suggesting that I avoid using a stored procedure and have all my code in the trigger. So the same code that deals with this auditing would then appear in every trigger on every table that needs auditing. If you then need to modify a trigger and its auditting code, you have to modify every single trigger. Im sorry, but thats not OO. Thats why I have the SP, which contains all the shared code that deals with the auditing process.

Go to Top of Page

jen
Master Smack Fu Yak Hacker

4110 Posts

Posted - 2006-04-11 : 20:47:31
do you mean something like this?

exec @run=sprocname(parameters...)
if @run!=0
raiseerror...

but i agree on creating it on the trigger and if you're incorporating dynamic sql in the sproc, this actually beats the purpose of optimization via execution plan, so you lose that benefit

creating it on the trigger reduces the risk that someone might just modify the sproc called within

that's just me, your requirements might be different

--------------------
keeping it simple...
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-04-12 : 00:07:01
quote:
Originally posted by Ogreite
Hmmm. One of the core priniciples of Object Orientation design is encapsulation and code re-use. However you are suggesting that I avoid using a stored procedure and have all my code in the trigger. So the same code that deals with this auditing would then appear in every trigger on every table that needs auditing.
Hmmm...have to <slightly> disagree with your definition. The benefit of code reuse in OOP is incidental to the goal of object reuse. And in any code, first and foremost the code and objects must be robust. Regardless, I would not try to push the TSQL "OOP" analogy too far. TSQL is by no means an object-oriented language. My reference was too OOP's principle of self-sufficient "black-box" objects. So instead of thinking of the trigger as an object, think of it as a method of the table. It's scope of control should be limited to the table.
FYI, triggers are very useful tools and I use them frequently, but any of these gurus will tell you there are few faster ways to hose up your database than with improperly written or poorly controled triggers.
Go to Top of Page

Ogreite
Starting Member

5 Posts

Posted - 2006-04-12 : 04:43:35
Thanks for the discussion guys.

The fact that I am using an SP here (called from within the trigger) is actually irrelevant to my problem.
Jen, you are right that this solution of mine is not the best in terms of optimization, but unfortunately I am forced to go down this route. I don’t pay my wages, my boss does ;)
blindman, I respect your opinions but I think I’ll have to agree to disagree on that one.

For the time being lets forget about the stored proc please. I was really after some more information about recovering from errors in a trigger. I want to make it robust so that if it crashes, I can report the error and continue, but also so that I do not loose the modifications that caused the trigger to fire in the first place.
I can’t see why this wouldn’t be possible; after all, the AFTER trigger is fired once the transaction has been successfully applied. I guess I want a trigger that fires after a commit. Is there such a thing?

If I force my trigger to crash for testing, I get the following message: “Transaction doomed in trigger. Batch has been aborted.”
That means I have lost the changes that originally caused my trigger to fire e.g. inserting a new record into the table.

So currently if your trigger crashes, you can’t catch the error and your transaction is always doomed. Is there no way around this?
Go to Top of Page

holistic
Starting Member

4 Posts

Posted - 2009-02-20 : 13:29:33
ever found a solution to this isse?
Go to Top of Page

panchimartin
Starting Member

2 Posts

Posted - 2011-03-29 : 12:17:45
You can commit the transaction in the trigger, before you do whatever you want to do. I tried in a test env and it seems to work for the transaction, but what gets ended is the batch:

Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted

(2 years later...)
=)
Go to Top of Page

panchimartin
Starting Member

2 Posts

Posted - 2011-03-29 : 12:34:36
an addition to my previous post:
after transaction is commited, tables inserted and deleted are not available any more!!!
if you need them, you may try something like:

select * into #i from inserted
select * into #d from deleted
commit tran
-- your code here that use #i and #d in place of inserted and deleted

It works... but it is weird so i'm not sure i would use it.

any comments?
Go to Top of Page

Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)

7020 Posts

Posted - 2011-03-30 : 11:59:44
quote:
Originally posted by Ogreite

Thanks for the discussion guys.

The fact that I am using an SP here (called from within the trigger) is actually irrelevant to my problem.
Jen, you are right that this solution of mine is not the best in terms of optimization, but unfortunately I am forced to go down this route. I don’t pay my wages, my boss does ;)
blindman, I respect your opinions but I think I’ll have to agree to disagree on that one.

For the time being lets forget about the stored proc please. I was really after some more information about recovering from errors in a trigger. I want to make it robust so that if it crashes, I can report the error and continue, but also so that I do not loose the modifications that caused the trigger to fire in the first place.
I can’t see why this wouldn’t be possible; after all, the AFTER trigger is fired once the transaction has been successfully applied. I guess I want a trigger that fires after a commit. Is there such a thing?

If I force my trigger to crash for testing, I get the following message: “Transaction doomed in trigger. Batch has been aborted.”
That means I have lost the changes that originally caused my trigger to fire e.g. inserting a new record into the table.

So currently if your trigger crashes, you can’t catch the error and your transaction is always doomed. Is there no way around this?




Your trigger is part of the transaction, not something that happens after the transaction is committed.

Just make sure your code is so bulletproof that you don't get errors and you will be fine.

As a general rule a trigger should be very simple, so calling a stored procedure in a trigger is usually a very bad idea. Too many things can go wrong, and the requirement to call it multiple times in a loop to process each row can really slow things down.












CODO ERGO SUM
Go to Top of Page
   

- Advertisement -