Is a ROLLBACK TRANSACTION statement necessary - sql

When opening an explicit transaction if a failure occurs will all statements between the BEGIN and COMMIT automatically be rolled back? Or do you have to issue a ROLLBACK statement.
In my previous experience everything between the BEGIN and COMMIT automatically rolled back. Therefore what constitutes when you need to issue a ROLLBACK statement to manually roll it back?

It depends upon your session settings and the type of error. If it is a statement-terminating error, then the transaction just continues with the next statement. If it's a batch-terminating error, then the transaction is aborted.
To avoid issues with statement-terminating errors, make sure you have previously executed:
SET XACT_ABORT ON;
and then all statement-terminating errors will also abort the transaction and roll back.
Some programming client libraries turn that on automatically for you, and that's why you may have previously seen the auto-rollback, but I usually add it as the first line of all my stored procedures, just to be sure.

Related

Uncommitted transaction asking every time in SQL Server 2012

I am facing issues like whenever I open SQL windows and run any SQL transaction (insert, update or delete or any modification in procedure), it asks about uncommitted transaction.
How to fix this permanently?
Sounds like you probably have SET IMPLICIT_TRANSACTIONS on.
This will open a new transaction implicitly when it encounters statements such as insert, update or delete and no transaction is open and will require an explicit commit or rollback.
Implicit transactions may unexpectedly be ON due to ANSI defaults. For details see SET ANSI_DEFAULTS (Transact-SQL).
IMPLICIT_TRANSACTIONS ON is not popular. In most cases where IMPLICIT_TRANSACTIONS is ON, it is because the choice of SET ANSI_DEFAULTS ON has been made.
You need to go into the connection properties and ensure you have not enabled implicit transactions either explicitly or implicitly.
When you use BEGIN TRANSACTION you also need to either COMMIT TRANSACTION if it has done all the work you want correctly or ROLLBACK TRANSACTION if there has been issues and you want to return to the state before the start of the transaction.
Read up on transactions here

SQL Server How to always rollback the entire transaction

I have a somewhat unusual need for my transaction handling.
I am working on a mutation test framework for SQL Server, for this I need to run my tests inside a transaction, so that the database always is in the state it started when the tests finish.
However I have the problem that users can code inside the test procedures and may call rollback transaction that may or may not be inside a (nested) transaction (savepoint).
high level it looks like this
start transaction
initialize test
run test with user code
may or may not contain:
- start tran
- start tran savename
- commit tran
- commit tran savename
- rollback tran
- rollback tran savename
output testresults
rollback transaction
Is there a way to make sure I can at last always roll back to the initial state? I have to take in account that users can call stored procedures/triggers that maybe nested and can all contain transaction statements. With all my solutions the moment a user uses rollback tran in their test code they escape the transaction and not everything will be cleaned
What I want is that if a user calls rollback only their part of the transaction is rolled back and my transaction that I start before the initialization of the test is still intact.
If it is possible I want to prevent to force my users to use a transaction template that uses savepoints when a transaction already exists.
I do not think this is possible without any communication/rules for the user code. Despite what you do, if the user's code runs as many COMMITs as there are ##TRANCOUNT at that time, the transaction will be commited and there will be nothing you can do about that.
One way you could do this is if you check/enforce the user code to, instead of using COMMIT, change that to if ##TRANCOUNT>=2 COMMIT. This will make sure the TRUE data commit can only be done by YOUR COMMIT command. Of course, it happens you never really want to commit, so you just rollback and it's over.
You mention:
What I want is that if a user calls rollback only their part of the
transaction is rolled back
Please note that nested transactions are kind of a myth. Refer to this excellent article. Long story short: "Nested" BEGIN TRANSACTIONs and COMMITs do actually nothing except change the value of the system var ##TRANCOUNT so that some organisation can be made through procedures.
I don't think it is possible to rollback a part of transaction and keep the other transaction intact. The moment we rollback the transaction, the entire transaction gets rolled back.
As pointed out by serveral others nested transactions are not supported in SQL Server. I have decided to minimize the number of state changing statements after the users code and that I clean those statements at the start of a test batch. This way it doesn't really matter that I can't rollback my own transaction, since the heavy lifting will be rolled back either by me or the user.
I also decided to fail the test when the starting ##TRANCOUNT and the ending ##TRANCOUNT dont match up so that no test can pass when there is something wrong with the users transactions.
The current system however will still struggle with a user doing a COMMIT TRAN. That is a problem for another time.
I believe the actual need is to write the test cases for T-SQL. If that is correct then I feel you don't have to reinvent the wheel and start using the open source test framework T-SQLT https://tsqlt.org/

Are T-SQL rollbacks deterministic or garbage collected?

In our Miscrosoft Sql Server 2008 database, I found some stored procedures that do this:
BEGIN TRY
BEGIN TRANSACTION
query1
query2
query3
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RAISERROR
END CATCH
I said to my coworker that this is functionally identical to this:
BEGIN TRANSACTION
query1
query2
query3
COMMIT TRANSACTION
If, say, query2 fails, you never hit the COMMIT line, so of course SqlServer rolls back, and because of the error, it throws it back to the client as well. Since the CATCH block does the same thing as the default, I argued that we don't need the TRY/CATCH block at all.
My co-worker agrees that the ROLLBACK will happen eventually, but it could be some time later, and could hold resources or lock records for some non-deterministic amount of time, and this could cause problems.
So my question is: if a stored procedure fails in a transaction, when does that transaction get rolled back?
The rollback won't be triggered with your solution in the expected way.
You have to add
set xact_abort on
to your query.
For further information see an old answer and the Microsoft documentation
SQL Server will happily leave the transaction open forever as long as the client connection remains open. An explicit rollback is a good practice in that it doesn't wait for the client connection to be physically closed or the pooled connection reused. A simple THROW (SQL 2012 and later) in the CATCH block will rollback the transaction and raise the error.
I recommend the SET XACT_ABORT ON option hash suggested to mitigate a another issue. If a client timeout occurs during query execution, no further code in the batch (including the ROLLBACK) will execute and the transaction will be left open. However, the transaction will still get rolled back with SET XACT_ABORT ON.

Prevent a sql trigger from rolling back original transaction on error

In Sql Server 2008 I've been asked to write triggers to do various types of logging in an application I've built. The problem is my SQL is rusty and sometimes some of the things I put in the triggers to do logging will unexpectedly error out. I intend to eventually work out all the errors but in the meantime my trigger code is breaking my application because if the trigger unexpectedly fails it rolls back the update or insert that triggered it breaking pieces of the application. I attempted to put a try catch block around the trigger code but it appears that doesn't do what I'm accustomed to it doing in other languages and it will rollback the original transaction even with that.
Does anybody know how/if you can write a trigger that when it errors it just dies gracefully and lets the transaction that triggered it complete?
You can enclose your trigger body code into a try-catch block.
CREATE TRIGGER tg
ON tt
AFTER DELETE
AS
set xact_abort off;
BEGIN TRY
sql_statements
END TRY
BEGIN CATCH
dies gracefully
END CATCH;
Remember to set xact_abort to off to avoid automatically rolling back current transaction.
sql fiddle sample

Requirement for explicit transaction statements when using SET XACT_ABORT ON?

If I use SET XACT_ABORT ON in CREATE PROCEDURE statement, do I have to wrap all my statements under explicit transaction statements: BEGIN TRANSACTION and COMMIT?
Or does SET XACT_ABORT ON terminate procedure execution regardless of these in the case of execution error?
XACT_ABORT is typically for when someone has coded a transaction in a stored procedure, when maybe they should have left this responsibility to the calling module.
When something goes wrong, a transaction can remain open with the caller being oblivious to it. Setting XACT_ABORT to ON means that the transaction is not left open in the event of a failure.
When using transaction, you should really always look to handle every possible error within the SQL and deal with the transaction accordingly. Better still, strip the transactions from the SQL altogether, and rely on the calling module.