How to force mysql to rollback transaction on error - mysql

We have a process where we need to test a SQL script before it runs against a production database. The approach is to execute the script within a transaction with a rollback statement at the end, capturing before/after logs illuminating the effects of the scripts.
select now();
start transaction;
select 'data before any changes', ...;
<insert / update / delete statements>;
select 'data after changes', ...;
rollback;
We've used this approach for many years with MSSQL, however we're having trouble implementing with mysql. If everything is working to-plan, things are great and it works exactly as expected. However, if we run into any errors (ex: typo in SQL or table constraint violation), the script aborts ... but COMMITS whatever ran before the error!!
How is this remotely considered an acceptable outcome? It drastically reduces the effectiveness of transactions if an error condition results in committing changes. I'm gobsmacked at this behavior--simply cannot understand the rationale.
I've been searching all over for a resolution to this and am coming up basically empty-handed. Options I've seen:
Catch the error in client code and rollback. This isn't really an option, as our workflow process is just running the SQL script--and the "commit" needs to happen within the script rather than caller code (after testing the script, we run it without the transaction or rollback).
Use DECLARE ... HANDLER FOR ...; to rollback on error. This looked promising, but appears to be only supported within a stored procedure (or similar) and therefore not suitable for one-off scripts.
What am I missing? Thanks

Related

Performing a transaction across multiple statements in phpMyAdmin

I'm not sure if this is an issue with phpMyAdmin, or that I'm not fully understanding how transactions work, but I want to be able to step through a series of queries within a transaction, and either ROLLBACK or COMMIT based on the returned results. I'm using the InnoDB storage engine.
Here's a basic example;
START TRANSACTION;
UPDATE students
SET lastname = "jones"
WHERE studentid = 1;
SELECT * FROM students;
ROLLBACK;
As a single query, this works entirely fine, and if I'm happy with the results, I could re-run the entire query with COMMIT.
However, if all these queries can be ran seperately, why does phpMyAdmin lose the transaction?
For example, if I do this;
START TRANSACTION;
UPDATE students
SET lastname = "jones"
WHERE studentid = 1;
SELECT * FROM students;
Then this;
COMMIT;
SELECT * FROM students;
The update I made in the transaction is lost, and lastname retains its original value, as if the update never took place. I was under the impression that transactions can span multiple queries, and I've seen a couple of examples of this;
1: Entirely possible in Navicat, a different IDE
2: Also possible in PHP via MySQLi
Why then am I losing the transaction in phpMyAdmin, if transactions are able to span multiple individual queries?
Edit 1: After doing a bit of digging, it appears that there are two other ways a transaction can be implicitly ended in MySQL;
Disconnecting a client session will implicitly end the current
transaction. Changes will be rolled back.
Killing a client session will implicitly end the current
transaction. Changes will be rolled back.
Is it possible that phpMyAdmin is ending the client session after Go is hit and a query is submitted?
Edit 2:
Just to confirm this is just a phpMyAdmin-specific issue, I ran the same query across multiple seperate queries in MySQL Workbench, and it worked exactly as intended, retaining the transaction, so it appears to be a failure on phpMyAdmin's part.
Is it possible that phpMyAdmin is ending the client session after Go is hit and a query is submitted?
That is pretty much how PHP works. You send the request, it get's processed, and once done, everything (including MySQL connections) gets thrown away. With next request, you start afresh.
There is a feature called persistent connections, but that is as well doing it's clean up. Otherwise the code would have to somehow handle giving the same user the same connection. Which could prove very difficult given the way PHP works.

SQL overwriting data

I'm trying to learn SQL and using MySQL and I'm just fooling around with it as a hobby. However, I found that in trying to manipulate the data I'm using, a lot of the time I end up overwriting some important information in my records, and I am unable to just ctrl-z this information back.
What are some safety tips when working with SQL that will help me from losing this information. Example, should I always keep a backup copy of all my tables?
Please look at the following link:
http://dev.mysql.com/doc/refman/5.0/en/commit.html
Transactions allow you to Rollback certain blocks of code when something goes wrong during the execution.
You can always prefer writing your SQL queries in START TRANSACTION, COMMIT, and ROLLBACK Syntax
BEGIN and BEGIN WORK are supported as aliases of START TRANSACTION for
initiating a transaction. START TRANSACTION is standard SQL syntax and
is the recommended way to start an ad-hoc transaction.
The advantage of writing your code in transaction is you can rollback your transaction when you want.
Each transaction is stored in the binary log in one chunk, upon
COMMIT. Transactions that are rolled back are not logged.

SQL Azure / SQL Server 2008 - Stored procedure - do not insert if error at any point

I have quite a simple question I think, but I need a definitive answer. I can't think of way to test out my ideas because it depends on a transient error (out of my control)
SQL Azure is prone to transient errors, timeouts, rejected connections etc. Sometimes on opening the connection, other times when executing a query.
I am inserting a row into SQL Azure via a stored procedure (it's basically a messaging system, so each message should be sent/inserted only once).
If a transient error occurs, my system waits a few seconds, then tries again ... repeating until the stored procedure executes without any errors.
I need the stored procedure to either insert the row and confirm to me that it has been inserted OR to fail completely and NOT insert it.
At the minute, I'm finding that when the database is going through a really bad patch, a message can end up being sent several times.
What would a system that deals with financial transactions do?
Since it's jut one insert statement, am I right in thinking wrapping it in a transaction would have no effect?
Can anyone clarify for me or even point me to some documentation I should read to figure it out myself?
Suppose there is more than just one insert in procedure (some selects after insert?)
You can start transaction in application. that way- if you do commit transaction in application and it succeeds, everything is done.
As per the suggestion by gbn, I added an extra key, not an identity, that I could use to identify duplicates and check on this before inserting

mysql: working around implicit transaction commits?

I wrote a tool for our project, for applying sql update files that were committed, to the DB. Whenever run (on deployment), it calculates the list of update files which need to be applied, and applies them iniside a transaction.
Recently I became aware of an issue: mysql would implicitly commit a transaction, whenever DDL statements (like create) are executed. http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
This is an issue for me, as sometimes an sql update file contains several statements, which as I understand will result in committing the transaction in the middle of executing the update file. This is a problem, because whenever a subsequent update will fail (which happens from time to time) I want to be able to rollback the transaction, or at least track which update files where applied (completely) and which were not.
Is there a way around the implicit transactions issue? I.e. is there a way to rollback a sequence of DDL statements whenever one of them fail?
Any other suggestions how I can handle the issue?
Thanks
Gidi
No. MySQL does not support transactional DDL. You either need to separate your DDL statements from DML statements, or perhaps try to use migration tool like RuckUsing

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();
?>