Difference between BEGIN ATOMIC and BEGIN - sql

A question regardning SQL DB2 engine.
What is the difference between
BEGIN ATOMIC
...code
END
And
BEGIN
..code
END

The answer can be easiliy found in the documentation:
ATOMIC or NOT ATOMIC
ATOMIC indicates that if an unhandled exception condition occurs in the compound statement, all SQL statements in the compound statement will be rolled back.
NOT ATOMIC indicates that an unhandled exception condition within the compound statement does not cause the compound statement to be rolled back.

Related

Oracle DML statement file - Execute, Record error and continue without aborting

I have a file with INSERTs, UPDATEs, DELETEs. I want to execute each of the DML statements in this file, but in case any exception occurs, I want to print that exception and continue. Is there a simple solution for this? Below is a solution which involves wrapping each DML in an anonymous block and print the exception, but I think it is not simple (or elegant) enough:
BEGIN
<<DML statement goes here>>
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;
Needless to say, this cannot be done (easily) for hundreds of DMLs.
A possible decision is to add error logging clause to your statements. To each of your statement you have to add following (for example for INSERT):
insert into my_table (...)
values (...)
LOG ERRORS INTO err$_my_table ('INSERT') REJECT LIMIT UNLIMITED;
Here err$_my_table is a table for error logging. To create it, execute (once per table) following:
begin
DBMS_ERRLOG.CREATE_ERROR_LOG ('MY_TABLE');
end;
/
Error logging clause suppresses any exception and put in error logging table all lines, which fired exceptions. After executing you can query these tables. They will also contain values of SQLCODE and SQLERRM functions. Disadvantages of this method - you need to change all your statements and create a logging table for each table.
More about clause in documentation.

Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0

I have an Insert stored procedure which will feed data to Table1 and get the Column1 value from Table1 and call the second stored procedure which will feed the Table2.
But when I call The second stored procedure as:
Exec USPStoredProcName
I get the following error:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
I have read the answers in other such questions and am unable to find where exactly the commit count is getting messed up.
If you have a TRY/CATCH block then the likely cause is that you are catching a transaction abort exception and continue. In the CATCH block you must always check the XACT_STATE() and handle appropriate aborted and uncommitable (doomed) transactions. If your caller starts a transaction and the calee hits, say, a deadlock (which aborted the transaction), how is the callee going to communicate to the caller that the transaction was aborted and it should not continue with 'business as usual'? The only feasible way is to re-raise an exception, forcing the caller to handle the situation. If you silently swallow an aborted transaction and the caller continues assuming is still in the original transaction, only mayhem can ensure (and the error you get is the way the engine tries to protect itself).
I recommend you go over Exception handling and nested transactions which shows a pattern that can be used with nested transactions and exceptions:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare #trancount int;
set #trancount = ##trancount;
begin try
if #trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if #trancount = 0
commit;
end try
begin catch
declare #error int, #message varchar(4000), #xstate int;
select #error = ERROR_NUMBER(), #message = ERROR_MESSAGE(), #xstate = XACT_STATE();
if #xstate = -1
rollback;
if #xstate = 1 and #trancount = 0
rollback
if #xstate = 1 and #trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message) ;
end catch
end
go
I had this problem too. For me, the reason was that I was doing
return
commit
instead of
commit
return
in one stored procedure.
This normally happens when the transaction is started and either it is not committed or it is not rollback.
In case the error comes in your stored procedure, this can lock the database tables because transaction is not completed due to some runtime errors in the absence of exception handling
You can use Exception handling like below. SET XACT_ABORT
SET XACT_ABORT ON
SET NoCount ON
Begin Try
BEGIN TRANSACTION
//Insert ,update queries
COMMIT
End Try
Begin Catch
ROLLBACK
End Catch
Source
Be aware of that if you use nested transactions, a ROLLBACK operation rolls back all the nested transactions including the outer-most one.
This might, with usage in combination with TRY/CATCH, result in the error you described. See more here.
This can also occur if your stored procedure encounters a compile failure after opening a transaction (e.g. table not found, invalid column name).
I found i had to use 2 stored procedures a "worker" one and a wrapper one with try/catch both with logic similar to that outlined by Remus Rusanu. The worker catch is used to handle the "normal" failures and the wrapper catch to handle compile failure errors.
https://msdn.microsoft.com/en-us/library/ms175976.aspx
Errors Unaffected by a TRY…CATCH Construct
The following types of errors are not handled by a CATCH block when they occur at the same level of execution as the TRY…CATCH construct:
Compile errors, such as syntax errors, that prevent a batch from running.
Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.
Hopefully this helps someone else save a few hours of debugging...
In my case, the error was being caused by a RETURN inside the BEGIN TRANSACTION. So I had something like this:
Begin Transaction
If (#something = 'foo')
Begin
--- do some stuff
Return
End
commit
and it needs to be:
Begin Transaction
If (#something = 'foo')
Begin
--- do some stuff
Rollback Transaction ----- THIS WAS MISSING
Return
End
commit
For me after extensive debugging the fix was a simple missing throw; statement in the catch after the rollback. Without it this ugly error message is what you end up with.
begin catch
if ##trancount > 0 rollback transaction;
throw; --allows capture of useful info when an exception happens within the transaction
end catch
I had the same error message, my mistake was that I had a semicolon at the end of COMMIT TRANSACTION line
Avoid using
RETURN
statement when you are using
BEGIN TRY
...
END TRY
BEGIN CATCH
...
END CATCH
and
BEGIN, COMMIT & ROLLBACK
statements in SQL stored procedures
I encountered this error once after omitting this statement from my transaction.
COMMIT TRANSACTION [MyTransactionName]
In my opinion the accepted answer is in most cases an overkill.
The cause of the error is often mismatch of BEGIN and COMMIT as clearly stated by the error. This means using:
Begin
Begin
-- your query here
End
commit
instead of
Begin Transaction
Begin
-- your query here
End
commit
omitting Transaction after Begin causes this error!
Make sure you don't have multiple transactions in the same procedure/query out of which one or more are left uncommited.
In my case, I accidentally had a BEGIN TRAN statement in the query
This can also depend on the way you are invoking the SP from your C# code. If the SP returns some table type value then invoke the SP with ExecuteStoreQuery, and if the SP doesn't returns any value invoke the SP with ExecuteStoreCommand
For me, the issue was that I forgot to add the output keyword following some output parameters of a SP call within the transaction.
The exact reason for this message is the rule that SQL Server implies: Transaction count should be same at the beginning and the end of execution of a procedure. In other terms, a procedure;
shouldn't commit/rollback a transaction that it didn't start. In this case, previous count displayed in the exception message would be greater zero, and current count is zero. Best way to prevent this is capturing transaction count (##TRANCOUNT) at the very beginning of the execution, and using transaction statements only if it is zero. The sample procedure below is a simplest "safe" structure against this type of mistake. If this procedure is called within an existing transaction, it won't begin a new transaction nor try to commit or rollback the "inherited" one. Instead, it just re-throws the same error to caller context. This is also a good practice to keep the real source procedure of the error.
should decide the fate (commit or rollback) of a transaction it started, before it's execution ends. In this case, current count would be greater than previous count.
I would highly recommend reading Erland Sommarskog's Error and Transaction Handling in SQL Server thoroughly
create or alter proc sp_err266
as
begin
set nocount on
set xact_abort on
declare #trancount int = ##trancount
if #trancount = 0
begin tran
begin try
raiserror('Raise an unexpected error...', 16, 1);
if XACT_STATE() = 1 and #trancount = 0
commit;
end try
begin catch
if XACT_STATE() <> 0 and #trancount = 0
rollback;
else
throw;
end catch
end
If you are having a code structure of something like:
SELECT 151
RETURN -151
Then use:
SELECT 151
ROLLBACK
RETURN -151
For me two begin transactions and multi rollback transaction causing this issue.
------------------------------------------------------------
BEGIN TRANSACTION
-- BEGING TRANSACTION
call of stored procedure -- ROLLBACK TRANASCTION
-- ROLLBACK TRANSACTION
ROLLBACK TRANSACTION
-----------------------------------------------------------
It can rollback only one time, it won't have multi rollback statements, also check the return statements which is causing the issue.
In nested procedures ROLLBACK should be used with care, detailed explanation here https://stackoverflow.com/a/74479802/6204480

Is it a good thing to Raise Exceptions intentionally in PostgreSQL?

I am developing a PL function in Postgres, and in it I am modifying records of a table, according to some logic, then I execute a final query (basically counting), and if the number I get is positive, I throw an exception to rollback the transaction (since PostgreSQL's function doesn't support transactions explicitly).
So is this a good method to emulate transactions? Do you have better suggestions ?
PS: I am using PostgreSQL 9.2 but I am migrating to 9.3 soon, if this may help somehow.
If you wish to abort a transaction within a function, then yes, raising an exception is a good choice.
You can use subtransactions within PL/PgSQL functions by using BEGIN ... EXCEPTION blocks within the function. Use an inner BEGIN ... EXCEPTION block and within it RAISE an exception with a user defined SQLSTATE. Catch the exception in the EXCEPTION block.
A RAISE (of ERROR or higher) within a BEGIN ... EXCEPTION block will roll back work done within that block, as if you had used a SAVEPOINT and ROLLBACK TO SAVEPOINT.
Functions cannot force a rollback of the top level transaction; if you RAISE an exception, an outer pl/pgsql function can catch the exception in a BEGIN ... EXCEPTION block, or the client can use a ROLLBACK TO SAVEPOINT.
I have used this solution in the past. At issue is that the PLPGSQL language does not support the use of SAVEPOINT and ROLLBACK. Transactions wrap functions, not the other way around.
RAISE is the proper methodology to notify the client of some internal condition within a function. There are several levels of RAISE, the "worst" is EXCEPTION which will abort/rollback your transactions UNLESS something catches the exception and suppresses it.
For testing, use
`RAISE DEBUG 'some comment %', var;`
If you want to put something in your logs, but don't want to rollback, you can (typically) raise a WARNING. (Any level of RAISE can go to your logs, it depends on your configuration)

SQL automatically throw if a statement fails?

Is there any way to have a stored procedure automatically throw if any statement fails due to an error?
I'm inside a stored proc with a merge statement which can fail due to a primary key violation, however execution still continues.
Do I have to resort to if ##error != 0 throw ... everywhere?
EDIT: I'm using MS SQL Server 2012
EDIT: This seems to work, but is there a less verbose solution? It seems as if the introduction of try/catch makes flow jump to the catch block when an error is encountered. From there I just rethrow the exception.
begin try
....do lots of sql code
end try
begin catch
throw;
end catch
Use SET xact_abort ON at the beginning of the statement. It will cause an automatic rollback if any particular statement fails.
See What is the benefit of using "SET XACT_ABORT ON" in a stored procedure?.
Edit: the above is for SQL-Server.
How about wrapping it in a transaction so that if anything fails it will roll back any changes and you can have it return an error message.
Something like
BEGIN Transaction
--Do some code
if ##error > 0
BEGIN
--Do your throw here and then
ROLLBACK TRANSACTION
END
ELSE
BEGIN
COMMIT TRANSACTION
END

SQL: Try/Catch doesn't catch an error when attempting to access a table that it can't find

I've created a stored procedure that runs a number of commands to modify data. I only want to commit the transaction if everything succeeds. I'm doing this by using a try-catch block in the manner below (where my CATCH block in the real thing uses RAISERROR to return error messages):
BEGIN TRY
BEGIN TRANSACTION
UPDATE Table1 SET MyVarcharColumn = 'test'
UPDATE Table2 SET MyBitColumn = 1
UPDATE Table3 SET MyIntColumn = 42
COMMIT TRANSACTION
END TRY
CATCH
ROLLBACK TRANSACTION
END CATCH
That works the way I want it to. If, for example, I set MyBitColumn to 'b' instead of 1, the error is caught, control flows to the CATCH, and the transaction is not commited.
One issue I've noticed is that if, say, Table3 does not exist in the database then it errors out (invalid object name), but the CATCH block is never executed and the transaction remains open.
I want to handle this to take care of any (remote) possibility that a database gets modified (or something happens where this stored procedure is added properly, but one of the tables isn't).
How should I handle these error cases?
-Thanks for any help.
At the start of your script use SET XACT_ABORT
SET XACT_ABORT ON
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a
run-time error, the entire transaction is terminated and rolled back.
I don't think that's going to be possible:
The following types of errors are not handled by a CATCH block when
they occur at the same level of execution as the TRY…CATCH construct:
Compile errors, such as syntax errors, that prevent a batch from running.
Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of
deferred name resolution.
Ref.
The following example shows how an object name resolution error
generated by a SELECT statement is not caught by the TRY…CATCH
construct, but is caught by the CATCH block when the same SELECT
statement is executed inside a stored procedure.
USE AdventureWorks2012;
GO
BEGIN TRY
-- Table does not exist; object name resolution
-- error not caught.
SELECT * FROM NonexistentTable;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH
The error is not caught and control passes out of the TRY…CATCH
construct to the next higher level.
EXECUTE ('SELECT * FROM NonexistentTable');