CATCH 块在不应触发时被触发

GSe*_*erg 7 sql-server-2008 stored-procedures t-sql error-handling

在我永无止境地寻求通过保存交易射击自己的过程中,我似乎找到了更多实现任务目的的方法。这次的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);
Run Code Online (Sandbox Code Playgroud)

据记载

GOTO语句可用于跳转到同一个TRYCATCH块内的标签或离开一个TRYCATCH块。

或者可以吗?

鉴于上面的代码,一个理智的程序员会认为输出将是

第 1
步第 2
步第 2步处理程序 - 处理“第 2 步错误”
<引发的错误供调用者查看>

事实上,正在发生的事情是:

第 1
步第 2
步第 2步处理程序 - 处理“第 2 步错误”
外部处理程序 - 处理“引发的错误供调用者查看”
<引发的错误供调用者查看>

一步一步调试的时候,可以看到控制完全离开了try/catch块,然后报错,控制返回到最外面的catch块,那个块执行,控制commit_and_exit:再次执行,最后一个块被执行再次

如果您有一些commit trans 或rollback transs commit_and_exit:,您将尝试提交 tran 两次。考虑到可能存在由调用者启动的外部事务这一事实,结果可想而知。

我还尝试在外部块end_of_outer:之前创建另一个标签 ,end try以便控件try“正常”离开外部块。有趣的是,这没有任何区别。

这到底是怎么回事,正确的做法是什么?

gbn*_*gbn 10

看起来 SQL Server 2012 之前的行为是一个错误。

我从这篇 Connect 文章中推断出这一点:

https://connect.microsoft.com/SQLServer/feedback/details/712003/sql-server-2012-error-in-exception-handling-mechanism

我当然可能是错的...