Given 2 simple procedures
DELIMITER $$
CREATE PROCEDURE simpleproc()
BEGIN
UPDATE mytable SET name = 'bbb' WHERE id = 1;
DO SLEEP(15);
END
$$ DELIMITER ;
DELIMITER $$
CREATE PROCEDURE simpleproc2()
BEGIN
UPDATE mytable SET name = 'jjj' WHERE id = 1;
END
$$ DELIMITER ;
Then in one connection (in mysql workbench) I run first procedure:
call simpleproc();
and in second connection, run second procedure
call simpleproc2();
What surprised me, is that second transaction no waits for first and executes immediately.
What I missed? why first transaction no locks row id=1?
Table mytable have InnoDB engine
can we put a check (sort of) to-
Check if data is committed to database only when the operation is successfully completed.
Data should be rolled back in case of failed transactions.
like this one-
DELIMITER $$
CREATE PROCEDURE `sp_fail`()
BEGIN
DECLARE `_rollback` BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
START TRANSACTION;
UPDATE customer SET age= 20 WHERE name='stark';
UPDATE customer SET age= 20 WHERE name='brian'; -- fail as there is no name as brian in the table customer
IF `_rollback` THEN
SELECT 'The transaction has failed' AS 'Result';
ROLLBACK;
ELSE
SELECT 'The transaction was successful' AS 'Result';
COMMIT;
END IF;
END$$
DELIMITER ;
Its mine modified version of one of the answers in stackoverflow itself.I was thinking of using this to solve the above mentioned points but when i call the procedure the call runs successfully but no rows are affected.Why is that ?
Thanks in advance.
There is Autocommit properties in mysql. You will need set AUTOCOMMIT=0 (this set the autocommit false), after that you can issue COMMIT or ROLLBACK at the end of query according to your condition.
1. INSERT INTO table_name ...;
2. COMMIT; -- confirm your changes
OR
3. ROLLBACK; -- undo your changes
I am using Stored procedures to do CRUD operations on Mysql DB
DELIMITER $$
CREATE PROCEDURE `transaction_sp` ()
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO table_name (id, name, address) values ('1','Test','xpert.com');
SET #LID = LAST_INSERT_ID();
INSERT INTO table_name3 (id, job, responsibilities) values
(#LID,'Sr. Developer','Coding,mentoring etc');
COMMIT;
END
$$
Now, i want that if Second INSERT statement fails then second SQL statement shall not execute and first Insert shall be rolled back.
With my above approach 1st transaction is not rolled back. Do i need to set any flags?
How to handle it? Well explained answer will help here.
Please see my answer below. I believe if you check ##ROWCOUNT or ##Error after each insert you can rollback if there is no rows affected. Sorry if the method I used is wrong.
DELIMITER $$
CREATE PROCEDURE `transaction_sp` ()
BEGIN
DECLARE #nRowCount1 int,
#nRowCount2 int
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO table_name (id, name, address) values ('1','Test','xpert.com');
SET #LID = LAST_INSERT_ID();
SET #nRowCount1 = ##ROWCOUNT ;
INSERT INTO table_name3 (id, job, responsibilities) values
(#LID,'Sr. Developer','Coding,mentoring etc');
SET #nRowCount2 =##ROWCOUNT
if(#nRowCount1 <=0 and #nRowCount2<=0)
begin
ROLLBACK ;
end
COMMIT;
END
$$
Can someone tell me if it is possible to call another procedure from within a procedure and if any part of either procedure fails, roll everything back?
If this is possible, can someone please show me a tiny example of how this would be implemented?
EDIT: Procedure "b" fails but procedure "a" still inserts a row into table "a". It's my understanding that if any part of the insert fails that everything (both inserts) is rolled back which is not happening here. The questions is why not?
Procedure "a"
BEGIN
DECLARE b INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
INSERT INTO a(a)
VALUES(iA);
CALL b(iB,LAST_INSERT_ID(),#b);
SELECT #b INTO b;
IF b !=1 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END
Procedure "b"
BEGIN
DECLARE b INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
INSERT INTO b VALUES(iB,id);
SET b=1;
COMMIT;
END;
You will need to handle transactions in both procedures, but the proc that is calling the other, should check for the return value and rollback it's transactions based on that. Here is an example of the inner proc:
How to detect a rollback in MySQL stored procedure?
you would then check for p_return_code and do a rollback of the parent transaction.
EDIT:
What I think is happening is that inner SP COMMIT or ROLLBACK affect outer SP TRANSACTION. This code works for me, if inner SP fail it rolls back both insert statements. First call to ab() works, new user record gets inserted and new game record gets inserted, if we remove record from the games table and run ab() again, because user id already exists it rolls back games table insert:
create procedure ab()
BEGIN
START TRANSACTION;
INSERT INTO games (title) VALUES ('bad game');
CALL ba(#ret);
IF #ret!=0 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END;
create procedure ba(OUT return_value tinyint unsigned)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
set return_value = 1;
END;
INSERT INTO users (id) VALUES(1);
set return_value = 0;
END;
To test use call ab();
The basic structure of my stored procedure is,
BEGIN
.. Declare statements ..
START TRANSACTION;
.. Query 1 ..
.. Query 2 ..
.. Query 3 ..
COMMIT;
END
MySQL version: 5.1.61-0ubuntu0.11.10.1-log
Currently, if 'query 2' fails, result of 'query 1' is committed.
How can I rollback the transaction if any of the query fails?
Take a look at http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html
Basically you declare error handler which will call rollback
START TRANSACTION;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
EXIT PROCEDURE;
END;
COMMIT;
Just an alternative to the code by rkosegi,
BEGIN
.. Declare statements ..
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
.. set any flags etc eg. SET #flag = 0; ..
ROLLBACK;
END;
START TRANSACTION;
.. Query 1 ..
.. Query 2 ..
.. Query 3 ..
COMMIT;
.. eg. SET #flag = 1; ..
END
[This is just an explanation not addressed in other answers]
At least in recent versions of MySQL, your first query is not committed.
If you query it under the same session you will see the changes, but if you query it from a different session, the changes are not there, they are not committed.
What's going on?
When you open a transaction, and a query inside it fails, the transaction keeps open, it does not commit nor rollback the changes.
So BE CAREFUL, any table/row that was locked with a previous query like SELECT ... FOR SHARE/UPDATE, UPDATE, INSERT or any other locking-query, keeps locked until that session is killed (and executes a rollback), or until a following query commits it explicitly (COMMIT) or implicitly, thus making the partial changes permanent (which might happen hours later, while the transaction was in a waiting state).
That's why the solution involves declaring handlers to immediately ROLLBACK when an error happens.
Extra
Inside the handler you can also re-raise the error using RESIGNAL, otherwise the stored procedure executes "Successfully":
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
-- .. Query 1 ..
-- .. Query 2 ..
-- .. Query 3 ..
COMMIT;
END;
Here's an example of a transaction that will rollback on error and return the error code.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `SP_CREATE_SERVER_USER`(
IN P_server_id VARCHAR(100),
IN P_db_user_pw_creds VARCHAR(32),
IN p_premium_status_name VARCHAR(100),
IN P_premium_status_limit INT,
IN P_user_tag VARCHAR(255),
IN P_first_name VARCHAR(50),
IN P_last_name VARCHAR(50)
)
BEGIN
DECLARE errno INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET CURRENT DIAGNOSTICS CONDITION 1 errno = MYSQL_ERRNO;
SELECT errno AS MYSQL_ERROR;
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO server_users(server_id, db_user_pw_creds, premium_status_name, premium_status_limit)
VALUES(P_server_id, P_db_user_pw_creds, P_premium_status_name, P_premium_status_limit);
INSERT INTO client_users(user_id, server_id, user_tag, first_name, last_name, lat, lng)
VALUES(P_server_id, P_server_id, P_user_tag, P_first_name, P_last_name, 0, 0);
COMMIT WORK;
END$$
DELIMITER ;
This is assuming that autocommit is set to 0.
Hope this helps.