Não sou fã de gatilhos e SQL dinâmico, porém, o que estou trabalhando requer ambos.
CREATE TRIGGER [dbo].[GenerateDynamicFormItemViews] ON [dbo].[tblFormItems] AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @DatabaseName NVARCHAR(100)
DECLARE @FormItemID INT
DECLARE db_cursor CURSOR FOR
SELECT SourceID,FormItemID from Inserted
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @DatabaseName, @FormItemID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
EXEC spAddEditFormItemView @FormItemID, @DatabaseName WITH RESULT SETS NONE
END TRY
BEGIN CATCH
INSERT INTO AdminErrorLog(SourceID, ErrorNumber, ErrorState, ErrorSeverity, ErrorProcedure, ErrorLine, ErrorMessage, ErrorDateTime)
SELECT @DatabaseName, ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_PROCEDURE(), ERROR_LINE(), ERROR_MESSAGE(), GETDATE()
END CATCH
FETCH NEXT FROM db_cursor INTO @DatabaseName, @FormItemID
END
CLOSE db_cursor
DEALLOCATE db_cursor
END
Eis aqui o que está acontecendo.
- Uma tarefa de replicação do AWS DMS está replicando dados em lotes de 10.000. A implementação da replicação é uma caixa preta, mas parece haver transações aninhadas nas conexões.
- EXEC spAddEditFormItemView chama um procedimento armazenado que executa o SQL dinâmico e gera erros em um registro.
- O bloco CATCH nunca é executado
- O erro nunca chega ao AdminErrorLog.
- O AWS Batch apresenta erros e nunca é confirmado e toda a tarefa falha.
- Em eventos estendidos, o seguinte erro sql é detectado. Este é o erro que falhou na tarefa. Eu li sobre condições em que os comandos não podem estar em estado de confirmação ou reversão, mas realmente não entendi totalmente esse conceito.
mensagem: A transação atual não pode ser confirmada e não pode suportar operações gravadas no arquivo de log. Reverta a transação.
gravidade: 16
Alguém pode explicar por que o bloco catch não está sendo capturado e por que o aplicativo cliente está recebendo, o que eu acho, uma exceção sql e revertendo tudo. Meu palpite é que as exceções dinâmicas de SQL estão sendo tratadas de maneira diferente e a transação de gatilho implícita está sendo revertida em vez de capturar o erro. Além disso, alguém sabe uma maneira de evitar que a exceção seja propagada?
Aposto que a solução mais segura seria modificar o gatilho para amontoar os comandos sql em uma tabela e, em seguida, fazer com que um trabalho do agente sql procure comandos para serem executados a cada minuto ou mais.
EDIT - Adicionando procedimento para recriar: No SSSM:
EXEC [spAddEditFormItemView] 30032,'VP_BENCHMARKING_V05'
Comandos concluídos com sucesso.
Prazo de conclusão: 2023-11-27T16:11:18.1762097-05:00
No gatilho ele força um rollback com uma mensagem de erro horrível.
ALTER PROCEDURE [dbo].[spAddEditFormItemView] (
@FormItemID int,
@Schema nvarchar(100)
)
AS
DECLARE @SQL NVARCHAR(MAX) = 'e2e2e2e2e'
BEGIN TRY
EXEC (@SQLCommand)
END TRY
BEGIN CATCH
DECLARE @X INT
END CATCH
EDIT: agora posso duplicar no SSMS.
O gatilho é executado como parte da transação. Portanto, independentemente de o bloco catch ser executado ou não, a transação está condenada e nunca poderá ser confirmada na tabela AdminErrorLog.
Portanto, realmente não importa se o bloco CATCH é executado. Mas isso não acontece porque erros no escopo do gatilho geralmente abortam o lote. Veja o artigo clássico de Erland Sommarskog sobre tratamento de erros TSQL aqui: https://www.sommarskog.se/error-handling-I.html#triggercontext