From 867c8adb9566cda197377be496d48926ac7945b0 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 19 Dec 2021 17:59:17 -0500 Subject: [PATCH 1/4] Trying to figure out why transaction is in an uncommittable state. --- Experiments/Debugging tSQLt.tdf | Bin 0 -> 2986 bytes ...ry.sql => SM query - read trace table.sql} | 0 Experiments/StartHere 2021-12-20.sql | 353 ++++++++++++++++++ Source/Run_Methods.sql | 10 +- Tests/Run_Methods_Tests.class.sql | 29 +- 5 files changed, 381 insertions(+), 11 deletions(-) create mode 100644 Experiments/Debugging tSQLt.tdf rename Experiments/{SM query.sql => SM query - read trace table.sql} (100%) create mode 100644 Experiments/StartHere 2021-12-20.sql diff --git a/Experiments/Debugging tSQLt.tdf b/Experiments/Debugging tSQLt.tdf new file mode 100644 index 0000000000000000000000000000000000000000..86a69c6de0213fa509522a48909afff92bc7a04b GIT binary patch literal 2986 zcmeHJ+iFum6kVp)HkT%~B(X-Dq%A^wJjPQ~nzY1Nlwuzg6~92LML`g>`0BH7euIdA z;9XGvLH!0_`~f4@wP(&WlVqmUf-hpqNuaa$T5DhDZ2x+aNa8Z?U<3DY4-fckVhdGV z$2DA1zqfeu5c2*1^LH|k#kV0rzRT=y;0Awht6FR!#3B~>)cN=Ce|~hnckpw2ZX82M z;~+9Pgb|D)i4?|=MGhs*VxAePqlHzPRzJ}+gFFw5C}0vtQAUMEbu_T7Ov29&v@DH_ zG&Xo=x`>Z)fDS?~ltq$G(yU5aLt6aG!xj%Gc(B5M&y(mp)#Sg>v(7`Urh=wcP$XnB zR-q$GDWg37wOl)qH!5zq9G_uB&d_28(hQ=S*RO zUHE0VZEn-|ntA<{huKb+UOin6CSh8I07-kaLJw3|LAj@7z3#!fvqegnw%XBDIk@-- z#?4GDOqO(aq1h3drC=i4;Tdzfx~F_~-je{ENg8iTMP#^i8sZ#2_dx8e5fn$dcn>7_W;eqvHjaopbeIj*R!eA_bzDNHlg@xB Hz#5<54Egww literal 0 HcmV?d00001 diff --git a/Experiments/SM query.sql b/Experiments/SM query - read trace table.sql similarity index 100% rename from Experiments/SM query.sql rename to Experiments/SM query - read trace table.sql diff --git a/Experiments/StartHere 2021-12-20.sql b/Experiments/StartHere 2021-12-20.sql new file mode 100644 index 000000000..c0ecdc93b --- /dev/null +++ b/Experiments/StartHere 2021-12-20.sql @@ -0,0 +1,353 @@ + +EXEC tSQLt.NewTestClass 'NotInnerTests'; +GO +CREATE PROCEDURE NotInnerTests.[test brittle transaction test] +AS +BEGIN + BEGIN TRAN; +END; +GO + +--EXEC tSQLt.Private_RunTest @TestName = 'NotInnerTests.[test brittle transaction test]'; + + + DROP TABLE IF EXISTS #TestMessage; + DROP TABLE IF EXISTS #ExpectException; + DROP TABLE IF EXISTS #SkipTest; + DROP TABLE IF EXISTS #NoTransaction; + DROP TABLE IF EXISTS #TableBackupLog; + GO + + DECLARE @TestName NVARCHAR(MAX) = 'NotInnerTests.[test brittle transaction test]'; + DECLARE @SetUp NVARCHAR(MAX) = NULL; + DECLARE @CleanUp NVARCHAR(MAX) = NULL; + + DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; + + DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; + DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; + DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; + DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; + DECLARE @Result NVARCHAR(MAX); + DECLARE @TranName CHAR(32) = NULL; + DECLARE @TestResultId INT; + DECLARE @TestObjectId INT; + DECLARE @TestEndTime DATETIME2 = NULL; + + DECLARE @VerboseMsg NVARCHAR(MAX); + DECLARE @Verbose BIT; + SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); + + TRUNCATE TABLE tSQLt.CaptureOutputLog; + CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); + CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); + CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); + CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + + SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), + @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), + @TestObjectId = OBJECT_ID(@TestName); + + INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) + SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' + OPTION(MAXDOP 1); + SELECT @TestResultId = SCOPE_IDENTITY(); + + IF(@Verbose = 1) + BEGIN + SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; + EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; + END; + + + SET @Result = 'Success'; + DECLARE @SkipTestFlag BIT = 0; + DECLARE @NoTransactionFlag BIT = 0; + + --BEGIN TRY + --EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; + --SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; + --SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; + + --IF(@SkipTestFlag = 0) + --BEGIN + -- IF(@NoTransactionFlag = 0) + -- BEGIN + -- EXEC tSQLt.GetNewTranName @TranName OUT; + -- UPDATE tSQLt.TestResult SET TranName = @TranName WHERE Id = @TestResultId; + -- END; + --EXEC tSQLt.Private_RunTest_TestExecution + -- @TestName, + -- @SetUp, + -- @CleanUp, + -- @NoTransactionFlag, + -- @TranName, + -- @Result OUT, + -- @Msg OUT, + -- @TestEndTime OUT; +IF (1=1) +BEGIN + DECLARE @TransactionStartedFlag BIT = 0; + DECLARE @PreExecTrancount INT = NULL; + DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName; + DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; + + BEGIN TRY + + IF(@NoTransactionFlag = 0) + BEGIN + BEGIN TRAN; + SET @TransactionStartedFlag = 1; + SAVE TRAN @TranName; + END; + ELSE + BEGIN + SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; + EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + END; + + SET @PreExecTrancount = @@TRANCOUNT; + + DECLARE @TmpMsg NVARCHAR(MAX); + SET @TestEndTime = NULL; + BEGIN TRY + IF (@SetUp IS NOT NULL) + BEGIN + EXEC @SetUp; + END; + + BEGIN TRY + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + EXEC (@TestExecutionCmd); + END TRY + BEGIN CATCH + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + THROW; + END CATCH; + + IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) + BEGIN + SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; + EXEC tSQLt.Fail @TmpMsg; + END + SET @TestEndTime = SYSDATETIME(); + END TRY + BEGIN CATCH + SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); + IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' + BEGIN + SELECT @Msg = Msg FROM #TestMessage; + SET @Result = 'Failure'; + END + ELSE + BEGIN + DECLARE @ErrorInfo NVARCHAR(MAX); + SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); + + IF(EXISTS(SELECT 1 FROM #ExpectException)) + BEGIN + DECLARE @ExpectException INT; + DECLARE @ExpectedMessage NVARCHAR(MAX); + DECLARE @ExpectedMessagePattern NVARCHAR(MAX); + DECLARE @ExpectedSeverity INT; + DECLARE @ExpectedState INT; + DECLARE @ExpectedErrorNumber INT; + DECLARE @FailMessage NVARCHAR(MAX); + SELECT @ExpectException = ExpectException, + @ExpectedMessage = ExpectedMessage, + @ExpectedSeverity = ExpectedSeverity, + @ExpectedState = ExpectedState, + @ExpectedMessagePattern = ExpectedMessagePattern, + @ExpectedErrorNumber = ExpectedErrorNumber, + @FailMessage = FailMessage + FROM #ExpectException; + + IF(@ExpectException = 1) + BEGIN + SET @Result = 'Success'; + SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; + IF(ERROR_MESSAGE() <> @ExpectedMessage) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ + 'Actual Message : <'+ERROR_MESSAGE()+'>'; + SET @Result = 'Failure'; + END + IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ + 'Actual Message : <'+ERROR_MESSAGE()+'>'; + SET @Result = 'Failure'; + END + IF(ERROR_NUMBER() <> @ExpectedErrorNumber) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); + SET @Result = 'Failure'; + END + IF(ERROR_SEVERITY() <> @ExpectedSeverity) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); + SET @Result = 'Failure'; + END + IF(ERROR_STATE() <> @ExpectedState) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); + SET @Result = 'Failure'; + END + IF(@Result = 'Failure') + BEGIN + SET @Msg = @TmpMsg; + END + END + ELSE + BEGIN + SET @Result = 'Failure'; + SET @Msg = + COALESCE(@FailMessage+' ','')+ + 'Expected no error to be raised. Instead this error was encountered:'+ + CHAR(13)+CHAR(10)+ + @ErrorInfo; + END + END; + ELSE + BEGIN + SET @Result = 'Error'; + SET @Msg = @ErrorInfo; + END; + END; + END CATCH; + END TRY + BEGIN CATCH + SET @Result = 'Error'; + SET @Msg = ERROR_MESSAGE(); + END CATCH + + --TODO:NoTran + ---- Compare @@Trancount, throw up arms if it doesn't match + --TODO:NoTran + BEGIN TRY + IF(@TransactionStartedFlag = 1) + BEGIN + ROLLBACK TRAN @TranName; + END; + END TRY + BEGIN CATCH + DECLARE @PostExecTrancount INT; + SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; + IF (@@TRANCOUNT > 0) ROLLBACK; + BEGIN TRAN; + IF( @Result <> 'Success' + OR @PostExecTrancount <> 0 + ) + BEGIN + SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); + SET @Result = 'Error'; + END; + END CATCH; + IF (@NoTransactionFlag = 1) + BEGIN + SET @CleanUpProcedureExecutionCmd = ( + ( + SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' + FROM #NoTransaction NT + ORDER BY OrderId + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)') + ); + IF(@CleanUpProcedureExecutionCmd IS NOT NULL) + BEGIN + EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; + END; + + IF(@CleanUp IS NOT NULL) + BEGIN + EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; + END; + + DECLARE @CleanUpErrorMsg NVARCHAR(MAX); + EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; + SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); + + SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; + EXEC tSQLt.Private_AssertNoSideEffects + @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', + @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', + @TestResult = @Result OUT, + @TestMsg = @Msg OUT + END; + IF(@TransactionStartedFlag = 1) + BEGIN + COMMIT; + END; +END; + + --END; + --ELSE + --BEGIN + -- DECLARE @TmpMsg NVARCHAR(MAX); + -- SELECT + -- @Result = 'Skipped', + -- @Msg = ST.SkipTestMessage + -- FROM #SkipTest AS ST; + -- SET @TmpMsg = '-->'+@TestName+' skipped: '+@Msg; + -- EXEC tSQLt.Private_Print @Message = @TmpMsg; + -- SET @TestEndTime = SYSDATETIME(); + --END; + --END TRY + --BEGIN CATCH + -- SET @Result = 'Error'; + -- SET @Msg = ISNULL(NULLIF(@Msg,'') + ' ','')+ERROR_MESSAGE(); + -- --SET @TestEndTime = SYSDATETIME(); + --END CATCH; +------------------------------------------------------------------------------------------------ +-- If(@Result NOT IN ('Success','Skipped')) +-- BEGIN +-- SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; +-- EXEC tSQLt.Private_Print @Message = @Msg2, @Severity = 0; +-- END; +-- IF EXISTS(SELECT 1 FROM tSQLt.TestResult WHERE Id = @TestResultId) +-- BEGIN +-- UPDATE tSQLt.TestResult SET +-- Result = @Result, +-- Msg = @Msg, +-- TestEndTime = @TestEndTime +-- WHERE Id = @TestResultId; +-- END; +-- ELSE +-- BEGIN +-- INSERT tSQLt.TestResult(Class, TestCase, TranName, Result, Msg) +-- SELECT @TestClassName, +-- @TestProcName, +-- '?', +-- 'Error', +-- 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; +-- END; + +-- IF(@Verbose = 1) +-- BEGIN +-- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Finished'; +-- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; +-- --DECLARE @AsciiArtLine NVARCHAR(MAX) = CASE WHEN @Result<>'Success' THEN REPLICATE(CHAR(168),150)+' '+CHAR(155)+CHAR(155)+' '+@Result + ' ' +CHAR(139)+CHAR(139) ELSE '' END + CHAR(13)+CHAR(10) + CHAR(173); +-- --EXEC tSQLt.Private_Print @Message = @AsciiArtLine, @Severity = 0; +-- END; + +-- IF(@Result = 'FATAL') +-- BEGIN +-- INSERT INTO tSQLt.Private_Seize VALUES(1); +-- RAISERROR('The last test has invalidated the current installation of tSQLt. Please reinstall tSQLt.',16,10); +-- END; +-- IF(@Result = 'Abort') +-- BEGIN +-- RAISERROR('Aborting the current execution of tSQLt due to a severe error.', 16, 10); +-- END; + +-- IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index fc2a97683..99ce4ff56 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -81,7 +81,15 @@ BEGIN BEGIN EXEC @SetUp; END; - EXEC (@TestExecutionCmd); + + BEGIN TRY + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + EXEC (@TestExecutionCmd); + END TRY + BEGIN CATCH + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + THROW; + END CATCH; IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 741decb42..a05898d83 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2441,33 +2441,42 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO ----[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction') CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match] AS BEGIN - EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC tSQLt.NewTestClass 'MyInnerTestsA' + EXEC('CREATE PROCEDURE MyInnerTestsA.[test should execute outside of transaction] AS BEGIN TRAN;'); + + EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; + EXEC tSQLt.Run 'MyInnerTestsA.[test should execute outside of transaction]'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +--[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction for NoTransaction tests') +CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match in NoTransaction test] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTestsB' EXEC(' --[@'+'tSQLt:NoTransaction](DEFAULT) -CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN;SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); +CREATE PROCEDURE MyInnerTestsB.[test should execute outside of transaction] AS BEGIN TRAN;SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); '); EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; BEGIN TRY - EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + EXEC tSQLt.Run 'MyInnerTestsB.[test should execute outside of transaction]'; END TRY BEGIN CATCH - SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); + SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); END CATCH; - - END; GO /*-----------------------------------------------------------------------------------------------*/ GO -EXEC tSQLt.Run 'Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don''t match]'; -GO -SELECT * FROM tSQLt.TestResult; /*-- Transaction Tests From bd5d4744271a2bf6c425ea40dce94982a4b681f1 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 20 Dec 2021 06:16:46 -0500 Subject: [PATCH 2/4] More experiments --- Experiments/StartHere 2021-12-20 B.sql | 11 + Experiments/StartHere 2021-12-20.sql | 437 +++++++++++++------------ 2 files changed, 230 insertions(+), 218 deletions(-) create mode 100644 Experiments/StartHere 2021-12-20 B.sql diff --git a/Experiments/StartHere 2021-12-20 B.sql b/Experiments/StartHere 2021-12-20 B.sql new file mode 100644 index 000000000..2e3252985 --- /dev/null +++ b/Experiments/StartHere 2021-12-20 B.sql @@ -0,0 +1,11 @@ + +BEGIN TRAN; +SAVE TRAN TranName; + +BEGIN TRY +SELECT XACT_STATE(),@@TRANCOUNT; +EXEC ('BEGIN TRAN'); +END TRY +BEGIN CATCH +SELECT XACT_STATE(),@@TRANCOUNT; +END CATCH; diff --git a/Experiments/StartHere 2021-12-20.sql b/Experiments/StartHere 2021-12-20.sql index c0ecdc93b..319b13bd9 100644 --- a/Experiments/StartHere 2021-12-20.sql +++ b/Experiments/StartHere 2021-12-20.sql @@ -11,60 +11,60 @@ GO --EXEC tSQLt.Private_RunTest @TestName = 'NotInnerTests.[test brittle transaction test]'; - DROP TABLE IF EXISTS #TestMessage; - DROP TABLE IF EXISTS #ExpectException; - DROP TABLE IF EXISTS #SkipTest; - DROP TABLE IF EXISTS #NoTransaction; - DROP TABLE IF EXISTS #TableBackupLog; - GO + --DROP TABLE IF EXISTS #TestMessage; + --DROP TABLE IF EXISTS #ExpectException; + --DROP TABLE IF EXISTS #SkipTest; + --DROP TABLE IF EXISTS #NoTransaction; + --DROP TABLE IF EXISTS #TableBackupLog; + --GO DECLARE @TestName NVARCHAR(MAX) = 'NotInnerTests.[test brittle transaction test]'; - DECLARE @SetUp NVARCHAR(MAX) = NULL; - DECLARE @CleanUp NVARCHAR(MAX) = NULL; + --DECLARE @SetUp NVARCHAR(MAX) = NULL; + --DECLARE @CleanUp NVARCHAR(MAX) = NULL; - DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; + --DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; - DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; - DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; - DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; - DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; - DECLARE @Result NVARCHAR(MAX); - DECLARE @TranName CHAR(32) = NULL; - DECLARE @TestResultId INT; - DECLARE @TestObjectId INT; - DECLARE @TestEndTime DATETIME2 = NULL; + --DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; + --DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; + --DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; + --DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; + --DECLARE @Result NVARCHAR(MAX); + DECLARE @TranName CHAR(32) = 'sadfhksajdf'; + --DECLARE @TestResultId INT; + --DECLARE @TestObjectId INT; + --DECLARE @TestEndTime DATETIME2 = NULL; - DECLARE @VerboseMsg NVARCHAR(MAX); - DECLARE @Verbose BIT; - SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); + --DECLARE @VerboseMsg NVARCHAR(MAX); + --DECLARE @Verbose BIT; + --SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); - TRUNCATE TABLE tSQLt.CaptureOutputLog; - CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); - CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); - CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); - CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + --TRUNCATE TABLE tSQLt.CaptureOutputLog; + --CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); + --CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); + --CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); + --CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); + --CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); - SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), - @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), - @TestObjectId = OBJECT_ID(@TestName); + --SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), + -- @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), + -- @TestObjectId = OBJECT_ID(@TestName); - INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) - SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' - OPTION(MAXDOP 1); - SELECT @TestResultId = SCOPE_IDENTITY(); + --INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) + -- SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' + -- OPTION(MAXDOP 1); + --SELECT @TestResultId = SCOPE_IDENTITY(); - IF(@Verbose = 1) - BEGIN - SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; - EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; - END; + --IF(@Verbose = 1) + --BEGIN + -- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; + -- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; + --END; - SET @Result = 'Success'; - DECLARE @SkipTestFlag BIT = 0; - DECLARE @NoTransactionFlag BIT = 0; + --SET @Result = 'Success'; + --DECLARE @SkipTestFlag BIT = 0; + --DECLARE @NoTransactionFlag BIT = 0; --BEGIN TRY --EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; @@ -87,36 +87,36 @@ GO -- @Result OUT, -- @Msg OUT, -- @TestEndTime OUT; -IF (1=1) -BEGIN - DECLARE @TransactionStartedFlag BIT = 0; - DECLARE @PreExecTrancount INT = NULL; +--IF (1=1) +--BEGIN + --DECLARE @TransactionStartedFlag BIT = 0; + --DECLARE @PreExecTrancount INT = NULL; DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName; - DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; + --DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; - BEGIN TRY + --BEGIN TRY - IF(@NoTransactionFlag = 0) - BEGIN + --IF(@NoTransactionFlag = 0) + --BEGIN BEGIN TRAN; - SET @TransactionStartedFlag = 1; + --SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; - END; - ELSE - BEGIN - SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; - EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; - END; + --END; + --ELSE + --BEGIN + -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; + -- EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + --END; - SET @PreExecTrancount = @@TRANCOUNT; + --SET @PreExecTrancount = @@TRANCOUNT; - DECLARE @TmpMsg NVARCHAR(MAX); - SET @TestEndTime = NULL; - BEGIN TRY - IF (@SetUp IS NOT NULL) - BEGIN - EXEC @SetUp; - END; + --DECLARE @TmpMsg NVARCHAR(MAX); + --SET @TestEndTime = NULL; + --BEGIN TRY + --IF (@SetUp IS NOT NULL) + --BEGIN + -- EXEC @SetUp; + --END; BEGIN TRY SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; @@ -127,168 +127,169 @@ BEGIN THROW; END CATCH; - IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) - BEGIN - SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; - EXEC tSQLt.Fail @TmpMsg; - END - SET @TestEndTime = SYSDATETIME(); - END TRY - BEGIN CATCH - SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); - IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' - BEGIN - SELECT @Msg = Msg FROM #TestMessage; - SET @Result = 'Failure'; - END - ELSE - BEGIN - DECLARE @ErrorInfo NVARCHAR(MAX); - SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); + --IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) + --BEGIN + -- SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; + -- EXEC tSQLt.Fail @TmpMsg; + --END + --SET @TestEndTime = SYSDATETIME(); + --END TRY + --BEGIN CATCH + --SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); + --IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' + --BEGIN + -- SELECT @Msg = Msg FROM #TestMessage; + -- SET @Result = 'Failure'; + --END + --ELSE + --BEGIN + --DECLARE @ErrorInfo NVARCHAR(MAX); + --SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); - IF(EXISTS(SELECT 1 FROM #ExpectException)) - BEGIN - DECLARE @ExpectException INT; - DECLARE @ExpectedMessage NVARCHAR(MAX); - DECLARE @ExpectedMessagePattern NVARCHAR(MAX); - DECLARE @ExpectedSeverity INT; - DECLARE @ExpectedState INT; - DECLARE @ExpectedErrorNumber INT; - DECLARE @FailMessage NVARCHAR(MAX); - SELECT @ExpectException = ExpectException, - @ExpectedMessage = ExpectedMessage, - @ExpectedSeverity = ExpectedSeverity, - @ExpectedState = ExpectedState, - @ExpectedMessagePattern = ExpectedMessagePattern, - @ExpectedErrorNumber = ExpectedErrorNumber, - @FailMessage = FailMessage - FROM #ExpectException; + --IF(EXISTS(SELECT 1 FROM #ExpectException)) + --BEGIN + -- DECLARE @ExpectException INT; + -- DECLARE @ExpectedMessage NVARCHAR(MAX); + -- DECLARE @ExpectedMessagePattern NVARCHAR(MAX); + -- DECLARE @ExpectedSeverity INT; + -- DECLARE @ExpectedState INT; + -- DECLARE @ExpectedErrorNumber INT; + -- DECLARE @FailMessage NVARCHAR(MAX); + -- SELECT @ExpectException = ExpectException, + -- @ExpectedMessage = ExpectedMessage, + -- @ExpectedSeverity = ExpectedSeverity, + -- @ExpectedState = ExpectedState, + -- @ExpectedMessagePattern = ExpectedMessagePattern, + -- @ExpectedErrorNumber = ExpectedErrorNumber, + -- @FailMessage = FailMessage + -- FROM #ExpectException; - IF(@ExpectException = 1) - BEGIN - SET @Result = 'Success'; - SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; - IF(ERROR_MESSAGE() <> @ExpectedMessage) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ - 'Actual Message : <'+ERROR_MESSAGE()+'>'; - SET @Result = 'Failure'; - END - IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ - 'Actual Message : <'+ERROR_MESSAGE()+'>'; - SET @Result = 'Failure'; - END - IF(ERROR_NUMBER() <> @ExpectedErrorNumber) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); - SET @Result = 'Failure'; - END - IF(ERROR_SEVERITY() <> @ExpectedSeverity) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); - SET @Result = 'Failure'; - END - IF(ERROR_STATE() <> @ExpectedState) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); - SET @Result = 'Failure'; - END - IF(@Result = 'Failure') - BEGIN - SET @Msg = @TmpMsg; - END - END - ELSE - BEGIN - SET @Result = 'Failure'; - SET @Msg = - COALESCE(@FailMessage+' ','')+ - 'Expected no error to be raised. Instead this error was encountered:'+ - CHAR(13)+CHAR(10)+ - @ErrorInfo; - END - END; - ELSE - BEGIN - SET @Result = 'Error'; - SET @Msg = @ErrorInfo; - END; - END; - END CATCH; - END TRY - BEGIN CATCH - SET @Result = 'Error'; - SET @Msg = ERROR_MESSAGE(); - END CATCH + -- IF(@ExpectException = 1) + -- BEGIN + -- SET @Result = 'Success'; + -- SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; + -- IF(ERROR_MESSAGE() <> @ExpectedMessage) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ + -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ + -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_NUMBER() <> @ExpectedErrorNumber) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + -- 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_SEVERITY() <> @ExpectedSeverity) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + -- 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_STATE() <> @ExpectedState) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + -- 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); + -- SET @Result = 'Failure'; + -- END + -- IF(@Result = 'Failure') + -- BEGIN + -- SET @Msg = @TmpMsg; + -- END + -- END + -- ELSE + -- BEGIN + -- SET @Result = 'Failure'; + -- SET @Msg = + -- COALESCE(@FailMessage+' ','')+ + -- 'Expected no error to be raised. Instead this error was encountered:'+ + -- CHAR(13)+CHAR(10)+ + -- @ErrorInfo; + -- END + --PRINT ''; + --END; + --ELSE + --BEGIN + --SET @Result = 'Error'; + --SET @Msg = @ErrorInfo; + --END; + --END; + --END CATCH; + --END TRY + --BEGIN CATCH + -- --SET @Result = 'Error'; + -- --SET @Msg = ERROR_MESSAGE(); + --END CATCH --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran - BEGIN TRY - IF(@TransactionStartedFlag = 1) - BEGIN - ROLLBACK TRAN @TranName; - END; - END TRY - BEGIN CATCH - DECLARE @PostExecTrancount INT; - SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; - IF (@@TRANCOUNT > 0) ROLLBACK; - BEGIN TRAN; - IF( @Result <> 'Success' - OR @PostExecTrancount <> 0 - ) - BEGIN - SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); - SET @Result = 'Error'; - END; - END CATCH; - IF (@NoTransactionFlag = 1) - BEGIN - SET @CleanUpProcedureExecutionCmd = ( - ( - SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' - FROM #NoTransaction NT - ORDER BY OrderId - FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)') - ); - IF(@CleanUpProcedureExecutionCmd IS NOT NULL) - BEGIN - EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; - END; + --BEGIN TRY + -- IF(@TransactionStartedFlag = 1) + -- BEGIN + -- ROLLBACK TRAN @TranName; + -- END; + --END TRY + --BEGIN CATCH + -- DECLARE @PostExecTrancount INT; + -- SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; + -- IF (@@TRANCOUNT > 0) ROLLBACK; + -- BEGIN TRAN; + -- IF( @Result <> 'Success' + -- OR @PostExecTrancount <> 0 + -- ) + -- BEGIN + -- SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); + -- SET @Result = 'Error'; + -- END; + --END CATCH; + --IF (@NoTransactionFlag = 1) + --BEGIN + -- SET @CleanUpProcedureExecutionCmd = ( + -- ( + -- SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' + -- FROM #NoTransaction NT + -- ORDER BY OrderId + -- FOR XML PATH(''),TYPE + -- ).value('.','NVARCHAR(MAX)') + -- ); + -- IF(@CleanUpProcedureExecutionCmd IS NOT NULL) + -- BEGIN + -- EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; + -- END; - IF(@CleanUp IS NOT NULL) - BEGIN - EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; - END; + -- IF(@CleanUp IS NOT NULL) + -- BEGIN + -- EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; + -- END; - DECLARE @CleanUpErrorMsg NVARCHAR(MAX); - EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; - SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); + -- DECLARE @CleanUpErrorMsg NVARCHAR(MAX); + -- EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; + -- SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); - SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; - EXEC tSQLt.Private_AssertNoSideEffects - @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', - @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', - @TestResult = @Result OUT, - @TestMsg = @Msg OUT - END; - IF(@TransactionStartedFlag = 1) - BEGIN - COMMIT; - END; -END; + -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; + -- EXEC tSQLt.Private_AssertNoSideEffects + -- @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', + -- @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', + -- @TestResult = @Result OUT, + -- @TestMsg = @Msg OUT + --END; + --IF(@TransactionStartedFlag = 1) + --BEGIN + -- COMMIT; + --END; +--END; --END; --ELSE From 7a4f0d669cead6a1fc19fd242df18f3e4dc8a1bc Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 20 Dec 2021 06:39:29 -0500 Subject: [PATCH 3/4] We may have found a defect in mssql 2017 related to starting a transaction (BEGIN TRAN) only within a try-catch block invalidating the transaction. --- Experiments/Experiments.ssmssqlproj | 6 + ...fect Try-Catch Invalidates Transaction.sql | 20 + Experiments/StartHere 2021-12-20 B.sql | 11 - Experiments/StartHere 2021-12-20.sql | 354 ------------------ 4 files changed, 26 insertions(+), 365 deletions(-) create mode 100644 Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql delete mode 100644 Experiments/StartHere 2021-12-20 B.sql delete mode 100644 Experiments/StartHere 2021-12-20.sql diff --git a/Experiments/Experiments.ssmssqlproj b/Experiments/Experiments.ssmssqlproj index 73eb07cae..7ea928e3d 100644 --- a/Experiments/Experiments.ssmssqlproj +++ b/Experiments/Experiments.ssmssqlproj @@ -66,6 +66,12 @@ LIKE4000.sql + + + + + MSSQL Defect Try-Catch Invalidates Transaction.sql + diff --git a/Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql b/Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql new file mode 100644 index 000000000..6497e537a --- /dev/null +++ b/Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql @@ -0,0 +1,20 @@ +PRINT 'No TRY...CATCH:'; +EXEC ('BEGIN TRAN'); +SELECT XACT_STATE() AS [XACT_STATE()],@@TRANCOUNT AS [@@TRANCOUNT]; +GO +IF(XACT_STATE()<>0)ROLLBACK; +GO + + + +GO +PRINT 'In TRY...CATCH:'; +BEGIN TRY +EXEC ('BEGIN TRAN'); +END TRY +BEGIN CATCH +END CATCH; +SELECT XACT_STATE() AS [XACT_STATE()],@@TRANCOUNT AS [@@TRANCOUNT]; +GO +IF(XACT_STATE()<>0)ROLLBACK; +GO diff --git a/Experiments/StartHere 2021-12-20 B.sql b/Experiments/StartHere 2021-12-20 B.sql deleted file mode 100644 index 2e3252985..000000000 --- a/Experiments/StartHere 2021-12-20 B.sql +++ /dev/null @@ -1,11 +0,0 @@ - -BEGIN TRAN; -SAVE TRAN TranName; - -BEGIN TRY -SELECT XACT_STATE(),@@TRANCOUNT; -EXEC ('BEGIN TRAN'); -END TRY -BEGIN CATCH -SELECT XACT_STATE(),@@TRANCOUNT; -END CATCH; diff --git a/Experiments/StartHere 2021-12-20.sql b/Experiments/StartHere 2021-12-20.sql deleted file mode 100644 index 319b13bd9..000000000 --- a/Experiments/StartHere 2021-12-20.sql +++ /dev/null @@ -1,354 +0,0 @@ - -EXEC tSQLt.NewTestClass 'NotInnerTests'; -GO -CREATE PROCEDURE NotInnerTests.[test brittle transaction test] -AS -BEGIN - BEGIN TRAN; -END; -GO - ---EXEC tSQLt.Private_RunTest @TestName = 'NotInnerTests.[test brittle transaction test]'; - - - --DROP TABLE IF EXISTS #TestMessage; - --DROP TABLE IF EXISTS #ExpectException; - --DROP TABLE IF EXISTS #SkipTest; - --DROP TABLE IF EXISTS #NoTransaction; - --DROP TABLE IF EXISTS #TableBackupLog; - --GO - - DECLARE @TestName NVARCHAR(MAX) = 'NotInnerTests.[test brittle transaction test]'; - --DECLARE @SetUp NVARCHAR(MAX) = NULL; - --DECLARE @CleanUp NVARCHAR(MAX) = NULL; - - --DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; - - --DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; - --DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; - --DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; - --DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; - --DECLARE @Result NVARCHAR(MAX); - DECLARE @TranName CHAR(32) = 'sadfhksajdf'; - --DECLARE @TestResultId INT; - --DECLARE @TestObjectId INT; - --DECLARE @TestEndTime DATETIME2 = NULL; - - --DECLARE @VerboseMsg NVARCHAR(MAX); - --DECLARE @Verbose BIT; - --SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); - - --TRUNCATE TABLE tSQLt.CaptureOutputLog; - --CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); - --CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); - --CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - --CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); - --CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); - - - --SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), - -- @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), - -- @TestObjectId = OBJECT_ID(@TestName); - - --INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) - -- SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' - -- OPTION(MAXDOP 1); - --SELECT @TestResultId = SCOPE_IDENTITY(); - - --IF(@Verbose = 1) - --BEGIN - -- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; - -- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; - --END; - - - --SET @Result = 'Success'; - --DECLARE @SkipTestFlag BIT = 0; - --DECLARE @NoTransactionFlag BIT = 0; - - --BEGIN TRY - --EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; - --SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; - --SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; - - --IF(@SkipTestFlag = 0) - --BEGIN - -- IF(@NoTransactionFlag = 0) - -- BEGIN - -- EXEC tSQLt.GetNewTranName @TranName OUT; - -- UPDATE tSQLt.TestResult SET TranName = @TranName WHERE Id = @TestResultId; - -- END; - --EXEC tSQLt.Private_RunTest_TestExecution - -- @TestName, - -- @SetUp, - -- @CleanUp, - -- @NoTransactionFlag, - -- @TranName, - -- @Result OUT, - -- @Msg OUT, - -- @TestEndTime OUT; ---IF (1=1) ---BEGIN - --DECLARE @TransactionStartedFlag BIT = 0; - --DECLARE @PreExecTrancount INT = NULL; - DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName; - --DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; - - --BEGIN TRY - - --IF(@NoTransactionFlag = 0) - --BEGIN - BEGIN TRAN; - --SET @TransactionStartedFlag = 1; - SAVE TRAN @TranName; - --END; - --ELSE - --BEGIN - -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; - -- EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; - --END; - - --SET @PreExecTrancount = @@TRANCOUNT; - - --DECLARE @TmpMsg NVARCHAR(MAX); - --SET @TestEndTime = NULL; - --BEGIN TRY - --IF (@SetUp IS NOT NULL) - --BEGIN - -- EXEC @SetUp; - --END; - - BEGIN TRY - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - EXEC (@TestExecutionCmd); - END TRY - BEGIN CATCH - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - THROW; - END CATCH; - - --IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) - --BEGIN - -- SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; - -- EXEC tSQLt.Fail @TmpMsg; - --END - --SET @TestEndTime = SYSDATETIME(); - --END TRY - --BEGIN CATCH - --SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); - --IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' - --BEGIN - -- SELECT @Msg = Msg FROM #TestMessage; - -- SET @Result = 'Failure'; - --END - --ELSE - --BEGIN - --DECLARE @ErrorInfo NVARCHAR(MAX); - --SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); - - --IF(EXISTS(SELECT 1 FROM #ExpectException)) - --BEGIN - -- DECLARE @ExpectException INT; - -- DECLARE @ExpectedMessage NVARCHAR(MAX); - -- DECLARE @ExpectedMessagePattern NVARCHAR(MAX); - -- DECLARE @ExpectedSeverity INT; - -- DECLARE @ExpectedState INT; - -- DECLARE @ExpectedErrorNumber INT; - -- DECLARE @FailMessage NVARCHAR(MAX); - -- SELECT @ExpectException = ExpectException, - -- @ExpectedMessage = ExpectedMessage, - -- @ExpectedSeverity = ExpectedSeverity, - -- @ExpectedState = ExpectedState, - -- @ExpectedMessagePattern = ExpectedMessagePattern, - -- @ExpectedErrorNumber = ExpectedErrorNumber, - -- @FailMessage = FailMessage - -- FROM #ExpectException; - - -- IF(@ExpectException = 1) - -- BEGIN - -- SET @Result = 'Success'; - -- SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; - -- IF(ERROR_MESSAGE() <> @ExpectedMessage) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ - -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ - -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_NUMBER() <> @ExpectedErrorNumber) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - -- 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_SEVERITY() <> @ExpectedSeverity) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - -- 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_STATE() <> @ExpectedState) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - -- 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); - -- SET @Result = 'Failure'; - -- END - -- IF(@Result = 'Failure') - -- BEGIN - -- SET @Msg = @TmpMsg; - -- END - -- END - -- ELSE - -- BEGIN - -- SET @Result = 'Failure'; - -- SET @Msg = - -- COALESCE(@FailMessage+' ','')+ - -- 'Expected no error to be raised. Instead this error was encountered:'+ - -- CHAR(13)+CHAR(10)+ - -- @ErrorInfo; - -- END - --PRINT ''; - --END; - --ELSE - --BEGIN - --SET @Result = 'Error'; - --SET @Msg = @ErrorInfo; - --END; - --END; - --END CATCH; - --END TRY - --BEGIN CATCH - -- --SET @Result = 'Error'; - -- --SET @Msg = ERROR_MESSAGE(); - --END CATCH - - --TODO:NoTran - ---- Compare @@Trancount, throw up arms if it doesn't match - --TODO:NoTran - --BEGIN TRY - -- IF(@TransactionStartedFlag = 1) - -- BEGIN - -- ROLLBACK TRAN @TranName; - -- END; - --END TRY - --BEGIN CATCH - -- DECLARE @PostExecTrancount INT; - -- SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; - -- IF (@@TRANCOUNT > 0) ROLLBACK; - -- BEGIN TRAN; - -- IF( @Result <> 'Success' - -- OR @PostExecTrancount <> 0 - -- ) - -- BEGIN - -- SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); - -- SET @Result = 'Error'; - -- END; - --END CATCH; - --IF (@NoTransactionFlag = 1) - --BEGIN - -- SET @CleanUpProcedureExecutionCmd = ( - -- ( - -- SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' - -- FROM #NoTransaction NT - -- ORDER BY OrderId - -- FOR XML PATH(''),TYPE - -- ).value('.','NVARCHAR(MAX)') - -- ); - -- IF(@CleanUpProcedureExecutionCmd IS NOT NULL) - -- BEGIN - -- EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; - -- END; - - -- IF(@CleanUp IS NOT NULL) - -- BEGIN - -- EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; - -- END; - - -- DECLARE @CleanUpErrorMsg NVARCHAR(MAX); - -- EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; - -- SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); - - -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; - -- EXEC tSQLt.Private_AssertNoSideEffects - -- @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', - -- @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', - -- @TestResult = @Result OUT, - -- @TestMsg = @Msg OUT - --END; - --IF(@TransactionStartedFlag = 1) - --BEGIN - -- COMMIT; - --END; ---END; - - --END; - --ELSE - --BEGIN - -- DECLARE @TmpMsg NVARCHAR(MAX); - -- SELECT - -- @Result = 'Skipped', - -- @Msg = ST.SkipTestMessage - -- FROM #SkipTest AS ST; - -- SET @TmpMsg = '-->'+@TestName+' skipped: '+@Msg; - -- EXEC tSQLt.Private_Print @Message = @TmpMsg; - -- SET @TestEndTime = SYSDATETIME(); - --END; - --END TRY - --BEGIN CATCH - -- SET @Result = 'Error'; - -- SET @Msg = ISNULL(NULLIF(@Msg,'') + ' ','')+ERROR_MESSAGE(); - -- --SET @TestEndTime = SYSDATETIME(); - --END CATCH; ------------------------------------------------------------------------------------------------- --- If(@Result NOT IN ('Success','Skipped')) --- BEGIN --- SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; --- EXEC tSQLt.Private_Print @Message = @Msg2, @Severity = 0; --- END; --- IF EXISTS(SELECT 1 FROM tSQLt.TestResult WHERE Id = @TestResultId) --- BEGIN --- UPDATE tSQLt.TestResult SET --- Result = @Result, --- Msg = @Msg, --- TestEndTime = @TestEndTime --- WHERE Id = @TestResultId; --- END; --- ELSE --- BEGIN --- INSERT tSQLt.TestResult(Class, TestCase, TranName, Result, Msg) --- SELECT @TestClassName, --- @TestProcName, --- '?', --- 'Error', --- 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; --- END; - --- IF(@Verbose = 1) --- BEGIN --- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Finished'; --- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; --- --DECLARE @AsciiArtLine NVARCHAR(MAX) = CASE WHEN @Result<>'Success' THEN REPLICATE(CHAR(168),150)+' '+CHAR(155)+CHAR(155)+' '+@Result + ' ' +CHAR(139)+CHAR(139) ELSE '' END + CHAR(13)+CHAR(10) + CHAR(173); --- --EXEC tSQLt.Private_Print @Message = @AsciiArtLine, @Severity = 0; --- END; - --- IF(@Result = 'FATAL') --- BEGIN --- INSERT INTO tSQLt.Private_Seize VALUES(1); --- RAISERROR('The last test has invalidated the current installation of tSQLt. Please reinstall tSQLt.',16,10); --- END; --- IF(@Result = 'Abort') --- BEGIN --- RAISERROR('Aborting the current execution of tSQLt due to a severe error.', 16, 10); --- END; - --- IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); From 453e269c1f791c3626eb5c3308af8f9ea6d3c3c9 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 20 Dec 2021 06:44:34 -0500 Subject: [PATCH 4/4] Update Run_Methods.sql trying to manually revert back to desired version --- Source/Run_Methods.sql | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 99ce4ff56..2395b8896 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -82,14 +82,7 @@ BEGIN EXEC @SetUp; END; - BEGIN TRY - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - EXEC (@TestExecutionCmd); - END TRY - BEGIN CATCH - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - THROW; - END CATCH; + EXEC (@TestExecutionCmd); IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN