I thought about this topic for a while and came up with a very simple solution that I had not seen before, so I wanted to share this:
Since it is impossible to repeat the same error, you need to throw an error that is very easy to correlate with the original error, for example, adding a fixed number, for example 100000, to each system error.
After the new mapped messages are added to the database, any system error with a fixed offset of 100000 can be executed.
Here is the code for creating mapped messages (this needs to be done only once for the entire instance of SQL Server. Avoid collisions with other user messages by adding the appropriate offset, for example, 100000):
DECLARE messageCursor CURSOR READ_ONLY FOR select message_id + 100000 as message_id, language_id, severity, is_event_logged, [text] from sys.messages where language_id = 1033 and message_id < 50000 and severity > 0 DECLARE @id int, @severity int, @lang int, @msgText nvarchar(1000), @withLog bit, @withLogString nvarchar(100) OPEN messageCursor FETCH NEXT FROM messageCursor INTO @id, @lang, @severity, @withLog, @msgText WHILE (@@fetch_status <> -1) BEGIN IF (@@fetch_status <> -2) BEGIN set @withLogString = case @withLog when 0 then 'false' else 'true' end exec sp_addmessage @id, @severity, @msgText, 'us_english', @withLogString, 'replace' END FETCH NEXT FROM messageCursor INTO @id, @lang, @severity, @withLog, @msgText END CLOSE messageCursor DEALLOCATE messageCursor
And this is the code for creating the newly created error codes that have the offset correction from the source code:
SELECT @ErrorNumber = ERROR_NUMBER(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE() set @MappedNumber = @ErrorNumber + 100000; RAISERROR ( @MappedNumber, @ErrorSeverity, 1 );
There is one small caveat: in this case, you cannot provide the message yourself. But you can work around this by adding extra% s to the sp_addmessage call, or changing all the matching messages to your own template and providing the correct parameters in the raiseerror call. It is best to set all messages to the same template, for example, "% s (line:% d procedure:% s)% s", so you can provide the original message as the first parameter and add the real procedure and line and your own message like other parameters.
In the client, now you can perform all the usual exception handling, as the original message would be selected, you only need to remember about adding a correction offset. You can even handle original and repeated exceptions with the same code as this:
switch(errorNumber) { case 8134: case 108134: { } }
Thus, you donβt even need to know if this is a repeated or original error, it is always correct, even if you forgot to process your errors and missed the original error.
There are some improvements mentioned in other sections regarding raising messages that you cannot raise or declare that you cannot use. Those that stayed here to show only the essence of the idea.