Eu tenho um procedimento armazenado que é chamado em um bloco insert-exec:
insert into @t
exec('test')
Como posso lidar com exceções geradas no procedimento armazenado e ainda continuar processando?
O código a seguir ilustra o problema. O que eu quero fazer é retornar 0 ou -1 dependendo do sucesso ou falha da exec()
chamada interna:
alter procedure test -- or create
as
begin try
declare @retval int;
-- This code assumes that PrintMax exists already so this generates an error
exec('create procedure PrintMax as begin print ''hello world'' end;')
set @retval = 0;
select @retval;
return(@retval);
end try
begin catch
-- if @@TRANCOUNT > 0 commit;
print ERROR_MESSAGE();
set @retval = -1;
select @retval;
return(@retval);
end catch;
go
declare @t table (i int);
insert into @t
exec('test');
select *
from @t;
Meu problema é o return(-1)
. O caminho do sucesso é bom.
Se eu deixar de fora o bloco try/catch no procedimento armazenado, o erro será gerado e a inserção falhará. No entanto, o que eu quero fazer é lidar com o erro e retornar um valor legal.
O código como está retorna a mensagem:
Msg 3930, Level 16, State 1, Line 6
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
Esta é talvez a pior mensagem de erro que encontrei. Parece realmente significar "Você não lidou com um erro em uma transação aninhada".
Se eu colocar o if @@TRANCOUNT > 0
, recebo a mensagem:
Msg 3916, Level 16, State 0, Procedure gordontest, Line 7
Cannot use the COMMIT statement within an INSERT-EXEC statement unless BEGIN TRANSACTION is used first.
Eu tentei brincar com instruções de transação begin/commit, mas nada parece funcionar.
Então, como posso fazer com que meu procedimento armazenado lide com erros sem abortar a transação geral?
Edite em resposta a Martin:
O código de chamada real é:
declare @RetvalTable table (retval int);
set @retval = -1;
insert into @RetvalTable
exec('
declare @retval int; exec @retval = '+@consulta+'; selecione @retval');
select @retval = retval from @RetvalTable;
Onde @query
está a chamada de procedimento armazenado. O objetivo é obter o valor de retorno do procedimento armazenado. Se isso for possível sem insert
(ou, mais especificamente, sem iniciar uma transação), seria ótimo.
Não consigo modificar os procedimentos armazenados em geral para armazenar o valor em uma tabela, porque há muitos deles. Um deles está falhando, e eu posso modificá-lo. Minha melhor solução atual é algo como:
if (@StoredProcedure = 'sp_rep__post') -- causing me a problem
begin
exec @retval = sp_rep__post;
end;
else
begin
-- the code I'm using now
end;
O erro na
EXEC
parte doINSERT-EXEC
extrato está deixando sua transação em estado condenado.Se você estiver
PRINT
noXACT_STATE()
blocoCATCH
, ele está definido como-1
.Nem todos os erros definirão o estado para isso. O seguinte erro de restrição de verificação passa para o bloco catch e é bem-
INSERT
sucedido.Adicionando isso ao
CATCH
blocoNão ajuda. Dá o erro
Eu não acho que haja alguma maneira de se recuperar de um erro assim, uma vez que aconteceu. Para o seu caso de uso específico, você não precisa de
INSERT ... EXEC
qualquer maneira. Você pode atribuir o valor de retorno a uma variável escalar e inseri-la em uma instrução separada.Ou é claro que você pode reestruturar o procedimento armazenado chamado para que ele não gere esse erro.