Net Jacker
Net Jacker

Reputation: 41

Rolling back MySQL queries

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?

Upvotes: 1

Views: 514

Answers (1)

Paul Spiegel
Paul Spiegel

Reputation: 31772

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

  1. you didn't validate the input properly
  2. 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
}

Upvotes: 2

Related Questions