MySQL Script with rollback on error - mysql

I am trying to create a transaction in MySql which will roll back when an exception occurs during the transaction. Similar using the following in a stored procedure.
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
Can this be done without writing a stored procedure? For example the following snippet should roll back the first insert because the second insert would fail.
START TRANSACTION;
INSERT INTO mytable VALUE 'foo1',2,'foo3','foo4'
INSERT INTO mytable VALUE 'foo1','foo2','foo3','foo4'
COMMIT

If I understand correctly you want to run a SQL script containing, for example inserts, in a single transaction that will rollback if any of the inserts fail. Is right?
I'm not 100% on this, but I'm pretty sure you'd need to do this as a stored procedure or programatically in, say, Java.

Related

Handling transaction errors without using a stored procedure

I'm trying to write a template for building my database. I want the template to wrap all commands in a transaction so it can be automatically rolled back if anything fails.
Currently, I'm using a stored procedure containing this general structure:
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
START TRANSACTION;
-- Commands here
COMMIT;
However there are some issues with this, for example it's impossible to add other stored procedures from a stored procedure. So I would like to have a similar functionality without being wrapped in a stored procedure. Is this possible? I haven't been able to find any way to catch SQL exceptions outside a stored procedure and without that I can't automatically rollback on error.
I've also tried something like this with the hopes that on an error execution should crash out of the first section without committing and then continuing on the next section with the rollback. But that doesn't seem to work:
DELIMITER //
START TRANSACTION;
-- Commands here
COMMIT//
ROLLBACK//
DELIMITER ;

Execute Stored Procedure with Transaction inside MySQLWorkbench alway commit automatically?

I have this STP:
CREATE DEFINER=`user1`#`%` PROCEDURE `test`(
OUT result TINYINT
)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET result = -1;
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO testtable (field1, field2) VALUES (11, 22);
SET result = 1;
END
After I execute it inside MySQLWorkbench (6.3 64b), testtable have new record. I though data must be not commited because there is no commit statement.
Then I try to call that STP again by C# client, and this time, new data is not commited.
Please help me to explain this problem, I dont understand what is the different between calling STP inside Workbench and calling STP by another client.
Thanks.
The difference is that when you query the table again inside mysql workbench you are inside the same session. Even though your changes have not been committed, even though your changes are not committed you can still see them because the same client session is allowed to see uncommited changes.
If however you start up a new session of mysql workbench or the mysql shell you will not see the changes that you have made through your existing workbench session.

stored procedure error's but then triggers last section

I have a stored procedure which updates a table, pretty standard insert query and the table in question has a constraint to ensure no duplicate data can go in, it error's just fine when the procedure is executed with duplicate data, however the last step of the proc executes an email stored proc. even when the insert fails the email gets sent? I thought the procedure would stop with the insert error?
Nope - it depends on the severity of the error. If the severity is less than 11 then by default SQL server will carry on.
I found this article which seems to do a decent job of explaining this: http://blog.sqlauthority.com/2012/04/26/sql-server-introduction-to-sql-error-actions-a-primer/
If I were you I'd make sure that the stored procedure is doing everything inside a transaction with a try/catch block.
EG
BEGIN TRAN
BEGIN TRY
-- Do insert
-- send email
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK TRAN
END CATCH
IF ##TRANCOUNT > 0 COMMIT TRAN

Do DMLs in a stored procedure run in a single transaction?

My MySQL 5.5 server has set autocommit=1.
My stored procedure has several DMLs but without explicit transaction management.
When I issue call the_procedure() from MySQL CLI (autocommit is still 1), do all the procedure's DMLs run in one transaction?
Or do they run in separate transactions, and causing implicit transaction commit after every DML (due to autocommit)?
This is surprising to me but:
Although MySQL will automatically initiate a transaction on your
behalf when you issue DML statements, you should issue an explicit
START TRANSACTION statement in your program to mark the beginning of
your transaction.
It's possible that your stored program might be run within a server in
which autocommit is set to TRUE, and by issuing an explicit START
TRANSACTION statement you ensure that autocommit does not remain
enabled during your transaction. START TRANSACTION also aids
readability by clearly delineating the scope of your transactional
code.
They run in separate transactions if autocommit=1. Suppose you define
CREATE TABLE test ( id int PRIMARY KEY )//
CREATE PROCEDURE sp_test_trans()
BEGIN
INSERT INTO test (id) VALUES (1);
INSERT INTO test (id) VALUES (2);
ROLLBACK;
END//
If you run this procedure with autocommit=0, the ROLLBACK will undo the insertions. If you run it with autocommit=1, the ROLLBACK will do nothing. Fiddle here.
Another example:
CREATE PROCEDURE sp_test_trans_2()
BEGIN
INSERT INTO test (id) VALUES (1);
INSERT INTO test (id) VALUES (1);
END//
If you run this procedure with autocommit=0, failure of the second insert will cause a ROLLBACK undoing the first insertion. If you run it with autocommit=1, the second insert will fail but the effects of the first insert will not be undone.
Tests done in the following SQL Fiddle, shows that by not explicitly handle transactions are handled separately when the variable autocommit is 1 (TRUE).

Is there an equivalent for SQL Server's ##error in MySQL

I want to run an update query against a production database and as good little developer I am trying to make it as safe as possible. I am looking to do the following
BEGIN TRANSACTION
UPDATE table_x SET col_y = 'some_value'
.
.
.
IF (##error <> 0)
BEGIN
ROLLBACK
END
ELSE
BEGIN
COMMIT
END
The above should work in SQL Server but I need this to work against a MySQL database.
EDIT:
Sorry, there is more than 1 statement to execute. Yes I am aware of not needing to wrap a single query in a transaction.
BEGIN;
UPDATE foo SET bar = 3;
UPDATE bar SET thing = 5;
COMMIT;
If an error occurs, the entire transaction will be rolled back automatically. You really only need to execute ROLLBACK if something in your application indicates the need to rollback.
It is possible to handle errors explicitly within procedures or compound statements in MySQL, but I wouldn't recommend going down this route. See this how-to article and the docs for DECLARE HANDLER. You'll also have to find the specific error code you want to handle, or you can use the general SQLEXCEPTION condition. You'll also want to review compound statements and defining stored programs.
Anyway, based on the docs, you could do something like the query below, but it honestly wouldn't do anything differently than my previous answer above. It would also get you very strange looks from anyone who uses MySQL (including myself).
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
UPDATE foo SET bar = 3;
UPDATE bar SET thing = 5;
COMMIT;
END;
Old answer:
If you're executing a single query, this doesn't make sense. Simply execute the query; if an error occurs, nothing will happen - your transaction will automatically be rolled back.
The reason is that, by default, all single queries are wrapped in a "hidden" transaction, called "autocommit" mode in MySQL. The typical alternative is to explicitly use transactions - once you execute "BEGIN" you have started a transaction. Once you either COMMIT or ROLLBACK you'll be back in autocommit mode.
So, the only reason to use a transaction in MySQL is if you want to rollback to a particular state when an error (or some other external event) occurs. In MySQL, a transaction is always aborted if an error occurs.
Lastly, it is possible to turn this behavior off entirely, and then you must use transactions explicitly at all times. I believe "BEGIN" is implied from when you last committed or rolled back, but you must either COMMIT or ROLLBACK any queries you run.
See The InnoDB Transaction Model in the MySQL manual for more info.
CREATE PROCEDURE prc_test()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
START TRANSACTION;
INSERT
INTO t_test VALUES ('test', 'test');
INSERT
INTO no_such_table
VALUES ('no');
COMMIT;
END;
CALL prc_test();
SELECT *
FROM t_test;
0 rows fetched.
I don't think this is necessary as there is the concept of implicit commit/rollback.
From MySQL docs:
By default, MySQL starts the session
for each new connection with
autocommit mode 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. See Section
13.6.13, “InnoDB Error Handling”.