MySQL ROLLBACK not actually rolling back - php

I have the following PHP code:
$dbh->beginTransaction();
$dbh->exec("LOCK TABLES
`reservations` WRITE, `settings` WRITE");
$dbh->exec("CREATE TEMPORARY TABLE
temp_reservations
SELECT * FROM reservations");
$dbh->exec("ALTER TABLE
`temp_reservations`
ADD INDEX ( conf_num ) ; ");
// [...Other stuff here with temp_reservations...]
$dbh->exec("DELETE QUICK FROM `reservations`");
$dbh->exec("OPTIMIZE TABLE `reservations`");
$dbh->exec("INSERT INTO `reservations` SELECT * FROM temp_reservations");
var_dump(GlobalContainer::$dbh->inTransaction()); // true
$dbh->exec("UNLOCK TABLES");
$dbh->rollBack();
Transactions are working fine for regular updates/inserts but the above code for some reason is not. When an error happens above, I'm left with a completely empty reservations table. I read on the PDO::beginTransaction page that "some databases, including MySQL, automatically issue an implicit COMMIT when a database definition language (DDL) statement such as DROP TABLE or CREATE TABLE is issued within a transaction". The MySQL manual has a list of "Data Definition Statements", which I would assume is the same as DDL mentioned above which lists CREATE TABLE but I am only creating a temporary table. Is there any way around this?
Also, does the fact that I'm left with an empty reservations table show that a commit occurred after the DELETE QUICK FROM reservations query?
Edit: On an additional note, the INSERT INTO reservations line also produces the following error:
Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
I tried doing $dbh->setAttribute( PDO::MYSQL_ATTR_USE_BUFFERED_QUERY , true); but this doesn't seem to affect it. I'm assuming it would have something to do with the transaction, but I'm not sure. Can anyone pinpoint what exactly is causing this error as well?

Your OPTIMIZE TABLE statement is causing an implicit commit.
I'm not sure exactly what you're trying to do, but it looks you can shorten your code to:
$dbh->exec("OPTIMIZE TABLE `reservations`");
All the other code is just making the job more complex, for no gain.
I'm also assuming you're using InnoDB tables, because MyISAM tables wouldn't support transactions anyway. Every DDL or DML operation on a MyISAM table implicitly commits immediately.
By the way, buffered queries have nothing to do with transactions. They have to do with fetching SELECT result sets one row at a time, versus fetching the whole result set into memory in PHP, then iterating through it. See explanation at: http://php.net/manual/en/mysqlinfo.concepts.buffering.php

Related

mysql transaction not working with innodb tables

i write this but transaction not working and i also convert both tables in innodb type can any one guide me whats wrong in my coding or another alternative of transaction.
mysql_query("begin;");
$query1 = mysql_query("ALTER TABLE products ADD COLUMN {$_POST[fields]} VARCHAR(60)");
$query2 = mysql_query("INSERT INTO fields (cid5,fields,field_title,field_type)
VALUE ('$_POST[cid]','$_POST[fields]','$_POST[field_title]','$_POST[field_type]')");
if (($query1)&&($query2)) {mysql_query("commit;");}
else {mysql_query("rollback;");}
}
i am using mysql 5.1.69-cll
ALTER TABLE is a DDL (Data Definition Language) statement; which is not transactional in MySQL innodb engine. INSERT is a DML statement (Data Manipulation Language), which is transactional. Because one statement isn't transactional and one is, the two shouldn't be combined in a transaction.
Quoting from the MySQL manual:
Some statements cannot be rolled back. In general, these include data
definition language (DDL) statements, such as those that create or
drop databases, those that create, drop, or alter tables or stored
routines.
You should design your transactions not to include such statements. If
you issue a statement early in a transaction that cannot be rolled
back, and then another statement later fails, the full effect of the
transaction cannot be rolled back in such cases by issuing a ROLLBACK
statement.
http://dev.mysql.com/doc/refman/5.6/en/cannot-roll-back.html

MYSQL PHP PDO order of statements in a transaction

I am using InnoDB in MySQL and accessing the table from PHP with PDO.
I need to lock the table, do a select and then, depending on the result of that either insert a row or not. Since I want to have the table locked for as short a time as possible, can I do it like this?
prepare select
prepare insert
begin transaction
lock table
execute select
if reservation time is available then execute insert
unlock table
commit
Or do the prepares have to be inside the transaction? Or do they have to be after the lock?
Should the transaction only include the insert, or does that make any difference?
beginTransaction turns off autocommit mode, so it only affects queries that actually commit changes. This means that prepared statements, SELECT, and even LOCK TABLES are not affected by transactions at all. In fact, if you're only doing a single INSERT there's no need to even use a transaction; you would only need to use them if you wanted to do multiple write queries atomically.

MySQL commit and transaction

I have a question regarding MySQL commits and transactions. I have a couple of PHP statements that execute MySQL queries. Do I just say the following?
mysql_query("START TRANSACTION");
//more queries here
mysql_query("COMMIT");
What exactly would this do? How does it help? For updates, deletes and insertions I also found this to block other queries from reading:
mysql_query("LOCK TABLES t1 WRITE, t2 WRITE");
//more queries here
mysql_query("UNLOCK TABLES t1, t2");
Would this block other queries whatever nature or only writes/selects?
Another question: Say one query is running and blocks other queries. Another query tries to access blocked data - and it sees that it is blocked. How does it proceed? Does it wait until the data is unblocked again and re-execute the query? Does it just fail and needs to be repeated? If so, how can I check?
Thanks a lot!
Dennis
In InnoDB, you do not need to explicitly start or end transactions for single queries if you have not changed the default setting of autocommit, which is "on". If autocommit is on, InnoDB automatically encloses every single SQL query in a transaction, which is the equivalent of START TRANSACTION; query; COMMIT;.
If you explicitly use START TRANSACTION in InnoDB with autocommit on, then any queries executed after a START TRANSACTION statement will either all be executed, or all of them will fail. This is useful in banking environments, for example: if I am transferring $500 to your bank account, that operation should only succeed if the sum has been subtracted from my bank balance and added to yours. So in this case, you'd run something like
START TRANSACTION;
UPDATE customers SET balance = balance - 500 WHERE customer = 'Daan';
UPDATE customers SET balance = balance + 500 WHERE customer = 'Dennis';
COMMIT;
This ensures that either both queries will run successfully, or none, but not just one.
This post has some more on when you should use transactions.
In InnoDB, you will very rarely have to lock entire tables; InnoDB, unlike MyISAM, supports row-level locking. This means clients do not have to lock the entire table, forcing other clients to wait. Clients should only lock the rows they actually need, allowing other clients to continue accessing the rows they need.
You can read more about InnoDB transactions here. Your questions about deadlocking are answered in sections 14.2.8.8 and 14.2.8.9 of the docs. If a query fails, your MySQL driver will return an error message indicating the reason; your app should then reissue the queries if required.
Finally, in your example code, you used mysql_query. If you are writing new code, please stop using the old, slow, and deprecated mysql_ library for PHP and use mysqli_ or PDO instead :)

Is it possible to have in a mysql transaction both select and update?

I'm curios if this can be achieved as I'm currently facing a bug and would like to see if putting a SELECT and an UPDATE in a transaction would fix it (if you're wondering why I'm not posting the code that causes the bug it's because it's a complex environment and I can't post all the influencing factors).
Something that I'm also interested in, related to this, is if you have ever experienced code that had and UPDATE query written after a SELECT query, yet the UPDATE gets executed before the SELECT (with the possibility that the script might run twice ruled out).
It depends on what do you mean by a transaction.
There are two types of transactions:
Implicit transactons: as in INSERT, UPDATE, SELECT, DELETE statements, and in such statements there is no explicit transaction commands, and the database engine will rollback the whole statement if an error happens.
Explicit Transactions: in such the enclosed statements inside the transaction are executed as a unit and either COMMIT the whole transaction or ROLLBACK .
So you can't have both SELECT and UPDATE inside one query, but you can but them inside a transaction like:
START TRANSACTION;
SELECT * FROM tableName;
UPDATE table SET something = 'other something' WHERE thirdsomething = #s;
COMMIT;
Then Put them in a stored procedure or a UDF.
Note that: SELECT statements do not modify data, so you might not need to enclose it in a transaction, so in your case you will have only UPDATE statement you can just use a stored procedure without a transaction.

PDO::ATTR_AUTOCOMMIT ignores non-transactional INSERT/UPDATE

Been scratching my head on this for a while....
I have a PDO object with pdo->setAttribute(PDO::ATTR_AUTOCOMMIT,0); as I want to use FOR UPDATE with some InnoDB tables. Reading the MySQL documentation, FOR UPDATE will only lock read rows if:
You are in a transaction
You are not in a transaction and set autocommit=0 has been issued
So, I am using ATTR_AUTOCOMMIT to allow the PDO object to lock rows. In either case, this is causing INSERT and UPDATE statements to not apply. These statements are nothing to do with FOR UPDATE they are just running through the same PDO object with prepared statements.
My MySQL query log looks like:
xxx Connect user#host
xxx Query set autocommit=0
xxx Query INSERT INTO foo_tbl (bar, baz) VALUES ('hello','world')
xxx Quit
PHP/PDO doesn't complain, but selecting from the table shows that the data hasn't been written.
The queries that I am running have been run thousands of time prior; only the ATTR_AUTOCOMMIT change has been made. Removing that option makes everything work again. Transactions are working fine with the option autocommit=0 too.
Are there additional calls that need making on the PDO object (commit() complains rightly that it isn't in a transaction) to make the changes stick? Basically, I want a plain PDO object but with the option to lock rows outside of transactions for InnoDB tables (the background to why is too long and boring for here).
I'm sure this is something stupid I am missing scratches head
$db = new PDO('mysql:dbname=test');
$db->setAttribute(PDO::ATTR_AUTOCOMMIT,0);
var_dump($db->query('SELECT ##autocommit')->fetchAll()); //OK
$db->query("INSERT INTO foo (bar) VALUES ('a');");
$db->query("COMMIT;");//do by SQL rather then by interface / PDO-method
But essentially, you're in a transaction (you just haven't started it with PDO), a rollback etc. is also still available. It's quite debatable whether this is a bug (not being able to call commit() directly).

Categories