How to implement Roll Back Transaction and Error Message in MYSQL - mysql

I Have sample procedure .
CREATE PROCEDURE `sample_procedure `
(
IN IDIn bigint(20),
IN MainIDIn bigint(20),
IN NameIn varchar(20)
)
READS SQL DATA
DETERMINISTIC
BEGIN
INSERT INTO tbl_DEPT
(
ID,
Name)
Select 1,'Mohan';
IF NOT EXISTS (SELECT ID FROM tbl_emp te WHERE te.ID = IDIn) THEN
INSERT INTO tbl_emp
(
MainID,
Name)
VALUES (MainIDIn,
NameIn);
ELSE
IF EXISTS (SELECT ID FROM tbl_emp te WHERE te.ID = IDIn) THEN
UPDATE tbl_emp
set
MainID =MainIDIn,
name = NameIn
WHERE te.ID= IDIn;
END IF;
END IF;
END
Call sample_procedure(1,2,Sampl123)
I'm just sending some irrelevant Data into the Procedure so that procedure gets failed . But how we need to implement roll back means it should come to the starting state with out Inserting Records into the tbl_DEPT also.
IN T-SQL we will have
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION
BEGIN TRY
SET #OUT = "success";
COMMIT TRANSACTION
END TRY
BEGIN CATCH
set #out = 'not success';
ROLLBACK TRANSACTION
END CATCH
END
this kind of TRY CATCH blocks and to capture Error
"ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity"
In the same way in MYSQL I'm looking for TRY CATCH and ROLL BACK Mechanism .
IF Procedure fails it should ROLL BACK and Not to load in any of the table .
Can any one Suggest me in MYSQL.

Mysql does not use try/catch. If uses handlers, e.g. the EXIT handler that will terminate the execution of the SP:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
-- other logic
END;
There are other type of handlers as well.

Related

Nested procedure - Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT

I get an error
Custom error message. Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0. Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
My SQL Code is following
Procedure 1
CREATE PROCEDURE [dbo].[spProcedure1]
#Id int
WITH EXECUTE AS OWNER
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
EXEC spProcedure2 #Id
-- Code ommited, Next procedures execution
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
-- Code ommited, saving error to Log table
RAISERROR(#errorMessage, #errorSeverity, #errorState);
END CATCH
END
Procedure 2
CREATE PROCEDURE [dbo].[spProcedure2]
#Id int
WITH EXECUTE AS OWNER
BEGIN
BEGIN TRY
-- Check if record exists
IF NOT EXISTS(SELECT * FROM dbo.Table t WHERE t.Id = #Id)
BEGIN
-- stop procedure due to user error
RAISERROR('Custom error message.',16,1)
RETURN
END
-- Code ommited, DML operation
END TRY
BEGIN CATCH
-- Code ommited, saving error to Log table
RAISERROR(#errorMessage, #errorSeverity, #errorState);
END CATCH
END
I am not sure how to handle transaction in the Procedure 2 to obey this error ?

SQL Server 2008 : Error handling in SP

I have a stored procedure that has 2 flaws:
If there is an error in the stored procedure, it keeps executing. I want the stored procedure to stop immediately once there is any error found.
There is an error log table for this stored procedure, I have customized the error and if the error do happen, it repeats the same error message multiple times because of the while loop used in the stored procedure. I want the error to be logged only once per occurrence.
This is the general outline of the stored procedure code:
ALTER PROCEDURE [sp_abc]
AS
BEGIN
create table #abc1 (id int, name char(20), done bit default 0)
values (1,'ben'), (2,'charles')
WHILE (SELECT count(*) from #abc1 where done = 0) > 0
BEGIN
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO abc123 (col1, col2)
VALUES (1,2), (3,4)
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE #error_number INT
, #error_message VARCHAR(MAX)
SET #error_number = ERROR_NUMBER()
SET #error_message = ERROR_MESSAGE() + ' [Trying to insert data into abc123]'
ROLLBACK TRANSACTION
-- Insert error into an error table
INSERT INTO [dbo].[error_log] ( sp_name,[error_message] )
SELECT DISTINCT ' sp123 ', #error_message
END CATCH
END

Mysql nested transaction with rollback

Can someone tell me if it is possible to call another procedure from within a procedure and if any part of either procedure fails, roll everything back?
If this is possible, can someone please show me a tiny example of how this would be implemented?
EDIT: Procedure "b" fails but procedure "a" still inserts a row into table "a". It's my understanding that if any part of the insert fails that everything (both inserts) is rolled back which is not happening here. The questions is why not?
Procedure "a"
BEGIN
DECLARE b INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
INSERT INTO a(a)
VALUES(iA);
CALL b(iB,LAST_INSERT_ID(),#b);
SELECT #b INTO b;
IF b !=1 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END
Procedure "b"
BEGIN
DECLARE b INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
INSERT INTO b VALUES(iB,id);
SET b=1;
COMMIT;
END;
You will need to handle transactions in both procedures, but the proc that is calling the other, should check for the return value and rollback it's transactions based on that. Here is an example of the inner proc:
How to detect a rollback in MySQL stored procedure?
you would then check for p_return_code and do a rollback of the parent transaction.
EDIT:
What I think is happening is that inner SP COMMIT or ROLLBACK affect outer SP TRANSACTION. This code works for me, if inner SP fail it rolls back both insert statements. First call to ab() works, new user record gets inserted and new game record gets inserted, if we remove record from the games table and run ab() again, because user id already exists it rolls back games table insert:
create procedure ab()
BEGIN
START TRANSACTION;
INSERT INTO games (title) VALUES ('bad game');
CALL ba(#ret);
IF #ret!=0 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END;
create procedure ba(OUT return_value tinyint unsigned)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
set return_value = 1;
END;
INSERT INTO users (id) VALUES(1);
set return_value = 0;
END;
To test use call ab();

How can I see errors from a MySQL stored procedure?

With regards to using MySQL stored procedures with transactions, and I am having a problem getting error output.
The problem is that I need to set an exit_handler to roll back the transaction if anything fails. But when I do this, I don't get any error output if something goes wrong. For example if I accidentally pass a NULL value and try to insert it into a non-null field.
I am using a return value to programmatically indicate success or failure, however this does nothing to tell me what actually went wrong.
I am using Perl DBI to talk to MySQL. I am using MySQL 5.0.92 on the production server and MySQL 5.0.51a on the development server. Upgrading to a newer version of MySQL is politically untenable.
This is a simplified example:
DELIMITER //
CREATE PROCEDURE pmt_new(
app_id varchar(40),
out ret tinyint unsigned,
out pmt_req_id int(10) unsigned)
BEGIN
DECLARE v_pmt_req_type int(10) unsigned;
DECLARE exit handler for not found, sqlwarning, sqlexception rollback;
set ret=1;
START TRANSACTION;
SELECT pmt_type INTO v_pmt_req_type FROM pmt_req_types WHERE pmt_req_name = 'Name 1';
INSERT INTO pmt_reqs (pmt_req_id, pmt_req_type, app_id)
values (null, v_pmt_req_type, app_id);
set pmt_req_id = last_insert_id();
INSERT INTO other (pmt_req_id) values (pmt_req_id);
COMMIT;
set ret=0;
END//
DELIMITER ;
Instead of just doing a rollback in your exit handler you need to return something as well...
You currently have
DECLARE exit handler for not found, sqlwarning, sqlexception rollback;
Change it to something like...
DECLARE exit handler for not found, sqlwarning, sqlexception
begin
rollback;
select "We had to rollback, error!";
end;
In 5.5 they added the SIGNAL/RESIGNAL statements so you could 'return' an error but prior versions you have to kind of roll your own solution. If you need you can declare multiple exit handlers to tailor the output better, or setup your own error table you can pull from.
You can also do input testing inside your stored procedure. Want to know if app_id is null?
DELIMITER //
CREATE PROCEDURE pmt_new(
app_id varchar(40),
out result varchar(256),
out ret tinyint unsigned,
out pmt_req_id int(10) unsigned)
BEGIN
DECLARE v_pmt_req_type int(10) unsigned;
DECLARE exit handler for not found, sqlwarning, sqlexception rollback;
SET ret=1;
SET result = "";
IF app_id IS NULL THEN
set result = "Supplied ID is Null";
ELSE
START TRANSACTION;
SELECT pmt_type INTO v_pmt_req_type FROM pmt_req_types WHERE pmt_req_name = 'Name 1';
INSERT INTO pmt_reqs (pmt_req_id, pmt_req_type, app_id)
values (null, v_pmt_req_type, app_id);
set pmt_req_id = last_insert_id();
INSERT INTO other (pmt_req_id) values (pmt_req_id);
COMMIT;
set ret=0;
END IF;
END//
DELIMITER ;
Doing it this way adds another out parameter, but gives you much better information. You could do the same with multiple exit handlers.

Mysql - How to quit/exit from stored procedure

I have very simple question but i did't get any simple code to exit from SP using Mysql.
Can anyone share with me how to do that?
CREATE PROCEDURE SP_Reporting(IN tablename VARCHAR(20))
BEGIN
IF tablename IS NULL THEN
#Exit this stored procedure here
END IF;
#proceed the code
END;
CREATE PROCEDURE SP_Reporting(IN tablename VARCHAR(20))
proc_label:BEGIN
IF tablename IS NULL THEN
LEAVE proc_label;
END IF;
#proceed the code
END;
If you want an "early exit" for a situation in which there was no error, then use the accepted answer posted by #piotrm. Most typically, however, you will be bailing due to an error condition (especially in a SQL procedure).
As of MySQL v5.5 you can throw an exception. Negating exception handlers, etc. that will achieve the same result, but in a cleaner, more precise manner.
Here's how:
DECLARE CUSTOM_EXCEPTION CONDITION FOR SQLSTATE '45000';
IF <Some Error Condition> THEN
SIGNAL CUSTOM_EXCEPTION
SET MESSAGE_TEXT = 'Your Custom Error Message';
END IF;
Note SQLSTATE '45000' equates to "Unhandled user-defined exception condition". By default, this will produce an error code of 1644 (which has that same meaning). Note that you can throw other condition codes or error codes if you want (plus additional details for exception handling).
For more on this subject, check out:
https://dev.mysql.com/doc/refman/5.5/en/signal.html
How to raise an error within a MySQL function
http://www.databasejournal.com/features/mysql/mysql-error-handling-using-the-signal-and-resignal-statements.html
Addendum
As I'm re-reading this post of mine, I realized I had something additional to add. Prior to MySQL v5.5, there was a way to emulate throwing an exception. It's not the same thing exactly, but this was the analogue: Create an error via calling a procedure which does not exist. Call the procedure by a name which is meaningful in order to get a useful means by which to determine what the problem was. When the error occurs, you'll get to see the line of failure (depending on your execution context).
For example:
CALL AttemptedToInsertSomethingInvalid;
Note that when you create a procedure, there is no validation performed on such things. So while in something like a compiled language, you could never call a function that wasn't there, in a script like this it will simply fail at runtime, which is exactly what is desired in this case!
To handle this situation in a portable way (ie will work on all databases because it doesn’t use MySQL label Kung fu), break the procedure up into logic parts, like this:
CREATE PROCEDURE SP_Reporting(IN tablename VARCHAR(20))
BEGIN
IF tablename IS NOT NULL THEN
CALL SP_Reporting_2(tablename);
END IF;
END;
CREATE PROCEDURE SP_Reporting_2(IN tablename VARCHAR(20))
BEGIN
#proceed with code
END;
This works for me :
CREATE DEFINER=`root`#`%` PROCEDURE `save_package_as_template`( IN package_id int ,
IN bus_fun_temp_id int , OUT o_message VARCHAR (50) ,
OUT o_number INT )
BEGIN
DECLARE v_pkg_name varchar(50) ;
DECLARE v_pkg_temp_id int(10) ;
DECLARE v_workflow_count INT(10);
-- checking if workflow created for package
select count(*) INTO v_workflow_count from workflow w where w.package_id =
package_id ;
this_proc:BEGIN -- this_proc block start here
IF v_workflow_count = 0 THEN
select 'no work flow ' as 'workflow_status' ;
SET o_message ='Work flow is not created for this package.';
SET o_number = -2 ;
LEAVE this_proc;
END IF;
select 'work flow created ' as 'workflow_status' ;
-- To send some message
SET o_message ='SUCCESSFUL';
SET o_number = 1 ;
END ;-- this_proc block end here
END
Why not this:
CREATE PROCEDURE SP_Reporting(IN tablename VARCHAR(20))
BEGIN
IF tablename IS NOT NULL THEN
#proceed the code
END IF;
# Do nothing otherwise
END;
MainLabel:BEGIN
IF (<condition>) IS NOT NULL THEN
LEAVE MainLabel;
END IF;
....code
i.e.
IF (#skipMe) IS NOT NULL THEN /* #skipMe returns Null if never set or set to NULL */
LEAVE MainLabel;
END IF;
I think this solution is handy if you can test the value of the error field later. This is also applicable by creating a temporary table and returning a list of errors.
DROP PROCEDURE IF EXISTS $procName;
DELIMITER //
CREATE PROCEDURE $procName($params)
BEGIN
DECLARE error INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET error = 1;
SELECT
$fields
FROM $tables
WHERE $where
ORDER BY $sorting LIMIT 1
INTO $vars;
IF error = 0 THEN
SELECT $vars;
ELSE
SELECT 1 AS error;
SET #error = 0;
END IF;
END//
CALL $procName($effp);