Rolling back MySQL queries - php

If I have multiple queries on chain, on a structure based on IFs, like this:
$query1 = mysqli_query("query here");
if(!query1){
//display error
} else {
$query2 = mysqli_query("another query here");
if(!query2){
//display error
//rollback the query1
} else {
query3 = mysqli_query("yet again another query");
if(!query3) {
//display error
//rollback the query2
//rollback the query1
} else {
query4 = mysqli_query("eh.. another one");
if(!query4){
//display error
//rollback the query3
//rollback the query2
//rollback the query1
} else {
return success;
}
}
}
}
Is there a best way to rollback the previous query, if the next one fails?
Otherwise I'm gonna have the first 2 query successfull, which edited the database, but the 3° failed, so 3° and 4° didn't edit the dabatase, with the result of having it corrupted.
I thought about something like:
...
$query2 = mysqli_query("another query here");
if(!query2){
//display error
$rollback = mysqli_query("query to rollback query1");
} else {
query3 = mysqli_query("yet again another query");
if(!query3) {
//display error
$rollback = mysqli_query("query to rollback query2");
$rollback = mysqli_query("query to rollback query1");
} else {
...
But the above method grants even more chances to fail more queries.
Is there any other more effective methods?

This is how i would do it with mysqli:
Configure mysqli (somewehere at the begining of your application) to throw exceptions when a query fails.
mysqli_report(MYSQLI_REPORT_STRICT);
This way you will not need all the if .. elseif .. else.
$connection->begin_transaction();
try {
$result1 = $connection->query("query 1");
// do something with $result1
$result2 = $connection->query("query 2");
// do something with $result2
$result3 = $connection->query("query 3");
// do something with $result3
// you will not get here if any of the queries fails
$connection->commit();
} catch (Exception $e) {
// if any of the queries fails, the following code will be executed
$connection->rollback(); // roll back everything to the point of begin_transaction()
// do other stuff to handle the error
}
Update
Usually the user don't care about, why his action failed. If a query fails, it's never the users fault. It's either the fault of the developer or of the environment. So there shouldn't be a reason to render an error message depending on which query failed.
Note that if the users intput is the source of the failed query, then
you didn't validate the input properly
your queries are not injection safe (If the input can cause an SQL error it can also be used to compromise your DB.)
However - I don't say there can't be reasons - I just don't know any. So if you want your error message depend on which query failed, you can do the following:
$error = null;
$connection->begin_transaction();
try {
try {
$result1 = $connection->query("query 1");
} catch (Exception $e) {
$error = 'query 1 failed';
throw $e;
}
// do something with $result1
try {
$result2 = $connection->query("query 2");
} catch (Exception $e) {
$error = 'query 2 failed';
throw $e;
}
// do something with $result2
// execute more queries the same way
$connection->commit();
} catch (Exception $e) {
$connection->rollback();
// use $error to find out which query failed
// do other stuff to handle the error
}

Related

Why doesn't the Insert work for me in SQLITE3?

when executing the form, the data is not inserting. I have activated SQLITE3 and I am not skipping any type of error.
The echo of the try is to see what was wrong but nothing. I see everything right.
Does anyone help me?
$username = $_POST['nombre'];
$clave = $_POST['clave'];
$apenom = $_POST['apenom'];
try {
$bd = new SQLite3("test");
//preparamos la sentencia
echo "INSERT INTO usuarios (username,clave,apenom) VALUES ('$username','$clave','$apenom')";
$bd->exec("INSERT INTO usuarios (username,clave,apenom) VALUES ('$username','$clave','$apenom')");
/* while ($row = $resultado->fetchArray()) {
echo "{$row['username']} {$row['clave']} {$row['apenom']} \n";
} */
} catch (\Throwable $th) {
echo $th;
}
Do take the advice from the comments into account, database security is not something you should 'wing'...
As for a little help on setting up a connection and importantly debugging if anything goes wrong so you know what to fix, the following 'skeleton' might help:
<?php
try {
// connect to your database
$sqlite = new SQLite3('test.db');
}
catch (Exception $e) {
// if no connection could be established a exception is thrown
echo $e->getMessage();
}
// your query
$query = '...';
$result = $sqlite->query($query); // result object (FALSE on error)
if (!$result) {
// query failed for some reason...
echo $sqlite->lastErrorMsg();
} else {
// do something with result
}

Get the mysql data

I am trying to get a mysql data from the table, here -
try
{
$stmt = $user->prepare("SELECT status FROM users");
$result=$stmt->fetch(PDO::FETCH_ASSOC);
if($result['status'] != "Y")
{
$error[] = "Some error warning!";
}
else
{
// Some php codes
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
Here user is a class where prepare is db connection mysql prepare function. The error always prints - "Array!". I am new to php. Any help will be appreciated.
EDIT: I have managed to solve the problem.
You forgot the call of PDOStatement::execute(). See php.net for some examples.
Have you already tried this?
try
{
$stmt = $user->prepare("SELECT status FROM users");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if($result['status'] != "Y")
{
$error[] = "Some error warning!";
}
else
{
// Some php codes
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
Regarding the Array! output: Did you post the whole code of your script? Were do you try to print the array $error?

mysqli transaction committing only some of queries

below is my code. when I use transaction, the transaction is committed but the first query ($receipt_query) is not entered into the DB, the other two are. When running the queries without transaction, all queries are run successfully. So can anyone spot the problem here?!
$mysqli->autocommit(false);
if(!empty($_POST['receipt'])) {
$result = $mysqli->query("insert query 1");
if (!$result) {
$error = 'Some error message';
}
}
if (!empty($_POST['payment'])) {
$result = $mysqli->query("insert query 2");
if (!$result) {
$error = 'Some error message';
}
}
if(empty($error)) {
if($mysqli->query("query 3")) {
$mysqli->commit();
} else {
$mysqli->rollback();
}
} else {
$mysqli->rollback();
}
Doesn't transaction mean "All or None"? so how come the first one doesn't commit even though the whole transaction is committed?
You need to start transaction after autommit.
$mysqli->autocommit(false);
$mysqli->begin_transaction();
An surround the code with try-catch:
try {
// First of all, let's begin a transaction
$mysqli->autocommit(false);
$mysqli->begin_transaction();
// A set of queries
$mysqli->query('first query');
$mysqli->query('second query');
$mysqli->query('third query');
// If we arrive here, it means that no exception was thrown
// i.e. no query has failed, and we can commit the transaction
$mysqli->commit();
} catch (Exception $e) {
// An exception has been thrown
// We must rollback the transaction
$mysqli->rollback();
}

Yii how to check if query execution failed

I M Using YII to develop web application
i want to check if query executed successfully or not
$data = Yii::app()->db->createCommand($SQL);
$result = $data->queryAll();
if(count($result) == 0)
{
throw new SoapFault('Sender', 'Unable to get display information.');
}
if code will execute if select query return no result-set. but i want to check if query executed successfully or not. based on that i want to throw exception. then if query executed successfully and returned no result-set then some other exception.
how to do this ? any suggestions ?
try {
$result = $data->queryAll();
} catch (Exception $ex) {
echo 'Query failed', $ex->getMessage();
}
try
{
$result = Yii::app()->db->createCommand($sqlQuery)->execute();
echo 'success';
}
catch (Exception $e)
{
echo 'fail';
}

MySQL 5.5 SIGNAL and PDO exceptions

I have a TRIGGER for a MySQL table which raises exception with SIGNAL when there's something wrong with the data provided. How can I catch this exception in PHP with PDO?
Thanks in advance.
UPDATE
More information on my problem: I am executing several queries within a transaction and I want to rollback the transaction if one of them fails because there was a SIGNAL. Currently a signal is activated but all the other queries are executed.
ANOTHER UPDATE
So far I've got:
try {
$db->beginTransaction();
if (!$db->query('UPDATE accounts SET amount = amount - 10 WHERE id = 1')) {
throw new Exception($db->errorInfo()[2]);
}
if (!$db->query('UPDATE accounts SET amount = amount + 10 WHERE id = 2')) {
throw new Exception($db->errorInfo()[2]);
}
$db->commit();
} catch (Exception $e) {
$db->rollback();
echo 'There was an error: ' . $e->getMessage();
}
Is this the proper way to do a transaction with handling the exceptions?
Sorry if my question is too broad.
Try something like:
$ls_error = "";
$mysqli->autocommit(FALSE);
// first sql
if (!$mysqli->query($sql)) {
$ls_error .= sprintf("Error: %d: %s\n",$mysqli->errno, $mysqli->error);
$mysqli->rollback();
}
// second one
if ($ls_error == "") {
if (!$mysqli->query($sql)) {
$ls_error .= sprintf("Error: %d: %s\n",$mysqli->errno, $mysqli->error);
$mysqli->rollback();
}
}
// third one
if ($ls_error == "") {
if (!$mysqli->query($sql)) {
$ls_error .= sprintf("Error: %d: %s\n",$mysqli->errno, $mysqli->error);
$mysqli->rollback();
}
}
if ($ls_error == "") {
$mysqli->commit();
} else {
echo $ls_error;
}
Abstract example. I have the solution works without any problems.
Trigger:
IF NEW.value < 0 THEN
SET #msg = CONCAT('Wrong count_from value! ', NEW.value);
SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = #msg;
END IF;
php (PDO):
try {
$conn->query('INSERT INTO `table` SET `value` = -1');
} catch (Exception $err) {
$error_message = $conn->query('SELECT #msg')->fetchColumn();
}

Categories