Log exception info in MySQL stored procedure - mysql

As I know, I can define exception handler in MySQL stored procedure, but seems I can't catch the exception message in the handler and write a log in a table for debugging purpose. I just want to know is there method to log exception code and message in MySQL store procedure?

You can catch the message, the error code, the sql state, ..., with the GET DIAGNOSTICS statement, in 5.6.4
See
http://dev.mysql.com/doc/refman/5.6/en/get-diagnostics.html

I don't remember what tutorial I copied this from. However, it has helped immensely in the versions of MySQL prior to 5.6. Thanks to whomever I learned this from!
Step 1 : Create a debug_log table. This will hold everything your write to it.
CREATE TABLE `debug_log` (
`debug_log_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`msg` varchar(512) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`debug_log_id`)
) ENGINE=MyISAM
Step 2 : Create a stored procedure for adding info to the debug_log table.
DELIMITER $$
USE `your_db_name_here`$$
DROP PROCEDURE IF EXISTS `log_debug`$$
CREATE DEFINER=`ss7admin`#`%` PROCEDURE `log_debug`(IN lastMsg VARCHAR(512))
BEGIN
INSERT INTO debug_log (msg) VALUES (lastMsg);
END$$
DELIMITER ;
Step 3 : Add a handler in your real stored procedure.
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
CALL log_debug(
CONCAT
(
now(),
' : Something went horribly wrong!',
'.'
)
);
CALL log_debug('Exiting now');
SET outReturnCode = 1;
END;

You cannot catch the message, but you can catch the error code.
Here is an example of how to deal with "Duplicate entry" (PK, UK constraint):
CREATE PROCEDURE sp_do_insert(
IN in_param1 int,
IN in_param2 int,
OUT out_status tinyint
)
BEGIN
DECLARE CONTINUE HANDLER FOR 1062 SET out_status = 1;
SET out_status = 0;
INSERT INTO tbl(field1, fiel2)
VALUES (in_param1, in_param2);
END;
If tbl has a UK constraint on field1 and you try to insert an existing value once again you will not get an error. Nothing will be inserted and status will be equal to 1.
You can also add other handlers for other error codes. And you will always know what is the error from out_status value and you will know "error message" from error_code (in handler).
You can try to play with show warnings (it shows errors/warnings for the last query) in case if out_status <> 0.
Hope it helps.

Related

Do temporary tables make functions non deterministic in MySQL

I want to create a stored function in MySQL. I've been granted ALL PRIVILEGES, what I think contains also the required SUPER privilege. And binary logging is enabled.
While creating a function I get the 1419 error:
Error Code: 1419. You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
I read through the MySQL manuals and it looks like this binary logging issue should only apply to NOT DETERMINISTIC functions, which change data.
I've created a simple example function which points out my question more clearly:
DROP FUNCTION IF EXISTS getIdTest;
DELIMITER $$
CREATE FUNCTION getIdTest( pv_order_nr VARCHAR( 45 ) )
RETURNS INT UNSIGNED
COMMENT 'Gets an order number and returns an ID'
DETERMINISTIC READS SQL DATA
BEGIN
DECLARE lv_id INT UNSIGNED;
-- DOES THIS COMMAND MAKE THE FUNCTION NOT-DETERMINISTIC?
CREATE TEMPORARY TABLE tmp_log(
order_nr VARCHAR(45)
, message VARCHAR(255)
, created_at DATETIME
);
-- AND/OR DOES THIS COMMAND MAKE THE FUNCTION NON-DETERMINISTIC?
INSERT INTO tmp_log
SET order_nr = pv_order_nr
, message = CONCAT( 'Id read for order ', pv_order_nr, '.')
, created_at = NOW();
SELECT so.id_sales_order
INTO lv_id
FROM sales_order AS so
WHERE so.order_nr = pv_order_nr
LIMIT 1;
RETURN lv_id;
END
$$
DELIMITER ;
As you see my function is declared as DETERMINISTIC.
My question is, does the second statement in the function routine body (CREATE TEMPORARY TABLE) make the function NOT DETERMINISTIC?
If I omit this statement, does the third statement (INSERT INTO --a temporary table--) make the function NOT DETERMINISTIC as well?
Thanks for reading this :)
Felix

call simple database procedure using Slick 3.0

I have written a simple database procedure in mySQL as follows:
DROP PROCEDURE IF EXISTS sp_doSomething;
DELIMITER //
CREATE PROCEDURE sp_doSomething
(IN pVal1 varchar(100),
IN pVal2 int(15)
)
BEGIN
DECLARE vCnt int(5) DEFAULT 0;
DECLARE vID int(15) DEFAULT 0;
DECLARE vTempID int(15) DEFAULT 0;
-- get ID
SELECT id INTO vID FROM T1
WHERE name = pVal1;
-- get count
SELECT count(*) INTO vCnt FROM T1
WHERE owner = vID;
-- get the log
INSERT INTO log select CONCAT('-v1-:', pVal1, ':-v2-:', pVal2);
-- Create basic stuff if it doesn't exist
IF vFolderCnt = 0 THEN
INSERT INTO T1 (`id`, `col1`, `col2`, `col3`)
SELECT null, vID, 'some value', CONCAT(vID,'^1') FROM T1
WHERE owner = 0;
END IF;
commit;
END //
DELIMITER ;
Now, I want to call this procedure in my Play Framework 2.4 application which uses Slick 3.0. It is such a simple thing but I am really struggling with it as there is no proper documentation available. It's very frustrating.
As it's mentioned on Google Group here https://groups.google.com/forum/#!searchin/scalaquery/procedure/scalaquery/BUB2-ryR0bY/EFZGX663tRYJ
I tried calling the procedure by different way. The code compiles but the procedure does not get called at all.
This statement gives an action error.
db.run(sql"{call sp_doSomething('${st.val1}', 1)}")
The following statement compiles fine but does not invoke the procedure.
db.run(sql"{call sp_doSomething('${st.val1}', 1)}".as[Int])
The following statement compiles fine but does not invoke the procedure.
db.run(sqlu"{call sp_doSomething('${st.val1}', 1)}")
Or
db.run(sqlu"{?=call sp_doSomething('${st.val1}', 1)}")
I have granted the Execute permission on the procedure to my DB user and verified it.
Also, I am not sure, whether the COMMIT is required in the procedure or not?
Any help, will be highly appreciated.
I have managed to invoke the stored procedure using old prepareCall method. Here's how I have done it. Hope, it might help someone.
db.withSession {
implicit session => {
val cs = session.conn.prepareCall("{call sp_doSomething(?, ?)}")
cs.setString(1, st.val1)
cs.setLong(2, 1L)
val result = cs.executeUpdate()
}
}
But I would be still interested in invoking the procedure using sql"" or sqlu"".
For me the following works in Slick 3.2:
delimiter //
create procedure Try1()
begin
select userid from TBL_USER where id = "4";
end //
delimiter ;
Then
def runProcByRawSql() : Future[Vector[String]] = {
def runproc = sql"""call Try1()""".as[String]
db.run(runproc)
}

Weird issue with a stored procedure in MySQL

I need to add a new stored procedure on our company's MySQL server. Since it's just slightly different, I used an already existing one, added the additional field and changed the name of the procedure. The weird thing now is that when I want to execute the statement, it returns:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 3
reffering to the 0 in this line: SET #update_id := 0; What makes it weird is, that I queried that stored procedure by using SHOW CREATE PROCEDURE . It's saved in our database and is working fine. I just can't use it as a new stored procedure (no matter if I try to apply it to the new test database or if I use it on the existing database by giving it a new name).
I searched the internet for a solution. Unfortunately to no avail. I even set up a new database with a new table and some demo values where I tried to execute the original, unaltered stored procedure. It returns the exact same error.
Here's the currently used and working stored procedure I'm talking about:
CREATE DEFINER=`root`#`localhost` PROCEDURE `customer_getcard`(IN Iinstance INT, IN Itimebuy DOUBLE, IN Iprice DECIMAL(10,2), IN Itariff INT, IN Icomment VARCHAR(128))
BEGIN
SET #update_id := 0;
UPDATE customer_shop SET state = 1, id = (SELECT #update_id := id), instance=Iinstance, timebuy=Itimebuy, price=Iprice, comment=Icomment WHERE tariff=Itariff AND state = 0 LIMIT 1;
SELECT * FROM customer_shop WHERE id = #update_id;
END
I hope you guys can help me as I am completely out of ideas what's wrong. :/
Regards, Mark
You need to define an alternative command delimiter, as MySQL currently thinks your CREATE PROCEDURE command ends at the first ; it encounters (on line 3, after the 0), which would be a syntax error as it's after a BEGIN but before the corresponding END:
DELIMITER ;; -- or anything else you like
CREATE PROCEDURE
...
END;; -- use the new delimiter you chose above here
DELIMITER ; -- reset to normal
MySQL stored procedures do not use ":=" for value assignment, just use "=".
Also don't think "id = (SELECT #update_id := id)" is acceptable. Here's an alternative solution (untested):
CREATE DEFINER=`root`#`localhost` PROCEDURE `customer_getcard`(IN Iinstance INT, IN Itimebuy DOUBLE, IN Iprice DECIMAL(10,2), IN Itariff INT, IN Icomment VARCHAR(128))
BEGIN
select id into #update_id from customer_shop WHERE tariff=Itariff AND state = 0 LIMIT 1;
UPDATE customer_shop SET state = 1, instance=Iinstance, timebuy=Itimebuy, price=Iprice, comment=Icomment where id = #update_id;
SELECT * FROM customer_shop WHERE id = #update_id;
END
You may also want to put error handlers in case there's no matching row to be edited.

Fixing "No data - zero rows fetched, selected, or processed" warning

The below function works fine except it throws a warning 'No data - zero rows fetched, selected, or processed (errno. 1329)'. and since i start using this function with django there cant be any warning or error because it stop the whole process
any idea how to fix this?
DELIMITER $$
DROP FUNCTION IF EXISTS objtree_node_add $$
CREATE FUNCTION objtree_node_add(i_name VARCHAR(255), i_parent_id BIGINT, i_type_id BIGINT) RETURNS bigint(20)
BEGIN
DECLARE a_name VARCHAR(255);
IF NOT i_name RLIKE '^[a-zA-Z0-9_-]+$' THEN
RETURN -1;
END IF;
SELECT name INTO a_name FROM objtree_nodes WHERE parent_id = i_parent_id AND name = i_name;
IF NOT a_name IS NULL THEN
RETURN -5;
END IF;
...
I don't know where you read that there is no warnings filtering in Django. Django is just Python, so you can use the Python warnings module.
import warnings
warnings.filterwarnings("ignore", "No data .*")
import warnings
warnings.filterwarnings("ignore", "No data .*")

Perl DBI execute not maintaining MySQL stored procedure results

I'm having a problem with executing a stored procedure from Perl (using the DBI Module). If I execute a simple SELECT * FROM table there are no problems.
The SQL code is:
DROP FUNCTION IF EXISTS update_current_stock_price;
DELIMITER |
CREATE FUNCTION update_current_stock_price (symbolIN VARCHAR(20), nameIN VARCHAR(150), currentPriceIN DECIMAL(10,2), currentPriceTimeIN DATETIME)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE outID INT;
SELECT `id` INTO outID FROM `mydb449`.`app_stocks` WHERE `symbol` = symbolIN;
IF outID > 0 THEN
UPDATE `mydb449`.`app_stocks`
SET `currentPrice` = currentPriceIN, `currentPriceTime` = currentPriceTimeIN
WHERE `id` = outID;
ELSE
INSERT INTO `mydb449`.`app_stocks`
(`symbol`, `name`, `currentPrice`, `currentPriceTime`)
VALUES (symbolIN, nameIN, currentPriceIN, currentPriceTimeIN);
SELECT LAST_INSERT_ID() INTO outID;
END IF;
RETURN outID;
END|
DELIMITER ;
The Perl code:
$sql = "select update_current_stock_price('$csv_result[0]', '$csv_result[1]', '$csv_result[2]', '$currentDateTime') as `id`;";
My::Extra::StandardLog("SQL being used: ".$sql);
my $query_handle = $dbh->prepare($sql);
$query_handle->execute();
$query_handle->bind_columns(\$returnID);
$query_handle->fetch();
If I execute select update_current_stock_price('aapl', 'Apple Corp', '264.4', '2010-03-17 00:00:00') asid; using the mysql CLI client it executes the stored function correctly and returns an existing ID, or the new ID.
However, the Perl will only return a new ID, (incrementing by 1 on each run). It also doesn't store the result in the database. It looks like it's executing a DELETE on the new id just after the update_current_stock_price function is run.
Any help? Does Perl do anything funky to procedures I should know about?
Before you ask, I don't have access to binary logging, sorry.
Perhaps you're doing it in a transaction and it's getting rolled back? The row is inserted but never becomes committed and cannot be seen.
I'd try it on your dev server and enable general query log, if in doubt.
Also you may want to know about the INSERT ... ON DUPLICATE KEY UPDATE syntax, which can probably do what you're trying to do anyway.
try
$query_handle->dump_results(15, "\n", '|');
before the bind_columns call to see if it is actually getting the results back, you could also try replace SELECT storedprocedure with SELECT * FROM storedprocedure
You should check that you are running the latest version of DBD::mysql (which is the MySQL-driver used by DBI). There used to be several issues with stored procedures, at least some are fixed in recent versions. Maybe these ressources are also helpful:
http://www.perlmonks.org/?node_id=609098
http://www.perlmonks.org/?node_id=830585