Search code examples
phpmysqlpdotransactions

PHP PDO - There is no active transaction


I am having problem with transactions in php script. I would like to make multiply queries and be able to recall them all, if at least one of them fails. Below you can find a simple example of the script I am using:

$tags_input = array(6,4,5);
$conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',  
DB_USER, DB_PASSW, array(  
    PDO::ATTR_EMULATE_PREPARES => false,  
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));

    $conn->beginTransaction();

    $sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
    $stmt = $conn->prepare($sql);  
    if(count($tags_input)>0){
            for($i = 0;$i<count($tags_input);$i++){
                    $stmt->bindValue(':val0', 57); 
                    $stmt->bindValue(':val1', $tags_input[$i]); 
                    $stmt->bindValue(':val2', 'Y'); 
                    $result = $stmt->execute();

            }

    }

    $res1 = $conn->commit();
    $conn->rollBack();

Now, this example generates an error:

Uncaught exception 'PDOException' with message 'There is no active transaction'

If I erase the line $conn->rollBack();, the error disappears. Therefore I cannot understand, why pdo object can't see open transaction (begintransaction and commit do not generate any errors). I also tried putting rollBack() inside the transaction, but made no difference. I was still getting an error 'There is no active transaction'.

I am running PHP 5.6 and Mysql tables on InnoDB.


Solution

  • Wrap your transaction code inside a try-catch statement.

    //try {
    $tags_input = array(6,4,5);
    $conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',  
    DB_USER, DB_PASSW, array(  
        PDO::ATTR_EMULATE_PREPARES => false,  
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
    } catch (Exception $e) {
      die("Unable to connect: " . $e->getMessage());
    }    
    try {  
        $conn->beginTransaction();   
        $sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
        $stmt = $conn->prepare($sql);  
        if(count($tags_input)>0){
                for($i = 0;$i<count($tags_input);$i++){
                        $stmt->bindValue(':val0', 57); 
                        $stmt->bindValue(':val1', $tags_input[$i]); 
                        $stmt->bindValue(':val2', 'Y'); 
                        $result = $stmt->execute();
                }
        }
    $res1 = $conn->commit();    
    } catch (Exception $e) {
      $conn->rollBack();
      echo "Failed: " . $e->getMessage();
    }
    

    EDIT

    A really well-based and straight-forward explanation of the answer was provided by Richard as a comment.

    The reason you got error is because you were trying to close a transaction when it was already closed. beginTransaction opens one, and EITHER rollBack OR commit closes it. You have to avoid doing BOTH actions, meaning commit/rollback, for a single beginTransaction statement, or you'll get an error. The above try/catch code ensures that only one closing statement is executed.