So here's the stored procedure I've written. When I ran the DELETE and UPDATE in a single sql tab
as:
DELETE FROM curriculumsubjects WHERE curriculumId = 27;
INSERT INTO curriculumsubjects(curriculumId,subjectCode)
VALUES(27,'MATH101');
it works. It executes delete and insert without any problem
But if I call the stored procedure as:
CALL `enrollmentdb`.`updateCurriculumSubjects`(27, 'MATH101');
it returns the 'error' string i put during ROLLBACK
What could be causing the failure of transaction within the stored procedure body when it runs successfully if ran without stored procedure CALL?
Here's the stored procedure.
CREATE DEFINER=`root`#`localhost` PROCEDURE `updateCurriculumSubjects`(IN p_curriculumId int, IN p_subjectCode varchar(100))
BEGIN
DECLARE hasError BOOLEAN DEFAULT 0;
DECLARE CONTINUE HANDLER FOR sqlexception SET hasError = 1;
START TRANSACTION;
DELETE FROM curriculumsubjects WHERE curriculumId = p_curriculumId;
INSERT INTO curriculumsubjects(curriculumId,subjectCode)
VALUES(p_curriculumId,p_subjCode);
IF hasError THEN
ROLLBACK;
SELECT 'error';
ELSE
COMMIT;
END IF;
END
By the way I'm using Mysql Workbench 6.3 and what I'm trying to do is to delete all the columns matching the curriculumId before I insert again.
On Java, I'll be iterating the call to the stored procedure for multiple inserts.
I hope you can help. I just can't find a reason why delete and insert won't work if put within a transaction.
Thanks.
You have just a mistake in your insert syntax which you have written p_subjCode but your input variable is p_subjectCode and I change Boolean type variable to tinyint(1) for more version support.
CREATE DEFINER=`root`#`localhost` PROCEDURE `updateCurriculumSubjects`(IN p_curriculumId int, IN p_subjectCode varchar(100))
BEGIN
DECLARE hasError TINYINT(1) DEFAULT 0;
DECLARE CONTINUE HANDLER FOR sqlexception SET hasError = 1;
START TRANSACTION;
DELETE FROM curriculumsubjects WHERE curriculumId = p_curriculumId;
INSERT INTO curriculumsubjects(curriculumId,subjectCode)
VALUES(p_curriculumId,p_subjectCode);
IF hasError=1 THEN
ROLLBACK;
SELECT 'error';
ELSE
COMMIT;
END IF;
END
Related
I have this small script in my populate.sql which is supposed to roll back all inserts if any fails. However im not being able to execute populate.sql through "source populate.sql" cause it says i have a syntax error on my handler, but i've already check mysql 5.7 docs and everything seems all right. What am i doing wrong?
DELIMITER $$
CREATE PROCEDURE foo()
BEGIN
DECLARE rollbacka BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET rollbacka = 1;
START TRANSACTION;
insert into obra values('101-903aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','101-903.jpeg');
insert into obra values('101-904','101-904.jpeg');
insert into obra values('101-905','101-905.jpeg');
insert into obra values('101-906','101-906.jpeg');
insert into obra values('101-907','101-907.jpeg');
IF rollbacka THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END $$
DELIMITER ;
select * from museu.obra;
[update]Here's what im testing. It Shouldn't update table obras cause first attribute of the first insert is too big, so it will throw an exception, however when i comment it, the table remains empty, in that situation the other 4 inserts should work.
I am very new in Mysql, probably don't know or don't understand something essential.
Could you please advise me why 'begin !!!' message is not inserted in this
case?
DELIMITER $$
CREATE TABLE `_debugLogTable` (
`Message` varchar(255) DEFAULT NULL
) ENGINE=InnoDB $$
CREATE PROCEDURE `debug_msg`(msg VARCHAR(255))
BEGIN
insert into _debugLogTable select msg;
END$$
CREATE FUNCTION `ValueMeetsCondition`(value varchar(20)) RETURNS tinyint(1)
BEGIN
DECLARE ConditionValue INTEGER;
call debug_msg('begin !!!');
SET ConditionValue = CAST(`value` AS UNSIGNED);
call debug_msg('end !!!');
RETURN TRUE;
END$$
DELIMITER ;
I am aware that CAST function fails, but why call debug_msg('begin !!!'); does not insert new record into table?! There are not any transactions there!
Just want post an answer, maybe it will help somebody in the future.
From this we have -
If autocommit mode is enabled, each SQL statement forms a single transaction on its own. By default, MySQL starts the session for each new connection with autocommit enabled, so MySQL does a commit after each SQL statement if that statement did not return an error. If a statement returns an error, the commit or rollback behavior depends on the error
I call function in this way - select ValueMeetsCondition('>10').
So actually it is wrapped into transaction by MySQL, that's why if something inside my procedure fails - the whole changes are roll backed.
If i remake my query in this way the message begin !! will be inserted, while end !! does not
call debug_msg('begin !!!');
SET ConditionValue = CAST(`>10` AS UNSIGNED);
select ConditionValue;
call debug_msg('end !!!');
I have a stored procedure which insert data from one database to another. Stored procedure is working fine in mysql but when i call through hibernate, its not working.
DELIMITER $$
CREATE PROCEDURE `INSERTDATA`(stdID BIGINT)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO `db2`.`std`
SELECT * FROM stdDetails WHERE studID=stdID;
COMMIT;
END
// working fine if i do this
call INSERTDATA(1);
But
Query procQry = session.createSQLQuery( "CALL ARCHIVEEVENTDATA(?)");
procQry.setParameter(0,examEventID);
procQry.executeUpdate();
transaction.commit();
Not working for me :(
Note : Both databases are on same server
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();
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.