What will be the execution flow for the following procedure? - sql-server-2008

Below is the code for my stored procedure but I am not completely clear with the flow of execution. How the ##RAISERROR() in my case will work and how it will change the normal flow? Also what does the number 10 & 1 mean in that function. There are few other numbers also that can be used instead of 10 & 1 so how all the number affects flow of execution. Please let me know if my coding structure of style is bad/wrong or if there is any scope for improvement.
Below is my code.
CREATE PROCEDURE spAddressMaster
#Mode varchar(20),
#Street varchar(MAX),
#City varchar(300),
#State varchar(300),
#Country varchar(300),
#PostalCode int,
#Remarks varchar(MAX),
#Type varchar(300)
AS
BEGIN
BEGIN TRANSACTION
BEGIN TRY
--INSERT MODE--
IF(#Mode='INSERT')
BEGIN
INSERT INTO [AddressMST] (
[Street],
[City],
[State],
[Country],
[PostalCode],
[Remarks],
[Type]
)
VALUES (
#Street,
#City,
#State,
#Country,
#PostalCode,
#Remarks,
#Type
)
IF(##ERROR<>0)
RAISERROR('Insert Operation Fail',10,1)
END
END TRY
BEGIN CATCH
IF(##ERROR<>0)
BEGIN
ROLLBACK TRANSACTION
SELECT ERROR_MESSAGE()
END
END CATCH
END
GO

First off, you have a big problem: you have a BEGIN TRANSACTION without any COMMIT. That’s about as wrong as you can get when programming for databases! Use this general structure:
BEGIN TRANSACTION
BEGIN TRY
…do stuff…
COMMIT
END TRY
BEGIN CATCH
…error handling…
ROLLBACK
END CATCH
Next up, when you are executing code within a BEGIN TRY block, if any errors are raised execution is immediately transferrred to the BEGIN CATCH block. This means that the IF(##ERROR<>0) block will only ever be executed when no errors have occured, i.e. ##ERROR will always be 0.
As for the meaning of the 10 and 1 in the RAISERROR, that is complex, detailed, and best explained in SQL Server books online under the entry “RAISERROR”. Here’s the Link.

MSDN Doc
Generates an error message and initiates error processing for the session.
RAISERROR can either reference a user-defined message stored in the sys.messages catalog
view or build a message dynamically. The message is returned as a server error message
to the calling application or to an associated CATCH block of a TRY…CATCH construct.
For more refer link

Related

Data Truncation error in stored procedure but SSIS package not failing

I'm having stored procedure: usp_data. If I run in SSMS, am not getting any error but if I remove being and end trans then am I'm getting data truncation error. I'm using SSIS package data flow task to run this stored procedure. Job is going thru and not failing. What needs to be done to fix this SP and SSIS package. Which one needs to fix? I have put dummy fields and table name.
Server : MS SQL
Appreciate your help.
create procedure usp_data as
begin
begin trans
begin try
insert into table1 (field1, field2)
select field1,field2 from table2
commit trans
return
end try
begin catch
if ##transcount>0
begin
rollback trans
end
set #err = ERROR_MESSAGE()
RAISERROR (#err,-1,-1,'usp_data')
print(ERROR_MESSAGE())
RETURN -1
END Catch
END
An error severity of 11 or higher is needed in order for the error message to be considered an exception. A severity of 10 or less are considered informational/warning messages and do not throw an exception in consuming applications.
You can observe this in SSMS with the following:
--this is informational message
RAISERROR ('example %s',-1,-1,'usp_data');
--this is an error message
RAISERROR ('example %s',16,-1,'usp_data');
In SQL Server 2012 and later, consider using THROW to re-raise the original error. Below is the boiler plate code I suggest. Additionally, it's a good practice to specify SET XACT_ABORT ON; in stored procedures with explict transactions to ensure the transaction is rolled back immediately when a client query timeout occurs.
BEGIN CATCH
IF ##transcount > 0 ROLLBACK;
THROW;
END CATCH;

Why does this MySQL stored procedure not seem to execute its exit handler?

I have read the documentation on Exit Handlers and have found useful code ideas in relevant SO questions here and here amongst other places.
Nevertheless, calling the stored procedure below appears to complete OK and returns a TRUE value for the parameter success. when I know it is definitely not completing OK, not least because there was a syntax error in the body SQL (it was referring to a field that did not exist).
So the exit handler should have kicked in and returned FALSE for parameter success.
Can anyone help me to understand why a transaction that fails does not return the correct value for the parameter? (I suspect it has something to do with where I set success to true)
The actual SQL inside the transaction is not important to this question so I haven't shown it. Just assume that it might or might not successfully complete the transaction. It is the success or failure of that transaction that I want to detect through the parameter
DELIMITER $$
CREATE PROCEDURE do_thing (OUT success BOOLEAN)
DETERMINISTIC
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN SET success := FALSE; ROLLBACK; END; # rollback on any error
DECLARE EXIT HANDLER FOR SQLWARNING BEGIN SET success := FALSE; ROLLBACK; END; # rollback on any warning
START TRANSACTION;
< SQL that might cause an error >
< in my case it was referring to a field that didn't exist>
COMMIT;
SET success := TRUE;
END$$
DELIMITER ;
SEE DBFIDDLE
The first part is a copy of your code, it throws an error....
The second part is corrected, both DECLARE EXIT are moved within the block.
The third part is an example where #success will be set to false.

TRY/CATCH not working when table goes missing

I can't seem to get this to work... The result is SQL Management Studio seems to hang, gives me the message
Msg 208, Level 16, State 1, Line 76 Invalid object name
If I try to close the code window, I get a warning that the transaction isn't committed, and asks if I would like to commit it. If I do, the truncate has happened, and the items are missing.
I'm trying to make sure the truncate does NOT happen or gets rolled back if the table in the "INSERT" statement is missing.
BEGIN TRY
BEGIN TRAN
TRUNCATE TABLE X
INSERT INTO TABLE X ([values...]) -- pseudo code; insert works fine if table is present
SELECT * FROM <potentially missing table>
COMMIT TRAN
END TRY
BEGIN CATCH
if (##TRANCOUNT > 0)
ROLLBACK
END CATCH
Based on the information provided it looks like it may be a problem with your syntax, but it is unclear without a CREATE TABLE statement and some working code. It could also be that you're not checking if the table exists before the SELECT. I just tested the below and it has the desired results.
BEGIN TRY
BEGIN TRAN
TRUNCATE TABLE [test_table]
INSERT INTO [test_table] VALUES ('...')
SELECT * FROM [test_table]
COMMIT
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
PRINT N'rolling back transaction' --for confirmation of the catch
ROLLBACK
END CATCH
Or to avoid the TRY/CATCH use IF EXISTS to check if the table exists before starting anything.
IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'test_table')
BEGIN
BEGIN TRAN
TRUNCATE TABLE [test_table]
INSERT INTO [test_table] VALUES ('...')
SELECT * FROM [test_table]
COMMIT
END
ELSE
BEGIN
-- do something else
PRINT N'The table does not exist'
END
Hope this helps!
2020-10-11:
I tried SET XACT_ABORT ON, prior to engaging a transaction.
Interestingly, when encountering a missing table (e.g. at SELECT stmt), the script STILL halts without dropping into the TRY/CATCH block.
Moreover, and actually worse, all prior SQL output is no longer available.
Notwithstanding, the transaction was rolled back, as there is no open Transaction, thereafter.
PROOF:
executing "select ##TRANCOUNT" returns 0
manually executing "ROLLBACK TRAN" states: "The ROLLBACK TRANSACTION
request has no corresponding BEGIN TRANSACTION."
I, therefore, decided to check for required tables, at script start/prior to engaging a Transaction, instead of using XACT_ABORT.

Broker Service Transaction Management

I am implementing many SSB working on two different instances. They are data push pattern based on asynchronous triggers.
My SQL Info is as shown below:
Microsoft SQL Server Management Studio 10.50.2500.0
Microsoft Analysis Services Client Tools 10.50.2500.0
Microsoft Data Access Components (MDAC) 6.1.7601.17514
Microsoft MSXML 3.0 4.0 5.0 6.0
Microsoft Internet Explorer 9.0.8112.16421
Microsoft .NET Framework 2.0.50727.5448
Operating System 6.1.7601
My scenarios are mainly as shown below
Multiple Records are inserted as Bulk in table or One Record.
This data is sent to another DB.
The activation procedure starts between BEGIN TRAN and END TRAN.
It validates this message.
If validation not succeeded, this message should be removed from Queue and ACk is sent back to infrom that message was invalid using a different SSB objects.
Else, ACK is sent infroming message is read successfully.
Then, Activation Procedure call another Procedure to process the message body.
This USP_Process_Records is between BEGIN TRAN AND END TRAN too.
For so many reasons, this procedure might fail according to some buisness needs I've.
Either it'll Pro SQL Server 2008 Service Broker.
So in the Activation Procedure, it'll either go into failure condition for the USP_Process_Records or go to BEGIN CATCH part and rollback the transaction and send failure ACK.
At the end, I found that the previous read success ack isnt' sent at all and the second is sent normally.
So I am very confused for Transaction Management in Broker Service.
Should I use use BEGIN TRAN for each separated task and remove it from Receive and Process UPS?
Should I use TRY, CATCH inside USP_Process_Records too and return errors to USP_Receive_Records?
Should I modify my TRY, CATCH blocks ar Receive to avoid this issues
At the end, I want All acks to be sent even if something went wrong after and want to avoid Poison messages and rolling back at all.
Thanks in advance.
-BTW I've used rusanu blog for Broker Service Error Handling and Read Pro SQL Server 2008 Service Broker Transaction Management part.
Find below sample for USP.
--USP_Receive_Records
BEGIN TRY
BEGIN TRAN
WHILE 1=1
BEGIN
SELECT #ReplyMessage = NULL, #TargetDlgHandle = NULL
WAITFOR (RECEIVE TOP(1)
#TargetDlgHandle=Conversation_Handle
,#ReplyMessage = CAST(message_body AS XML)
,#ReplyMessageName = Message_Type_Name
FROM Q_Service_Receive), TIMEOUT 1000
IF #TargetDlgHandle IS NULL
BREAK
--Check if the message has the same message type expected
IF #ReplyMessageName=N'Service_Msg'
BEGIN
--Send Batch Read Success ACK
--Send ACK Here
EXEC [dbo].[USP_ACKMsg_Send] #ACKMsg, #Service_Msg;
--Handle ACK Send failed!
-- Execute the USP_Service_Msg_Process for the batch rows
EXECUTE USP_Service_Msg_Process #ReplyMessageName, #RC OUTPUT;
--Case Processing Succeeded
IF #RC=0
BEGIN
--Send Batch Read Success ACK
END
--SEND ACK Processing failed with Return Code to define cause of the error
ELSE
BEGIN
--Send Batch Processing Failed ACK
END
END
END CONVERSATION #TargetDlgHandle;
END
COMMIT TRAN;
END TRY
BEGIN CATCH
if (XACT_STATE()) = -1
BEGIN
rollback transaction;
END;
if (XACT_STATE()) = 1
BEGIN
DECLARE #error int, #message nvarchar(4000), #handle uniqueidentifier;
SELECT #error = ERROR_NUMBER(), #message = ERROR_MESSAGE();
END conversation #handle with error = #error description = #message;
COMMIT;
END
END CATCH
END
--USP_Process_Records
BEGIN TRAN
While(#nCount <= #nodesCount)
BEGIN
IF(#S_HIS_Status = '02')
BEGIN
-- check N_Paid_Trans_ID is not nuul or zero or empty
IF( #N_GET_ID IS NULL OR #N_GET_ID = 0 OR #N_GET_ID = '')
BEGIN
SET #RC = 8
RETURN;
END
EXECUTE USP_Handle_Delivered_Service #N_GET_ID, #RC OUTPUT
SELECT #myERROR = ##ERROR--, #myRowCount = ##ROWCOUNT
IF #myERROR <> 0 OR #RC <> 0
BEGIN
ROLLBACK TRAN
END
END
--A lot of similar cases
END TRAN
You are mixing BEGIN TRY/BEGIN CATCH blocks with old style ##ERROR checks. It makes both error handling and transaction handling pretty much impossible to manage. Consider this snippet of code:
SELECT #myERROR = ##ERROR--, #myRowCount = ##ROWCOUNT
IF #myERROR <> 0 OR #RC <> 0
BEGIN
ROLLBACK TRAN
END
Can you follow the control flow and the transaction flow involved here? The code is executing in the context of being called from a TRY/CATCH block, so the ##ERROR case should never occur and the control flow should jump to the CATCH block. But wait, what if the procedure is called from a different context when there is no TRY/CATCH block? then the ##ERROR case can be taken but that implies the control flow continues! Even when a TRY/CATCH contest is set up, if #RC is non-zero the transaction is rolled back but control flow continues to the next statements which will now execute in the context of per-statement standalone transactions since the overall encompassing transaction has rolled back! In other words in such case you may send an response Ack to a message you did not receive (you just rolled it back!). No wonder you are seeing cases when behavior seems erratic.
I recommend you stick with only one style of error handling (and the only sane style is BEGIN TRY/BEGIN CATCH blocks). Do not rollback intentionally in case of application logic error, but instead use RAISERROR and rely on the CATCH block to rollback as necessary. Also do style your procedure after the template shown at Exception handling and nested transactions. This template allows for message-by-message decision to rollback to a safepoint in the transaction in case of error (ie. commit your RECEIVE batch of messages, even if some of the messages occurred an error in processing).

Error handling in TSQL procedure

PROBLEM SUMMARY:
i made error handling that seems to be way too complicated and still does not solve all situations (as there can be situations where transaction gets in uncommitable state). I suspect i:
have missed something important and doing it wrong (can you explain what? and how should i do it then?).
haven't missed anything- just have to accept that error handling is still huge problem in SQL Server.
Can you offer better solution (for described situation below)?
ABOUT MY SITUATION:
I have (couple of) stored procedure in SQL Server, that is called from different places. Can generalize for 2 situations:
Procedure is called from .NET code, transaction is made and handled in SQL procedure
Procedure is called in other procedure (to be more specific- in Service Broker activation procedure), so the transaction is handled by outer procedure.
I made it so, that procedure returns result (1 for success, 0 for failure) + returns message for logging purposes in case of error.
Inside the procedure:
Set XACT_ABORT ON; -- transaction not to be made uncommitable because of triggers.
Declare #PartOfTran bit = 0; -- is used, to save status: 1- if this procedure is part of other transaction or 0- should start new transaction.
If this is part of other tran, then make save point. If not- then begin transaction.
Begin try block- do everything and if there is no mistakes AND if this is not nested transaction do commit. If it is nested transaction- commit will be made in caller procedure.
In case of error: if this is nested transaction and transaction is in commitable state- can do rollback to savepoint "MyTran". if its not part of transaction, rollback transaction called "MyTran". In all other cases- just return error code and message.
Code looks like this:
Create Procedure dbo.usp_MyProcedure
(
-- params here ...
#ReturnCode int out, -- 1 Success, != 1 Error
#ReturnMsg nvarchar(2048) out
)
AS
Begin
Set NoCount ON;
Set XACT_ABORT ON;
Declare #PartOfTran bit = 0;
IF(##TRANCOUNT > 0)
Begin
SET #PartOfTran = 1;
SAVE TRAN MyTran;
END
Else
BEGIN TRAN MyTran;
Begin Try
-- insert table1
-- update table2
-- ....
IF(#PartOfTran = 0)
COMMIT TRAN MyTran;
Select #ReturnCode = 1, #ReturnMsg = Null;
End Try
Begin Catch
IF (XACT_STATE() = 1 And #PartOfTran = 1) OR #PartOfTran = 0
Rollback Tran MyTran;
Select #ReturnCode = 0, #ReturnMsg = ERROR_MESSAGE();
End Catch
End
OTHER LITERATURE:
From my favorite bloggers have seen:
sommarskog - but i don't like that "outer_sp" has line "IF ##trancount > 0 ROLLBACK TRANSACTION", because in my case- outer procedure can be called in transaction, so in that case i have "Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0."
rusanu - actually almost the same as i wrote here (maybe idea comes from that blog post- i wrote my solution based on all i have read about this subject). This blog post still does not solve what should i do with uncommitable transactions. This is problem in case of Service Broker. How can i make correct logging of error message, if i have to rollback uncommitable transaction? i have ideas about this, but all of them seems like workarounds not elegant solutions.
You won't be able to achieve a solution that rolls back only the work done in usp_MyProcedure in any condition. Consider the most obvious example: deadlock. When your are notified of exception 1205 (you've been chosen as a deadlock victim) the transaction has already rolled back ( in order to allow progress). As error handling goes, the only safe option is to further raise and re-throw so that the caller has a chance to react. The 'uncommittable transaction' is just a variation on that theme: there is just no way error handling can recover from such a situation in a manner that is sensible for the caller, when the caller has started a transaction. The best thing is to raise (re-throw). This is why I used the pattern you've seen in my blog at Exception HAndling and Nested Transactions
Considering this in Service Broker context it means that there is no completely bullet proof, exception safe message handling routine. If you hit an uncommitable transaction (or a transaction that has already rolled back by the time you process the catch block, like 1205 deadlock) then your whole batch of received messages will have to rollback. Logging is usualy done in such situations after the outermost catch block (usually locate din the activated procedure). Here is pseudo code of how this would work:
usp_myActivatedProc
as
#commited = false;
#received = 0;
#errors = 0;
begin transaction
begin try
receive ... into #table;
#received = ##row_count;
foreach message in #table
save transaction
begin try
process one message: exec usp_myProcedure #msg
end try
begin catch
if xact_state()=1
rollback to savepoint
#errors += 1;
-- decide what to do with failed message, log
-- this failure may still be committed (receive won't roll back yet)
else
-- this is a lost cause, re-throw
raiserror
end catch
fetch next #table
endfor
commit
#commited = true;
end try
catch
#error_message = error_message();
if xact_state() != 0
rollback
end catch
if #commited = false
begin
insert into logging 'failed', #received, #error_message
end
-- insert table1
IF ##ERROR > 0
GOTO _FAIL
-- update table2
-- ....
IF ##ERROR > 0
GOTO _FAIL
GOTO _SUCCESS
_ERROR:
ROLLBACK TRAN
SET #ReturnCode = 1
RETURN
_FAIL:
ROLLBACK TRAN
SET #ReturnCode = 1
RETURN
_SUCCESS:
COMMIT TRAN
at the end of the tran insert the
GOTO _SUCCESS
so it will commit the tran if didnt hit any errors.
AND all of ur insert or update statement inside the tran insert the
IF ##ERROR > 0
GOTO _FAIL
so when it hit error.. it will go to the
_FAIL:
ROLLBACK TRAN
SET #ReturnCode = 1
RETURN
and u can set all ur return value at there