Automatic Rollback if COMMIT TRANSACTION is not reached - mysql

Consider the following:
START TRANSACTION;
BEGIN;
INSERT INTO prp_property1 (module_name,environment_name,NAME,VALUE) VALUES ('','production','','300000');
/** Assume there is syntax error SQL here...**/
Blah blah blah
DELETE FROM prp_property1 WHERE environment_name = 'production';
COMMIT TRANSACTION;
Question:
I noticed that the transaction automatically rolls back and the record insert attempt fails.
If I don't provide a error handler or error check along with ROLLBACK TRANSACTION as above, is it safe as it seems to be doing the job in an example like above because the COMMIT TRANSACTION never gets executed?
I assume the transaction is rolled back immediately and discarded as soon as a error occurs.

No, transactions are not rolled back as soon as an error occurs. But you may be using a client-application which applies this policy.
For example, if you are using the mysql command-line client, then it normally stops executing when an error occurs and will quit. Quitting while a transaction is in progress does cause it to be rolled back.
When you are writing your own application, you can control the policy on rollback, but there are some exceptions:
Quitting (i.e. disconnecting from the database) always rolls back a transaction in progress
A deadlock or lock-wait timeout implicitly causes a rollback
Other than these conditions, if you invoke a command which generates an error, the error is returned as normal, and you are free to do whatever you like, including committing the transaction anyway.

Use Mysql stored procedure
BEGIN
DECLARE exit handler for sqlexception
BEGIN
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO prp_property1 (module_name,environment_name,NAME,VALUE) VALUES ('','production','','300000');
[ERROR]
COMMIT;
END
You can set if warning or error rollback, then you don't need delete, with transaction all entry is deleted.

You may use procedure to do this more effectively.
Transaction with Stored Procedure in MySQL Server

I would like to add to what #MarkR already said. Error Handling, assuming InnoDB engine, happens as described in the Mysql Server Documentation
If you run out of file space in a tablespace, a MySQL Table is full error occurs and InnoDB rolls back the SQL statement.
A transaction deadlock causes InnoDB to roll back the entire transaction.
A duplicate-key error rolls back the SQL statement
A row too long error rolls back the SQL statement.
Other errors are mostly detected by the MySQL layer of code (above the InnoDB storage engine level), and they roll back the corresponding SQL statement
My understanding is also that when the Mysql session ends (when the php scripts ends), anything that is not committed is rolled back. I yet have to find a really reliable source to back this statement so do not take my word for it.

I've tested these three situations; mySQL does not roll back automatically.
A transaction deadlock causes InnoDB to roll back the entire transaction.
A duplicate-key error rolls back the SQL statement
A row too long error rolls back the SQL statement.
Only the affected records fail, the rest of the records succeed unless your application calls "rollback" explicitly.

Related

innodb rollback on timeout

I have a mysql 5.1 db running a stored proc with
START TRANSACTION
Insert some rows to table 1
Insert some rows to table 2
COMMIT
Calling this stored procedure often fails with
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction
According to this page here, if mysql server is not started with innodb_rollback_on_timeout then only the last statement is rolled back but START TRANSACTION itself will set autocommit = 0. Does that mean that our mysql server needs to be started with this parameter so that it doesn't leave the db in an inconsistent state where some rows are inserted into table 1 but not into table 2?
Yes, either that, or declare a handler "FOR '1205'" in your procedure by which you could (eg.) roll back the transaction and interrupt the process.
You can rollback yourself if the calling client checks for errors and rolls back the transaction if an error occurs. As stated in the bug log:
In the event of a row level lock timeout, it can be desirable to allow your application to decide what to do (such as ROLLBACK, retry the statement, etc...) so this behavior was added with an option for backwards compatibility if desired.
Otherwise yes - if you don't check for lock wait errors, and you always want the whole transaction to rollback, then you should set innodb_rollback_on_timeout in your my.cnf.

MySQL rollback on transaction with lost/disconnected connection

I need to make MySQL server to rollback transaction immediately after its client disconnected, because each client works concurrently. The problem can be reproduced like these (using an innodb table type)
On Client A:
START TRANSACTION;
SELECT MAX(ID) FROM tblone FOR UPDATE;
#... then disconnect your connection to the server
On Client B:
START TRANSACTION;
SELECT MAX(ID) FROM tblone FOR UPDATE;
#... lock wait time out will occur here
I had set MySQL's server option like innodb_rollback_on_timeout and using mysql's client mysql --skip-reconnect on both client. I tried this using one server and two client, on a network. I disconnected the network physically (unplug the cable) after SELECT ... FOR UPDATE; line. I need to make other clients to be able to use tblone on a transaction (lock it, update it) immediately, and for that to happen I think the server should rollback the transaction for Client A, after Client A disconnects.
When you are physically disconnecting a client you're not sending a normal disconnect (which would have caused a rollback) and the MySQL protocol isn't very chatty so the server never knows that the client isn't there. I think this is a flaw in the protocol when comparing to other database systems where the client and server talks internally much more.
Anyway. There are two variables that you could change. They basically do the same but for different clients.
The first is wait_timeout and it is used by application clients like java or php.
The other is interactive_timeout and it is used by the mysql client (as in your tests)
In both cases the server to kills the connection after a number of seconds and when doing so rollbacks all transactions and releases all locks.
This is to discuss some of the Comments. Please note that this disagrees with some of the Comments. I'll use an INSERT instead of SELECT..FOR UPDATE because the effect is more visible.
Let's look at some different cases:
(1) No SQL + timeout
START TRANSACTION;
do some SQL statement(s)
do no SQL for more than the timeout (before COMMITing)
Avoid this because of the cases detailed below. Solution: Don't depend on InnoDB to help you with long transactions.
(2) long-running query
START TRANSACTION;
do some SQL statement(s)
run an SQL query that takes more than the timeout
COMMIT;
All is well. The timeout does not apply as long as the server (mysqld) is continuing to perform queries. That is, the timeout 'clock' starts over at the end of each SQL statement
(3) (auto-reconnect)
START TRANSACTION;
INSERT ... VALUES (123);
time passes; no SQL performed for longer than the timeout
disconnect occurs
INSERT ... VALUES (456);
auto-reconnect (because you have it ENabled);
the INSERT proceeds
COMMIT;
123 will be rolled back; 456 will be inserted. (Similarly SELECT..FOR UPDATE would lose the locks.) Not good. The solution is to turn off "auto-reconnect". Instead, check for errors and treat the disconnect error as a fatal error for the Transaction. (Then start over the transaction.)
The INSERT 456 will be running in a new transaction, controlled by autocommit.
(4) (NO auto-reconnect)
START TRANSACTION;
INSERT ... VALUES (123);
time passes; no SQL for longer than the timeout
disconnect occurs
INSERT ... VALUES (456);
NO auto-reconnect (because you have it DISabled)
COMMIT;
123 will be rolled back. The INSERT of 456 will get an error something like "connection lost". Start the transaction over.

Is possible to do a ROLLBACK in a MySQL trigger?

Just that is the question: is possible to do a ROLLBACK in a MySQL trigger?
If answer is yes, then, please, explain how.
I've found out that this functionnality exists since MySQL 5.5 and does not work in earlier releases.
The trigger does no rollback or commit.
To initiate any rollback, you have to raise an exception. Thus your insert/update/delete command will abort.
The rollback or commit action has to be raised around your SQL command.
To raise your exception, in your XXX's trigger (eg.) :
create trigger Trigger_XXX_BeforeInsert before insert on XXX
for each row begin
if [Test]
then
SIGNAL sqlstate '45001' set message_text = "No way ! You cannot do this !";
end if ;
end ;
If the trigger raises an exception, that will abort the transaction, effectively rolling back. Will this work for you?
From: http://dev.mysql.com/doc/refman/5.1/en/trigger-syntax.html
The trigger cannot use statements that
explicitly or implicitly begin or end
a transaction such as START
TRANSACTION, COMMIT, or ROLLBACK.
and
For transactional tables, failure of a
statement should cause rollback of all
changes performed by the statement.
Failure of a trigger causes the
statement to fail, so trigger failure
also causes rollback. For
nontransactional tables, such rollback
cannot be done, so although the
statement fails, any changes performed
prior to the point of the error remain
in effect.

MySQL: automatic rollback on transaction failure

Is there any way to set MySQL to rollback any transaction on first error/warning automatically?
Now if everything goes well, it commits, but on failure it leaves transaction open and on another start of transaction it commits incomplete changes from failed transaction. So i need to rollback automatically those failed transactions..
(I'm executing queries from php, but i don't want to check in php for failure, as it would make more calls between mysql server and webserver.)
Thank you
I don't know of such feature, but I also don't see how checking for failure would mean more calls:
try:
<my code>
except:
transaction.rollback()
raise
else:
transaction.commit()
-- it's in Python/Django, but it should directly transpose to PHP - and it takes exactly the same amount of code to start new transaction, no matter if there is a problem (exception) or not.
Sorry, You are going to need to do this on your own.
I am not a PHP person, but in SQL: If you create a transaction and do several MySQL operations within the transaction, if you rollback the transaction everything will be rolled back. NOTE: You need to be using a transactional storage engine and autocommit must be set to off.
If all you are concerned about is the transaction coordination traffic then you could create a stored procedure and simply call it.
The reason the database does not automatically commit or rollback is because it does not know what you are trying to do, committing some data and rolling back other data might be acceptable in an app.
the key point to to set autocommit to false.
<?php
$database= new mysqli("sever", "user", "key", "database");
$database->autocommit(FALSE);
$error=0;
//asumming we want to delete a users infomation from two table
$database->query("delete from `pay` where `user`=1 ")?NULL:$error=1;
$database->query("delete from `users` where `id`=1 ")?NULL:$error=1;
if($error=0){
$database->commit();
} else {
$database->rollback();
}
$database->close();
?>

Transactional updates that are applied to DB regardless of no commit or rollback

I was under the impression that all updates to a SQL server database are first added the T-Log before being applied to the underlying database. In the event of the server crashing, the restore process would rollback any uncommitted transactions. This I also assumed works with transactions, if a commit or rollback is not called the changes will not be made.
So I wanted to see the reaction of SQL server to transactions being cut short. i.e. transactional updates without a commit or rollback. What I found I don’t quite understand. Especially, how SQL server can allow this to happen.
I used the script below to insert rows into a table with a delay to give me enough time to stop the transaction before it reaches the commit or rollback. This I guess would simulate the client application timing out before the transaction completed.
Create Table MyTest (Comment varchar(20))
Go
Create Procedure MyProc
as
Begin Try
Begin Transaction
Insert Into MyTest Select 'My First Entry'
WaitFor Delay '00:00:02'
Insert Into MyTest Select 'My Second Entry'
WaitFor Delay '00:00:02'
Insert Into MyTest Select 'My Third Entry'
Commit Transaction
Return 0 -- success
End Try
Begin Catch
If (##trancount<>0) Rollback Transaction
Declare #err int, #err_msg varchar(max)
Select #err = error_number(), #err_msg = error_message()
Raiserror(#err_msg, 16,1)
Return #err
End Catch
If you run the script, depending on how quickly you stop the procedure, you will see that the first one or two inserts will remain in the table. Could someone explain why this would happen?
Select * From MyTest
I tested this on SQL 2008.
Correct, TXNs are written using "Write Ahead Logging". There are MSDB articles about it and how this interacts with commit/rollback/checkpoints etc
However, a command timout (or what you are doing simply stops code executing) and the TXN is never rolled back and locks released until the connection is closed (or done later separately). This is what SET XACT_ABORT is for
If you begin a transaction and do not commit it or roll it back, you will simply get a hanging transaction that is likely to block other users until something is done with the current transaction. SQL Server will not automatically commit or rollback a transaction on its own, simply because your code didn't do so. The transaction will stay in place and block other users until it's committed or rolled back.
Now, I can quite easily begin a transaction in my T-SQL code, not commit or roll it back, and do a Select statement and see that data that I just inserted or updated as long as the Select statement is using the same connection as my transaction. If I attempt to do a Select using a different transaction, I won't see the inserted or updated data. In fact, the Select statement might not finish at all until the transaction on the other connection is completed.