PDO Transactions. What is the right way to roll back [duplicate] - php

I'm pretty new to transactions.
Before, what I was doing was something like:
Code Block 1
$db = new PDO(...);
$stmt = $db->prepare(...);
if($stmt->execute()){
// success
return true;
}else{
// failed
return false;
}
But in an attempt to group multiple queries into a single transaction, I'm now using something like:
Code Block 2
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->beginTransaction();
try{
$stmt = $db->prepare(... 1 ...);
$stmt->execute();
$stmt = $db->prepare(... 2 ...);
$stmt->execute();
$stmt = $db->prepare(... 3 ...);
$stmt->execute();
$db->commit();
return true;
}catch(Exception $e){
// Failed, maybe write the error to a txt file or something
$db->rollBack();
return false;
}
My question is: If the transaction fails for whatever reason, does the code stop at $db->commit(); and jump to the catch block? Or would the return true; run first, and then it would try to go to the catch? 'Cause if that's the case, then I've already returned, and so it wouldn't go to the catch. AND it would have returned the wrong value.
Do I still need to include something like:
Code Block 3
if($stmt->commit()){
return true;
}
or is it sufficient the way I have it written in Code Block 2?

If the transaction fails for whatever reason, the code does stop at the very line where error occurred end then the execution jumps directly to the catch block. So it is sufficient the way you have it written in Code Block 2.
Note that you should always re-throw the Exception after rollback. Otherwise you will never have an idea what was a problem. So it should be
try{
$stmt = $db->prepare(... 1 ...);
$stmt->execute();
$stmt = $db->prepare(... 2 ...);
$stmt->execute();
$stmt = $db->prepare(... 3 ...);
$stmt->execute();
$db->commit();
return true;
}catch(Exception $e){
$db->rollBack();
throw $e;
}

The execution is stopped when an exception is thrown.
The first return will not be reached but the catch statement will be executed.
You can even return the commit directly:
$dbh->beginTransaction();
try {
// insert/update query
return $dbh->commit();
} catch (PDOException $e) {
$dbh->rollBack();
return false;
}

if you face any error you can do this to rollback all transactions like this
catch(Exception $e){
$db->rollBack();
// Failed, maybe write the error to a txt file or something
return false;
}

Related

Do I have to rollBack() before exit()?

I have code like this
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare($query1);
$stmt->execute($x);
} catch (Exception $e) {
$pdo->rollBack();
throw->$e;
}
if (condition) {
exit();
}
$x['column1'] = 'string1';
$x['column2'] = 'string2';
$x['column3'] = 'string3';
try {
$stmt = $pdo->prepare($query2);
$stmt->execute($x);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw->$e;
}
If the if condition succeeded and the code did exit()
Is everything related to the $pdo is safe too or do I add before exit() a $pdo->rollBack();?
Technically you don't.
PHP will close the database connection on exit.
Database will roll back all active transactions on close.
However, it is quite unlikely that such a case will ever happen in your code because right now it is wrong. You have to wrap the entire transaction in a try catch, not just database operations. Otherwise, if an exception will be thrown in your "condition" part, it will break a transaction but won't be caught.
Besides, using exit is a bad practice by itself and amidst a transaction a tenfold.
But if you really really need that (in reality you don't) then do something like
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare($query1);
$stmt->execute($x);
if (condition) {
throw new Exception("Stopped on condition");
}
$x['column1'] = 'string1';
$x['column2'] = 'string2';
$x['column3'] = 'string3';
$stmt = $pdo->prepare($query2);
$stmt->execute($x);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw->$e;
}
From the docs:
When the script ends or when a connection is about to be closed, if you have an outstanding transaction, PDO will automatically roll it back.
https://www.php.net/manual/en/pdo.transactions.php
In my opinion, it's better to run rollback to make sure to other devs that it's not code garbage, someon think about it.

Neo4j, graphaware: After catching an exception, another query will not execute.

I am connecting to neo4j the normal way and i can run queries no problem.
During testing, i wrote a query that should fail (due to uniqueness constraint), the query does fail as expected and i catch the exception.
The problem is when i try to execute the next query in the queue, it just hangs (longer than timeout).
I don't suppose that is normal behavior.
try{
$result = $neo->run ($query);
}
catch (Exception $e) {
// handle it
}
// all good so far
// now we attempt:
try{
$result = $neo->run ($next_query);
}
catch (Exception $e) {
// handle it
}
// hangs longer than timeout
if i remove the failed query from the queue, everything completes
So it seems that the exception thrown by the php-client breaks the connection to neo4j.
If i modify the code to the following, it all works fine.
try{
$result = $neo->run ($query);
}
catch (Exception $e) {
// handle it
connect_to_neo()
}
// all good so far
try{
$result = $neo->run ($next_query);
}
catch (Exception $e) {
// handle it
}
// all good, $next_query gets executed
I do not think that an exception that breaks the connection is desired behavior. Will raise the issue on github.

PHP / MySQL General Error

I got a trigger that executes when I update the table, everything works fine trigger executes great however I get a return in PHP in catch statement telling me General Error. I am not sure what is wrong wondering if anyone here can catch it.
Here is the code
$sql = "UPDATE pre_reg SET active =:val WHERE authentication =:auth";
try{
$query = $this->pdo->prepare($sql);
$query->bindParam(':val', $val, PDO::PARAM_INT);
$query->bindParam(':auth', $auth, PDO::PARAM_STR);
$query->execute();
$user = $query->fetch();
if($user){
return 'Congratulation you have activated your account!';
}else{return '';}
}catch (PDOException $e){
return 'This error:' .$e->getMessage(); // Store to file
}
ERROR
SQLSTATE[HY000]: General error
In the above code I always get catch return, even though in my database everything seems to happen according to the $sql / followed by a trigger.
I am open to suggestion thanks!
Comment out
$user = $query->fetch();
As you cant use fetch methods when inserting or udating the data.

MySQL query works but PHP returning Exception: errorinfo:null

The following code updates the table correctly, but it also returns an Exception. Any idea what might be happening here?
public function updateThis($aaa){
try
{
$success = false;
$query = "
UPDATE this_table
SET thing = '0'
WHERE aaa = :aaa";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':aaa', $aaa);
$stmt->execute();
if($this->conn->commit())
$success = true;
return $success;
}
catch(Exception $e)
{
return $e;
}
}
When you are using PDO, Auto-Commit is on by default, unless you specifically turn it off using Begin Transaction. I can't see it in your connection, so are you perhaps trying to commit a transaction that has already been auto-commited?

What's **if (no errors)**?

I am trying to implement transactions to Kohana but it seems to be not so easy as in Spring/Java.
So far I found this code to try but I don't know how to replace the part (no errors)
DB::query('START TRANSACTION');
// sql queries with query builder..
if (no errors)
DB::query('COMMIT');
else
DB::query('ROLLBACK');
How do I make the if clause?
Normally transactions are handled as such:
DB::query('START TRANSACTION');
try {
//do something
DB::query('COMMIT');
} catch (Exception $e) {
DB::query('ROLLBACK');
}
What this means is if everything succeeds within the try block, great. If any part of it fails then it won't reach the commit and will jump to the catch block, which contains the rollback. You can add more error handling within the catch if you wish, even throw a new exception of your own or throw the same exception you caught.
Just wrap everything in a try/catch block:
DB::query('START TRANSACTION');
try {
// sql queries with query builder..
DB::query('COMMIT');
} catch (Database_Exception $e) {
DB::query('ROLLBACK');
}
DB errors are converted to exceptions:
DB::query('START TRANSACTION');
try {
// sql queries with query builder..
DB::query('COMMIT');
}
catch($e)
{
$this->template->body = new View('db_error');
$this->template->body->error = 'An error occurred ...';
DB::query('ROLLBACK');
}
If you're using Kohana 3:
$db = Database::instance();
$db->begin();
try
{
// Do your queries here
$db->commit();
}
catch (Database_Exception $e)
{
$db->rollback();
}

Categories