Em minha busca sem fim para dar um tiro no próprio pé com uma transação salva, parece que encontrei ainda mais maneiras de cumprir o propósito da missão. A própria cláusula de transação de salvamento desta vez está fora de questão, mas é por causa dela que escrevi o código abaixo.
Considere o seguinte exemplo completo com manipuladores de erro aninhados:
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);
Está documentado que
GOTO
As instruções podem ser usadas para pular para um rótulo dentro do mesmo blocoTRY
ouCATCH
ou para sair de um blocoTRY
ouCATCH
.
Ou pode?
Dado o código acima, um programador sensato descobriria que a saída seria
Etapa 1
Etapa
2 Manipulador da etapa 2 - manipulação de 'Erro na etapa 2'
<Erro gerado para o chamador ver>
Na verdade o que está acontecendo é:
Etapa 1
Etapa 2
Etapa 2 manipulador - tratamento de 'Erro de etapa 2'
Manipulador externo - tratamento de 'Erro gerado para o chamador ver'
<Erro gerado para o chamador ver>
Ao depurar passo a passo, posso ver que o controle deixa completamente os blocos try/catch, então um erro é gerado, o controle é retornado ao bloco mais externo catch
, esse bloco é executado, o controle vai para commit_and_exit:
novamente e esse bloco final é executado novamente .
Se você tiver alguns commit tran
s ou rollback trans
s abaixo commit_and_exit:
de , tentará confirmar o tran duas vezes. Com resultados imagináveis, dado que pode haver transações externas iniciadas pelo chamador.
Também tentei criar outro label, end_of_outer:
, logo antes do end try
, para que o controle saia do bloco externo try
"normalmente". Interessante o suficiente, isso não fez nenhuma diferença.
O que diabos está acontecendo e qual é a maneira correta de fazer isso?
Parece que o comportamento anterior do SQL Server 2012 é um bug.
Eu deduzo isso deste artigo do Connect:
https://connect.microsoft.com/SQLServer/feedback/details/712003/sql-server-2012-error-in-exception-handling-mechanism
posso estar errado claro...