Log errors on SQLException to table - mysql

I have stored procedures that are likes this overall:
BEGIN
DECLARE exit handler for sqlexception
BEGIN
SHOW ERRORS; # this shows a result set with the errors that occured
ROLLBACK; # this rollbacks all the magic i tried to to, making this whole stuff atomic, YAY!
insert into LogTest select 1,2; # this is just a test to make sure it would work
END;
START TRANSACTION;
# do some magic
COMMIT
END
Now, what i need to do is change the Handler to something like this, obviously semi-pseudocode:
DECLARE exit handler for sqlexception
BEGIN
ROLLBACK; # this rollbacks all the magic i tried to to, making this whole stuff atomic, YAY!
insert into LogTest # and this tells me what timey whimey hit the maelstorm
select now(),*,'some id', ... <some more stuff maybe?>
from (Show errors) as Errors
END;
Any one any experience/idea/knowledge?

Related

Log the triggers, procedure, function errors in separate Table - printed on the console only

I want to log the errors/exceptions for the triggers, procedures and functions in separate Table.
"my code is look like this":
CREATE PROCEDURE SILENCE_DB.GET_WEB_APPLICATION_NAVIGATION(_xxx CHAR(32))
BEGIN
-- DECLARE THE EXCEPTION AND WORNING HANDLER
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS #_no = NUMBER, #row_no = ROW_COUNT;
GET DIAGNOSTICS CONDITION #_no
#sql_sat=RETURNED_SQLSTATE, #errMsg= MESSAGE_TEXT
-- to insert the error on my error table
CALL INSERT_ERROR_TABLE(........);
RESIGNAL;
END;
DECLARE CONTINUE HANDLER FOR SQLWARNING
GET DIAGNOSTICS #_no = NUMBER, #row_no = ROW_COUNT;
GET DIAGNOSTICS CONDITION #_no
#sql_sat=RETURNED_SQLSTATE, #errMsg= MESSAGE_TEXT;
-- to insert the error on my error table
CALL INSERT_ERROR_TABLE(........);
RESIGNAL; -- DECLARE THE NOT FOUND HANDLER
DECLARE CONTINUE HANDLER FOR NOT FOUND
BEGIN
-- NOTHING TO DO
END;
................... business code
................... business code
................... business code
................... business code
END;
but if I use the RESIGNAL/SIGNAL I get the error on console but no insert into the table ERRORS, and if I remove the RESIGNAL/SIGNAL I did not get the error on the console but the error is inserted on the ERRORS table !!!
SO anyone have suggestion how to achieve my target.
The solution as Solarflare said, using the MyISAM instead of InnoDB for the INSERT_ERROR_TABLE type

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.

How can I log errors from a Stored Procedure in MySQL even when a ROLLBACK occurs?

I have the following code in a Store Procedure in MySQL (see below). When I don't put everything in a transaction, I can log errors on the Sproc directly into a table in the database. But when I put a START TRANSACTION, COMMIT, and ROLLBACK around the entire thing, my logging no longer works. I'm guessing because it's rolling back my logging? How do I get around this?
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS condition 1
#SQLState = RETURNED_SQLSTATE, #SQLMessage = MESSAGE_TEXT;
SELECT CONCAT('Database error occurred, state - ',#SQLState, '; error msg - ', #SQLMessage) INTO #errorString;
CALL Log_Errors
(#errorString,
'MySprocName',
some_variable_1,
some_variable_2);
ROLLBACK;
END;
START TRANSACTION;
-- do some stuff
-- error happens somewhere in here
COMMIT;
One option is to use a MyISAM table for logging. The MyISAM engine is not transactional. A row that is inserted into a MyISAM table stays inserted, even if a ROLLBACK is issued.
Using MyISAM for the logging table does introduce some other limitations. There's no enforcement of referential integrity (foreign key constraints). And no concurrent DML operations.
The question is old, but for who needs an answer, you must call the ROLLBACK before the LOG and then call a COMMIT:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS condition 1
#SQLState = RETURNED_SQLSTATE, #SQLMessage = MESSAGE_TEXT;
SELECT CONCAT('Database error occurred, state - ',#SQLState, '; error msg - ', #SQLMessage) INTO #errorString;
ROLLBACK;
CALL Log_Errors
(#errorString,
'MySprocName',
some_variable_1,
some_variable_2);
COMMIT;
END;
START TRANSACTION;
-- do some stuff
-- error happens somewhere in here
COMMIT;
When exiting, just do a SELECT and allow the calling application to handle logging like this:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
...
SELECT
CONCAT('Database error occurred, state - ',#SQLState, '; error msg - ', #SQLMessage) as #errorString,
'MySprocName' as proc,
some_variable_1 as var1,
some_variable_2 as var2;
ROLLBACK;
END;
START TRANSACTION;
-- do some stuff
COMMIT;
The caller PHP/Python/whatever will catch the resultset and analyze it for errors. If error is found, it will call Log_Errors. That will also allow you to log to filesystem if needed.
Here is the example of rollback code block with ability to log the error to some other table without loosing the exception details in MySQL and throwing the error again after logging it. You should get diagnostics BEFORE the ROLLBACK and the logging part - AFTER.
# CREATE PROCEDURE AND OTHER DECLARE STATEMENTS HERE
# ....
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 #sqlstate = RETURNED_SQLSTATE, #errno = MYSQL_ERRNO, #text = MESSAGE_TEXT;
ROLLBACK;
SET #full_error = CONCAT('ERR:', #errno, '(', #sqlstate, '):', #text);
CALL sp_logaction(#full_error); # Some logging procedure
RESIGNAL; # Rethrow the error into the wild
END;
# PROCEDURE BODY WITH START TRANSACTION & COMMIT HERE
# .....

MySql stored procedures, transactions and rollbacks

I can't find an optimal way to use transactions in a MySql Stored Procedure. I want to ROLLBACK if anything fails:
BEGIN
SET autocommit=0;
START TRANSACTION;
DELETE FROM customers;
INSERT INTO customers VALUES(100);
INSERT INTO customers VALUES('wrong type');
COMMIT;
END
1) Is autocommit=0 required?
2) If the second INSERT breaks (and it does of course) the first INSERT is not rolled back. The procedure simply continues down to the COMMIT. How can I prevent this?
3) I've found I can DECLARE HANDLER, should I use this instruction or is there a simpler way to say that if any command fails, the stored procedure should ROLLBACK and fail too?
DECLARE HANDLER works fine, but since I have MySql version 5.1 I can't use RESIGNAL. So if an error occurs, the caller won't be notified:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
-- RESIGNAL; not in my version :(
END;
START TRANSACTION;
Answer to 1: You don't need to set autocommit=0
With START TRANSACTION, autocommit remains disabled until you end the
transaction with COMMIT or ROLLBACK. The autocommit mode then reverts
to its previous state.
http://dev.mysql.com/doc/refman/5.6/en/commit.html
Different Approach to Answer 2: You could use a boolean variable to know if you should COMMIT or ROLLBACK. For example:
BEGIN
DECLARE `should_rollback` BOOL DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `should_rollback` = TRUE;
START TRANSACTION;
DELETE FROM customers;
INSERT INTO customers VALUES(100);
INSERT INTO customers VALUES('wrong type');
IF `should_rollback` THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END
Or, you could use your very usefull 3)