在我永无休止地用保存交易搬起石头砸自己的脚的过程中,我似乎找到了更多的方法来实现这个任务的目的。这次save transaction子句本身是不行的,但正是因为它我写了下面的代码。
考虑以下带有嵌套错误处理程序的完整示例:
begin try
begin try
select 'Step 1';
end try
begin catch
select 'Step 1 handler - handling ''' + error_message() + '''';
goto commit_and_exit;
end catch;
begin try
select 'Step 2';
raiserror('Step 2 error', 16, 1);
end try
begin catch
select 'Step 2 handler - handling ''' + error_message() + '''';
goto commit_and_exit;
end catch;
end try
begin catch
select 'Outer handler - handling ''' + error_message() + '''';
goto commit_and_exit;
end catch
commit_and_exit:
raiserror('Error raised for the caller to see', 16, 1);
据记载
GOTO
语句可用于跳转到同一TRY
orCATCH
块内的标签或离开TRY
orCATCH
块。
或者可以吗?
鉴于上面的代码,一个理智的程序员会认为输出将是
Step 1
Step 2
Step 2 handler - handling 'Step 2 error'
<Error raised for the caller to see>
事实上发生的事情是:
第 1
步第 2
步第 2 步处理程序 - 处理“第 2 步错误”
外部处理程序 - 处理“引发调用者看到的
错误”<引发调用者看到的错误>
逐步调试时,我可以看到控制完全离开 try/catch 块,然后引发错误,控制返回到最外层catch
块,该块执行,控制commit_and_exit:
再次进入,并执行最后一个块再次。
如果您有一些commit tran
s 或rollback trans
s under commit_and_exit:
,您将尝试提交 tran 两次。考虑到调用者可以启动外部事务这一事实,结果可想而知。
我还尝试end_of_outer:
在 outer 之前创建另一个标签 ,end try
以便控件try
“正常”离开 outer 块。有趣的是,这没有任何区别。
这到底是怎么回事,正确的做法是什么?
看起来 SQL Server 2012 之前的行为是一个错误。
我从这篇 Connect 文章中推断出这一点:
https://connect.microsoft.com/SQLServer/feedback/details/712003/sql-server-2012-error-in-exception-handling-mechanism
我当然可能是错的...