mysql transaction error handling - mysql

DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING, NOT FOUND
BEGIN
ROLLBACK;
END;
START TRANSACTION;
UPDATE tbl_order SET TransactionID="abc" WHERE OrderID=1;
UPDATE tbl_order SET TransactionID="xyz" WHERE OrderID=;
UPDATE tbl_order SET TransactionID="zzz" WHERE OrderID=13;
COMMIT;
for some reason order 1 and 13 are filled without rollback and i get syntax error for the exit hadler.
Query: DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING, NOT FOUND BEGIN ROLLBACK
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 'DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING, NOT FOUND
BEGIN
ROLLBACK' at line 1
can someone help me figure out what i'm doing wrong?
Thanks in advance
EDIT
UPDATE tbl_order SET TransactionID="xyz" WHERE OrderID=;
is intentional

I believe exit handlers can only be used in stored procedures. The documentation doesn't explicitly state this, but alludes to
Conditions may arise during stored program execution that require special handling
http://dev.mysql.com/doc/refman/5.1/en/condition-handling.html

I know this is quite outdated topic, but I encountered the same error when declaring my own procedure with transaction and error handling. The above procedure looks well, something OP didn't copy as it seems. The order is important and it should look like this:
Procedure declaration
Declaration of variables
ERROR HANDLING
Start transaction
Statements
Commit
End
In my case, it looks like this:
DELIMITER //
CREATE PROCEDURE pAddUser(IN _nick VARCHAR(30))
BEGIN
DECLARE doesUserExist BOOLEAN;
DECLARE accountCreationDate DATE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
START TRANSACTION;
SET doesUserExist = (SELECT EXISTS(SELECT * FROM tblUser WHERE nick=_nick));
IF NOT doesUserExist THEN
SET accountCreationDate = CURDATE();
INSERT INTO tblUser (nick, accountCreation) VALUES (_nick, CURDATE());
ELSE
SELECT CONCAT("Nick '", _nick, "' is in use!") AS 'Console output';
END IF;
COMMIT;
END;
//
DELIMITER ;

Related

MySQL said 1064 You have an error in your SQL syntax

I wrote a MYSQL Procedure for my user registration page, I have already written the PHP part to send data from there to MySQL, and it works fine (tried with a dummy data and retrieval). But there is some sort of problem with my handler or transaction I guess
This is my Procedure:
BEGIN
DECLARE unamec INT;
DECLARE emailc INT;
DECLARE m INT;
DECLARE msg VARCHAR(100);
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET m=1;
START TRANSACTION;
SET autocommit=0;
SELECT COUNT(*) INTO unamec FROM login WHERE uname=`#user`;
IF unamec=0 THEN
SELECT COUNT(*) INTO emailc FROM login WHERE email=#email;
IF emailc=0 THEN
INSERT INTO login (uname, hash, email, role) values(#user,MD5(#password),#email,'1');
SET msg='Successfully Registered';
ELSE
SET msg='Email Already Exists';
END IF;
ELSE
SET msg='Username Already Exists';
END IF;
COMMIT;
END;
END;
IF m=1 THEN
ROLLBACK;
SET msg='ERROR';
END IF;
SELECT msg as message;
END
I always get the error
MySQL said: #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
'END;
IF m=1 THEN
ROLLBACK;
SET msg='ERROR';
END IF;
SELECT msg as message;' at line 23
Is there something obvious I am missing? I have been trying for some time to solve this with almost all the results to show the same error.
Any help will be highly appreciated.
Ok, I edited my own code and got the required result.
The changes I made was:
BEGIN
DECLARE unamec INT;
DECLARE emailc INT;
DECLARE m INT;
DECLARE msg VARCHAR(100);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
START TRANSACTION;
SET autocommit=0;
SELECT COUNT(*) INTO unamec FROM login WHERE uname=`#user`;
IF unamec=0 THEN
SELECT COUNT(*) INTO emailc FROM login WHERE email=#email;
IF emailc=0 THEN
INSERT INTO login (uname, hash, email, role) values(#user,MD5(#password),#email,'1');
SET msg='Successfully Registered';
ELSE
SET msg='Email Already Exists';
END IF;
ELSE
SET msg='Username Already Exists';
END IF;
COMMIT;
BEGIN
ROLLBACK;
SET msg='ERROR';
END;
SELECT msg as message;
END
If anyone get any other answer, still appreciate it.
Anyways, the above code is now working fine.

MySQL - If an insertion fails during a transaction, will an exception or warning ALWAYS be raised?

I'm using MySQL Connector/Python to call the following stored procedure:
CREATE PROCEDURE AddDishReview
(
id INT,
OUT retVal TINYINT(1)
)
this_proc:BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING
BEGIN
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO Table1 VALUES (id);
IF ROW_COUNT() < 1 THEN
ROLLBACK;
LEAVE this_proc;
END IF;
SET #table1RefID = LAST_INSERT_ID();
INSERT INTO Table2 VALUES (#table1RefID);
IF ROW_COUNT() < 1 THEN
ROLLBACK;
LEAVE this_proc;
END IF;
SET retVal = 1;
COMMIT;
END //
DELIMITER ;
My question is, if an insertion fails, will there always be an exception or warning raised (which would in turn trigger the exit handler)? I just can't help but feel like all of the IF ROW_COUNT < 1 checks are unnecessary. But I put them in there because I wasn't sure if it was possible for an insertion to fail without raising an exception/warning (which would result in execution continuing and a ROW_COUNT of 0).
tl;dr: Is it safe to assume that if no exception or warning is raised, the insertion succeeded?
Yes, you will get either an error or a warning if an insert statement fails. Mysql documentation on sql mode setting has a nice summary table that describes when you get an error and when you get a warning for various sql mode / ignore scenarios. As you can see from the linked table, no warning is ever ignored, but errors may be reduced to warnings.

How can I use transactions in my MySQL stored procedure?

I'm trying to modify my MySQL stored procedure and make it transactional. The existing stored procedure works fine with no problem but as soon as I make it transactional it does not even allow me to save my changes. I checked MySQL documentation and searched online but I cannot find any problem with my code. It seems to be pretty straight forward but can't figure it out.
BEGIN
DECLARE poid INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION SQLWARNING
BEGIN
ROLLBACK;
END
START TRANSACTION;
-- ADD option 5
INSERT INTO product_option(product_id,option_id,required) VALUES(insertedProductID,5,0);
SET poid = (SELECT LAST_INSERT_ID());
INSERT INTO product_option_value(product_option_id,product_id,option_id,option_value_id,quantity,subtract,price,price_prefix,points,points_prefix,weight,weight_prefix) VALUES(poid,insertedProductID,5,50,0,0,4.99,'+',0,'+',0,'+');
-- ADD option 12
INSERT INTO product_option(product_id,option_id,required) VALUES(insertedProductID,12,1);
-- ADD option 13
INSERT INTO product_option(product_id,option_id,required) VALUES(insertedProductID,13,0);
COMMIT;
END
any idea ?
Two syntax errors:
You need commas in between the conditions for your exit handler. Notice the syntax documentation shows commas.
You need to terminate the END of the exit handler with a semicolon. The DECLARE statement itself (including its BEGIN...END block) is a statement like any other, and need to have a terminator.
So you need this:
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING
BEGIN
ROLLBACK;
END;
Try like this ie, include your Declare statement inside the START TRANSACTION;. Earlier your ROLLBACK was not a part of TRANSACTION as you wrote it above the START TRANSACTION:-
BEGIN
DECLARE poid INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING
START TRANSACTION;
BEGIN
ROLLBACK;
END
-- ADD option 5
INSERT INTO product_option(product_id,option_id,required) VALUES(insertedProductID,5,0);
SET poid = (SELECT LAST_INSERT_ID());
INSERT INTO product_option_value(product_option_id,product_id,option_id,option_value_id,quantity,subtract,price,price_prefix,points,points_prefix,weight,weight_prefix) VALUES(poid,insertedProductID,5,50,0,0,4.99,'+',0,'+',0,'+');
-- ADD option 12
INSERT INTO product_option(product_id,option_id,required) VALUES(insertedProductID,12,1);
-- ADD option 13
INSERT INTO product_option(product_id,option_id,required) VALUES(insertedProductID,13,0);
COMMIT;
END
Put your DECLAREs after the first BEGIN and it should work.
If you use BEGIN and END to group multiple statements, you normally also need to declare an alternate DELIMITER at the top and replace the ; after the last END with it.

Mysql error 1064 in Mysql 5.1- unable execute successfully

I am trying to write trigger in Mysql (5.1), but getting following error, please help.
The error is:
SQL Error (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 5.
Purpose for writing trigger:
I am writing application where I am assigning users, and I want to store unassigned usercount to field cluster_count in IX_branchdetails table.After updating the base table.
trigger:
DELIMITER $$
CREATE TRIGGER upd_trg AFTER
UPDATE ON DBNAME.BASETABLE
FOR EACH ROW
BEGIN
DECLARE m_branchcode INTEGER;
DECLARE cnt INTEGER;
DECLARE cursor_branch CURSOR FOR
SELECT DISTINCT branchcode
FROM ix_branchdetails;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
open cursor_branch;
my_loop: loop
set done = false;
fetch cursor_branch into m_branchcode;
if done then
leave my_loop;
end if;
select count(1) into cnt from (select count(1) from BASETABLE Where IX_BRANCHCODE = m_branchcode) as temp;
update DBANAME.ix_branchdetails set DBANAME.ix_branchdetails.cluster_count = cnt where DBANAME.ix_branchdetails.BRANCHCODE = m_branchcode;
end loop my_loop;
close cursor_branch;
END $$
DELIMITER ;
I don't see a declare for the done variable:
DECLARE done TINYINT DEFAULT FALSE;
The semicolon (;) is the default delimiter for MySQL statements. To get a procedure/function/trigger defined, we normally see the statement delimiter changed to a string that doesn't appear in the statement:
DELIMITER $$
CREATE PROCEDURE ...
END$$
DELIMITER ;
If the delimiter is not changed from the semicolon, then when MySQL encounters the first semicolon in your procedure/function/trigger, it sees that as the end of the statement, which is not what you want. You want MySQL to see the entire block of code as a single statement.

How can I see errors from a MySQL stored procedure?

With regards to using MySQL stored procedures with transactions, and I am having a problem getting error output.
The problem is that I need to set an exit_handler to roll back the transaction if anything fails. But when I do this, I don't get any error output if something goes wrong. For example if I accidentally pass a NULL value and try to insert it into a non-null field.
I am using a return value to programmatically indicate success or failure, however this does nothing to tell me what actually went wrong.
I am using Perl DBI to talk to MySQL. I am using MySQL 5.0.92 on the production server and MySQL 5.0.51a on the development server. Upgrading to a newer version of MySQL is politically untenable.
This is a simplified example:
DELIMITER //
CREATE PROCEDURE pmt_new(
app_id varchar(40),
out ret tinyint unsigned,
out pmt_req_id int(10) unsigned)
BEGIN
DECLARE v_pmt_req_type int(10) unsigned;
DECLARE exit handler for not found, sqlwarning, sqlexception rollback;
set ret=1;
START TRANSACTION;
SELECT pmt_type INTO v_pmt_req_type FROM pmt_req_types WHERE pmt_req_name = 'Name 1';
INSERT INTO pmt_reqs (pmt_req_id, pmt_req_type, app_id)
values (null, v_pmt_req_type, app_id);
set pmt_req_id = last_insert_id();
INSERT INTO other (pmt_req_id) values (pmt_req_id);
COMMIT;
set ret=0;
END//
DELIMITER ;
Instead of just doing a rollback in your exit handler you need to return something as well...
You currently have
DECLARE exit handler for not found, sqlwarning, sqlexception rollback;
Change it to something like...
DECLARE exit handler for not found, sqlwarning, sqlexception
begin
rollback;
select "We had to rollback, error!";
end;
In 5.5 they added the SIGNAL/RESIGNAL statements so you could 'return' an error but prior versions you have to kind of roll your own solution. If you need you can declare multiple exit handlers to tailor the output better, or setup your own error table you can pull from.
You can also do input testing inside your stored procedure. Want to know if app_id is null?
DELIMITER //
CREATE PROCEDURE pmt_new(
app_id varchar(40),
out result varchar(256),
out ret tinyint unsigned,
out pmt_req_id int(10) unsigned)
BEGIN
DECLARE v_pmt_req_type int(10) unsigned;
DECLARE exit handler for not found, sqlwarning, sqlexception rollback;
SET ret=1;
SET result = "";
IF app_id IS NULL THEN
set result = "Supplied ID is Null";
ELSE
START TRANSACTION;
SELECT pmt_type INTO v_pmt_req_type FROM pmt_req_types WHERE pmt_req_name = 'Name 1';
INSERT INTO pmt_reqs (pmt_req_id, pmt_req_type, app_id)
values (null, v_pmt_req_type, app_id);
set pmt_req_id = last_insert_id();
INSERT INTO other (pmt_req_id) values (pmt_req_id);
COMMIT;
set ret=0;
END IF;
END//
DELIMITER ;
Doing it this way adds another out parameter, but gives you much better information. You could do the same with multiple exit handlers.