I believe there is nothing currently available in MySQL that allows access to the SQLSTATE of the last executed statement within a MySQL stored procedure. This means that when a generic SQLException is raised within a stored procedure it is hard/impossible to derive the exact nature of the error.
Does anybody have a workaround for deriving the SQLSTATE of an error in a MySQL stored procedure that does not involve declaring a handler for every possible SQLSTATE?
For example - imagine that I am trying to return an error_status that goes beyond the generic "SQLException happened somewhere in this BEGIN....END block" in the following:
DELIMITER $$
CREATE PROCEDURE `myProcedure`(OUT o_error_status varchar(50))
MY_BLOCK: BEGIN
DECLARE EXIT handler for 1062 set o_error_status := "Duplicate entry in table";
DECLARE EXIT handler for 1048 set o_error_status := "Trying to populate a non-null column with null value";
-- declare handlers ad nauseum here....
DECLARE EXIT handler for sqlexception set o_error_status:= "Generic SQLException. You'll just have to figure out the SQLSTATE yourself...." ;
-- Procedure logic that might error to follow here...
END MY_BLOCK$$
Any tips?
PS I am running MySQL 5.1.49
I believe there is nothing currently available in MySQL that allows access to the SQLSTATE of the last executed statement within a MySQL stored procedure. This means that ... it is hard/impossible to derive the exact nature of the error.
Luckily that is not true.
SHOW ERRORS LIMIT 1 -- for SQL-state > 2
SHOW WARNINGS LIMIT 1 -- for SQL-state 1,2
Will show the last error or warning.
In order to prevent listing each and every error, you can handle a class of SQL-errors like so:
SQLWARNING is shorthand for the class of SQLSTATE values that begin with '01'.
NOT FOUND is shorthand for the class of SQLSTATE values that begin with '02'. This is relevant only within the context of cursors and is used to control what happens when a cursor reaches the end of a data set. If no more rows are available, a No Data condition occurs with SQLSTATE value 02000. To detect this condition, you can set up a handler for it (or for a NOT FOUND condition). An example is shown in Section 12.7.5, “Cursors”. This condition also occurs for SELECT ... INTO var_list statements that retrieve no rows.
SQLEXCEPTION is shorthand for the class of SQLSTATE values that do not begin with '00', '01', or '02'.
So to handle an exception, you need to only do:
DECLARE EXIT HANDLER FOR SQLSTATE SQLEXCEPTION .....;
Links:
http://dev.mysql.com/doc/refman/5.5/en/signal.html
http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html
GET DIAGNOSTICS is available in 5.6.4
See
http://dev.mysql.com/doc/refman/5.6/en/get-diagnostics.html
I am doing the following workaround: using a SELECT to provocate an error. For example:
SELECT RAISE_ERROR_unable_to_update_basket;
This will result in the following error message (example):
ERROR 1054 (42S22): Unknown column 'RAISE_ERROR_unable_to_update_basket' in 'field list'
I am wrapping my call to a stored procedure in a try { ... } catch { ... } and now can handle this error. This will of course only work for provocating custom error messages from inside your stored procedure and will not handle any SQL or database errors, that might occur (because of duplicate-key entry). In the latter case, you might be able to workaround this using the solution of Johan.
Related
I am trying to create an after insert trigger in MySQL that writes the message "Insert Failed" to an error table called tbl_error if the trigger fails for any reason.
To solve this problem, I have attempted to use a combination of handlers and signal statements. None have worked.
DELIMITER $$
CREATE TRIGGER trig_statesout_afterinsert AFTER INSERT ON states
FOR EACH ROW
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SIGNAL SQLSTATE VALUE '99999'
SET MESSAGE_TEXT = 'Insert Failed';
INSERT INTO tbl_error(ErrorMessage) Values ('Insert Failed');
END;
INSERT INTO statesout (states_id, state, cases, lastupdate)
SELECT s.states_id, s.state, s.cases, current_timestamp()
FROM states as s
WHERE s.states_id = NEW.states_id;
END $$
I attempted to return information from a diagnostic statement, but that didn't work either.
Because the documentation states that
For SQLEXCEPTION conditions, the stored program terminates at the
statement that raised the condition, as if there were an EXIT handler.
If the program was called by another stored program, the calling
program handles the condition using the handler selection rules
applied to its own handlers.
I split the update logic for the trigger into a separate stored procedure in case the issue was that the SQLEXCEPTION conditions cannot be in the calling object. It didn't work.
Some SO solutions suggest using a "trick" to call a nonexistent table, but the documentation says this is not necessary due to the SIGNAL statement:
Without SIGNAL, it is necessary to resort to workarounds such as
deliberately referring to a nonexistent table to cause a routine to
return an error.
In any case, those solutions only throw errors based on a specific condition, not for any condition.
What am I doing wrong? Is it impossible for a MYSQL trigger to return an error message if the trigger fails for any reason?
It's not possible to make any change to data if the trigger activates a SQLException.
If the trigger experiences any error, whether handled or not handled, then the INSERT that spawned the trigger is undone, as well as any SQL run by the trigger, and even any SQL run by triggers spawned by actions your trigger executes.
This is related to the idea of atomicity — either the whole action must succeed, or else no part of the action is saved. It's all or nothing.
If you want to log the error, you'll have to check for the error in your application, and then log the error as a separate action. You can't do it in the trigger body.
I have a MySQL database and a bunch of stored procedures.
I have a test SQL file which calls the stored procedures to check that they succeed on sample data and populate a test database with example records.
I would also like to have a set of statements which, in pseudocode, would be
SELECT (CALL SomeProc('invalid argument') EMITS ERROR) AS SomeProcCheck;
In this imaginary example, SomeProc is written as
CREATE STORED PROCEDURE SomeProc (arg TEXT)
BEGIN
IF (IsNotValid(arg))
THEN
SIGNAL SQLSTATE '45000';
END IF;
INSERT INTO Foo (...);
END
I want my test database init script to verify that failure branches are being hit under the right circumstances.
Can I do this within MySQL?
Use a continue handle and gobble the error, if your call succeeds then signal an error:
DECLARE signalled INT DEFAULT 0;
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '45000'
BEGIN
SET signalled = 1;
END;
CALL SomeProc('invalid argument');
END
IF (signalled = 0)
THEN
SIGNAL SQLSTATE '45000';
END IF;
Note the scope of the handler so it doesn't handle the second SIGNAL in case the error condition did not happen.
I think an exit handler without the signalled flag and subsequent test would also work, but I'm not sure whether the exit is global or just the scope of the handler... this brings me to I don't have a MySql db to test this so sorry for any syntax errors/bugs.
I dont remember exactly the specific but using something along the lines of start transaction and set autocommit and rollback if something fails into your transaction
I have a stored procedure that executes stored SQL.
However, the error-handler kicks-in and exits if the user attempts to execute
drop temporary table if exists t_person;
and 't_person' doesn't exist. I'm perfectly happy to generate an error when 'if exists' is not given, but how do I avoid an error for the earlier case (the error-code is unchanged)?
Here's my error handler:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
set #sql = 'select \'Invalid SQL or bad parameter resulting in invalid SQL\' as `Error`';
prepare stmt from #sql;
execute stmt;
END;
You could use a CONTINUE handler rather an an EXIT handler that catches MySQL error 1051 "Unknown table"...
DECLARE CONTINUE HANDLER FOR 1051 BEGIN END;
-or-
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' BEGIN END;
EDIT
To catch a MySQL error in an exception handler, you need to specify the MySQL error number or the corresponding SQLSTATE to be caught. (You could specify a named condition, but that named condition has to resolve to a MySQL error number or SQLSTATE).
A syntax error would throw MySQL error 1064.
If a table foo exists, and you issue a
CREATE TEMPORARY TABLE IF NOT EXISTS `foo` (id INT);
That would throw MySQL error 1050.
To catch that error, declare another handler for that. Assuming you want to "swallow" the exception and continue processing...
DECLARE CONTINUE HANDLER FOR 1050 BEGIN END;
Reference: https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html
The like p_person in the drop temporary table statement looks wrong to me; at least, I'm not familiar with using the LIKE keyword in a DROP TABLE statement.
I am Writing a MYSQL Stored Procedure. In that i know using HANDLER we can check the condition for SELECT STATEMENT.
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET NO_MORE_ROWS = TRUE;
In DB2 we can check the return code using SQLCODE immediatly after the statement.
Please let me know how i need to handle the Duplicate Index, More than one Row, Cursor Problem, Data mismatch in Source variable against table fields. Is there a way in MYSQL.
Basically i want to know in MYSQL How to handle the below condition.
SELECT - NOT FOUND, MORE THAN ONE ROW, EXCEPTION, DATATYPE MISMATCH IN TO CONDITION
INSERT - DUPLICATE INDEX, DATATYPE MISMATCH, OTHER INERT FAILURE
UPDATE - NO DATA FOR UPDATE, UPDATE WHERE CONDITION NOT MET
DELETE - NOT DELETED BECAUSE OF FOREIGN KEY ISSUE.
CURSOR - CURSOR FAILURE
Thanks.
As documented under DECLARE ... HANDLER Syntax:
The condition_value for DECLARE ... HANDLER indicates the specific condition or class of conditions that activates the handler:
A MySQL error code (a number) or an SQLSTATE value (a 5-character string literal). You should not use MySQL error code 0 or SQLSTATE values that begin with '00', because those indicate success rather than an error condition. For a list of MySQL error codes and SQLSTATE values, see Section C.3, “Server Error Codes and Messages”.
I'm using MySQL 5.5.To get the exception message on MySQL 5.6 is using GET DIAGNOSTIC function. Is there any similar function in MySQL 5.5 ,.? The project I'm working is already use MySQL version 5.5.
You could try using SHOW ERROR and SHOW WARNING. To see the last error or warning you could use it as:
SHOW ERRORS LIMIT 1 -- for SQL-state > 2
SHOW WARNINGS LIMIT 1 -- for SQL-state 1,2
In order to prevent listing each and every error, you can handle a class of SQL-errors like so:
SQLWARNING is shorthand for the class of SQLSTATE values that begin
with '01'.
NOT FOUND is shorthand for the class of SQLSTATE values that begin
with '02'. This is relevant only within the context of cursors and is
used to control what happens when a cursor reaches the end of a data
set. If no more rows are available, a No Data condition occurs with
SQLSTATE value 02000. To detect this condition, you can set up a
handler for it (or for a NOT FOUND condition). An example is shown in
Section 12.7.5, “Cursors”. This condition also occurs for SELECT ...
INTO var_list statements that retrieve no rows.
SQLEXCEPTION is shorthand for the class of SQLSTATE values that do not
begin with '00', '01', or '02'.
So to handle an exception, you need to only do:
DECLARE EXIT HANDLER FOR SQLSTATE SQLEXCEPTION .....;
Links:
http://dev.mysql.com/doc/refman/5.5/en/signal.html
http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html