MySQL stored procedure - distinguish between note and error - mysql

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.

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

How do I handle errors in a basic MySQL stored procedure?

Let's say I have a stored procedure:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `SetupBlocks`(
IN myBlock VARCHAR(20)
)
BEGIN
INSERT INTO blocks (block)
VALUE (myBlock);
END
What if I made a mistake in my code and DB setup, and the column block only allows for 15 characters but someone using my application has been able to get 20 characters into myBlock? I would get an error similar to this for this example:
Error Code: 1406. Data too long for column 'block'
Is there some way to handle any error (not just this one) and then report it into a table called BlockSprocErrors for monitoring within MySQL and within the stored procedure itself?
Just looking for a very basic example by modifying my procedure above.
Here is what I ended up doing:
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 Collectors_Errors
(#errorString,
'Collectors_InsertAlbum',
barcodeApp,
usernameApp);
END;

MySQL exception handler access exception being handled

I'm trying to rollback on an error, but still let the client receive the error. This might actually be impossible, unless there is a way to access the error in an exception handler.
It's possible to "throw" from an exception, i.e. it's possible to raise a signal:
CREATE PROCEDURE p ()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SIGNAL SQLSTATE VALUE '99999'
SET MESSAGE_TEXT = 'An error occurred';
END;
DROP TABLE no_such_table;
END;
But this sample code from the MySQL doc looks horrible, because it literally swallows all errors and jams them into one.
SHOW ERRORS seems relevant, but I don't see any way to work with it programmatically, e.g. SELECT Code FROM (SHOW ERRORS); is not possible.
Is this possible? Is there a better practice that I'm missing entirely?
Looks like RESIGNAL is what you are looking for.
RESIGNAL makes it possible to both handle an error and return the error information. Otherwise, by executing an SQL statement within the handler, information that caused the handler's activation is destroyed. RESIGNAL also can make some procedures shorter if a given handler can handle part of a situation, then pass the condition “up the line” to another handler.
DELIMITER $$
DROP PROCEDURE IF EXISTS `test`.`resig` $$
CREATE PROCEDURE `test`.`resig` ()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SELECT 'I executed something before throwing the error' as `this_works`;
RESIGNAL;
END;
SELECT foo FROM bar WHERE baz = 0;
END $$
DELIMITER ;
mysql> call resig();
+------------------------------------------------+
| this_works |
+------------------------------------------------+
| I executed something before throwing the error |
+------------------------------------------------+
1 row in set (0.00 sec)
ERROR 1054 (42S22): Unknown column 'foo' in 'field list'
mysql>

Mysql very strange behavior

I found two very strange problems in MySQL database.
My MySQL database version is 5.6.
Problem 1:
I have simple store procedure for update column value:
Store procedure is as below:
drop PROCEDURE if exists mysql_TestProc;
CREATE PROCEDURE mysql_TestProc(Finaltable VARCHAR(1024),ColTOProcess VARCHAR(1024)
,strFind TEXT,strReplace TEXT)
Label1:BEGIN
DECLARE code VARCHAR(1024) DEFAULT '00000' ;
-- Exception Handler
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
code = RETURNED_SQLSTATE;
END;
-- generate dynamic Query
SET #s:=CONCAT('UPDATE ',FinalTable,' SET
',ColTOProcess,'=REPLACE(',ColTOProcess,',\'',strFind,'\',\'',strReplace ,'\');');
PREPARE stmt from #s;
EXECUTE stmt;
-- If any exeption during query execution then Exception Handler will
-- assign error code to "code" variable
-- else "code" variable will have default value.
IF code != '00000' THEN
-- Error found..
select code ;
LEAVE Label1;
END IF;
END;
call mysql_TestProc("AnyTableName","ColumnName","Find Value","Replace Value").
If you call above store procedure with appropriate parameters it will
successfully update value.
But in my database It successfully update value with error code "42S22".
I changed machine then everything works fine.
So This strange behavior is only with my machine and my database("_temp").
Problem 2:
I have simple procedure as below:
DROP PROCEDURE IF EXISTS mysql_PrepareLogTable;
CREATE PROCEDURE mysql_PrepareLogTable(LogTable VARCHAR(1024),code TEXT,comment TEXT,category VARCHAR(1024),timestamp DATETIME,duration VARCHAR(100),rows INT,msg TEXT)
BEGIN
SET code=CONCAT(comment,' \n ',code);
SET #tempprepare=CONCAT('INSERT INTO ',LogTable,' VALUES ("',code,'","',category,'","',timestamp,'","',duration,'",',rows,',','"',msg,'")');
PREPARE stmt from #tempprepare;
EXECUTE stmt;
END;
I can compile above store procedure script all my mysql databases except one database("test2").
Only in database "test2", I am not able to compile above store procedure script.
Even I drop the store procedure and then try to execute the script but still It failed to compile script in database "test2".
I am using Toad 6 and mysql workbench 6.0.
So, anyone has any idea about these two problems.
Thank You,
Ronak

MySQL Stored Procedure Error Handling

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.