MySQL stored procedure the reason of rollback - mysql

MySQL stored procedure will throw the error out if there is no rollback command for SQLEXCEPTION, but it has changed some data before the exception.
I add rollback command for SQL exceptions:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
But I can not know the reason of the rollback now.
I know we can define every SQL exception and it's handler, but it's too complex and I just want to know why the rollback occurred.
Is there a simple method to get the reason of rollback in MySQL stored procedure?

Thanks #kordirko. I have get one solution with RESIGNAL. But it is only supported until MySQL 5.5.
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET #flag = 1;
IF #flag = 1 THEN RESIGNAL;END IF;
ROLLBACK;
END;

Related

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.

MySQL transaction doesn't return enough error message to help debug

The following transaction runs no problem. However, once a transaction is failed, for example, column number doesn't match, or any other reason, it will only show an error message "'SQLWARNING: The request at Linux system time ${LINUX_SYSTEM_TIME} insert failed. Th data will be rollbacked'" but no error message to show the actual failure. May I know if there is any MySQL way to show an error message in a MySQL transaction? Thank you.
delimiter //
CREATE PROCEDURE 123
BEGIN
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
ROLLBACK;
SELECT 'SQLWARNING: The request at Linux system time ${LINUX_SYSTEM_TIME} insert failed. Th data will be rollbacked';
END;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 'The request at Linux system time ${LINUX_SYSTEM_TIME} insert failed. Th data will be rollbacked';
END;
START TRANSACTION;
<do whatever we need in transaction>
COMMIT;
END //
CALL 123;
DROP PROCEDURE IF EXISTS
Just to highlight the answer I kindly got from ##Solarflare. Resignal is an easy solution here (MySQL exception handler access exception being handled):
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
ROLLBACK;
SELECT 'SQLWARNING: whatever error message';
RESIGNAL;
END;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 'SQLWARNING: whatever error message;'
RESIGNAL;
END;
Turn on error logging. http://www.pontikis.net/blog/how-and-when-to-enable-mysql-logs has a good tutorial.

SQLEXCEPTION handler for wrong usage of stored procedures

Is it possible to handle errors like inexistence of stored procedure and wrong number of params?
As I see, the handler like bellow doesn’t catch such errors :(
CREATE PROCEDURE _tmp_proc(
)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
IF IFNULL(#LOG_LEVEL, 1) < 2 THEN
ROLLBACK;
END IF;
SELECT 'An error has occurred';
RESIGNAL;
END;
CALL inexisted_proc();
END
Is it possible to handle errors like inexistence of stored procedure
and wrong number of params?
Yes you can but it should be handled at the calling site and not in the procedure. With calling site, I mean your application end where you are calling the stored procedure. So if you are using PHP then use the error handling mechanism try .. catch construct and in catch block either log the exception or perform as necessary.

How to catch and re-throw all errors in MySQL

I can't seem to find anywhere how to catch and re-throw any errors or warnings that can occur in a procedure.
What I want is the syntax to do the following:
create procedure myProcedure()
begin
declare exit handler for ANYTHING_WRONG_THAT_CAN_BE_CAUGHT_WARNINGS_INCLUDED
begin
rollback;
RE_THROW THE_THING_THAT_WAS_CAUGHT;
end;
start transaction;
-- do some stuff
commit;
end; //
The reason being that I want to force a rollback on an error or warning but leave it up to the client to decide what to do with the specific error.
The all-cap areas are the portions where I do not know what to put.
Thanks for any help!
Edit -------
I have since learned it is not possible to do what I have asked :'(.
Instead I have a single error for anything that goes wrong and used the following code:
declare exit handler for sqlwarning, sqlexception begin
rollback;
call error();
end;
(error() does not exist)
To catch all SQL exceptions, use:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
SQLWARNINGS can be used to catch warnings also.
Inside the exception handler, to raise the error or warning that was just caught, use:
RESIGNAL
See the documentation for the RESIGNAL statement:
http://dev.mysql.com/doc/refman/5.5/en/resignal.html
This is available since MySQL 5.5

MySQL transaction conundrum

I need to perform several inserts in a single atomic transaction. For example:
start transaction;
insert ...
insert ...
commit;
However when MySQL encounters an error it aborts only the particular statement that caused the error. For example, if there is an error in the second insert statement the commit will still take place and the first insert statement will be recorded. Thus, when errors occur a MySQL transaction is not really a transaction. To overcome this problem I have used an error exit handler where I rollback the transaction. Now the transaction is silently aborted but I don't know what was the problem.
So here is the conundrum for you:
How can I both make MySQL abort a transaction when it encounters an error, and pass the error code on to the caller?
How can I both make MySQL abort a transaction when it encounters an error, and pass the error code on to the caller?
MySQL does pass error code to the caller and based on this error code the caller is free to decide whether it wants to commit work done up to the moment (ignoring the error with this particular INSERT statement) or to rollback the transaction.
This is unlike PostgreSQL which always aborts the transaction on error and this behavior is a source of many problems.
Update:
It's a bad practice to use an unconditional ROLLBACK inside the stored procedures.
Stored procedures are stackable and transactions are not, so a ROLLBACK within a nested stored procedure will roll back to the very beginning of the transaction, not to the state of the stored procedure execution.
If you want to use transactions to restore the database state on errors, use SAVEPOINT constructs and DECLARE HANDLER to rollback to the savepoints:
CREATE PROCEDURE prc_work()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK TO sp_prc_work;
SAVEPOINT sp_prc_work;
INSERT …;
INSERT …;
…
END;
Failure in either insert will roll back all changes made by the procedure and exit it.
Using Mr. Quassnoi's example, here's my best approach to catching specific errors:
The idea is to use a tvariable to catch a simple error message, then you can catch sql states you think may happen to save custom messages to your variable:
DELIMITER $$
DROP PROCEDURE IF EXISTS prc_work $$
CREATE PROCEDURE prc_work ()
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '23000'
BEGIN
SET #prc_work_error = 'Repeated key';
ROLLBACK TO sp_prc_work;
END;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET #prc_work_error = 'Unknown error';
ROLLBACK TO sp_prc_work;
END;
START TRANSACTION;
SAVEPOINT sp_prc_work;
INSERT into test (id, name) VALUES (1, 'SomeText');
COMMIT;
END $$
DELIMITER ;
Then you just do your usual call, and do a select statement for the variable like:
call prc_work(); select #prc_work_error;
This will return either NULL if no error, or the message error in case of an error. If you need persistent error message you can optionally create a table to store it.
It's tedious and not very flexible because requires a DECLARE EXIT HANDLER segment for each status code you want to catch, it won't also show detailed error messages but hey, it works.