Search code examples
phpdoctrine-ormdoctrinephp-8

Exception 'no active transaction' when using ORMExecutor::execute


I upgraded a legacy project from:

PHP 5.6 -> 8.0

doctrine/orm 2.5.14 -> 2.13.4

doctrine/data-fixtures 1.2.2 -> 1.5.3

Now the following code throws the exception:

PDOException : There is no active transaction /var/www/html/src/vendor/doctrine/dbal/src/Driver/PDO/Connection.php:120 /var/www/html/src/vendor/doctrine/dbal/src/Connection.php:1481 /var/www/html/src/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:280

use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;

$ormPurger = new ORMPurger($entityManager, $exclude);
$ormPurger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);
$executor = new ORMExecutor($entityManager, $ormPurger);
$executor->execute($loader->getFixtures());

The exception is thrown when \Doctrine\ORM\EntityManager::transactional calls:

$this->conn->commit()

In the documentation I read:

Not all RDBMS have the capability to allow TRUNCATE statements inside transactions though. Notably, MySQL will produce the infamous There is no active transaction message when we attempt to close a transaction that was already implicitly closed.

doctrine-data-fixtures/en/latest/explanation/transactions-and-purging.html

-- UPDATE

I was able to fix the error by using the default purge mode PURGE_MODE_DELETE instead of PURGE_MODE_TRUNCATE. So does that mean I can't use TRUNCATE operations when using ORMExecutor::execute?


Solution

  • RDBMS such as MySQL does not allow TRUNCATE statements inside transactions.

    ORMExecutor::execute(array $fixtures, $append = false) perform SQL operations in a transaction. So we have to perform any TRUNCATE operations outside this transaction.

    $purger = new ORMPurger($em, $exclude);
    $purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);
    $purger->purge();
    
    $executor = new ORMExecutor($em);
    $executor->execute($loader->getFixtures(), append: true);
    

    To use executor without purging we can pass true to the 2nd argument of execute.

    // Don't do this unless you want to purge all the tables 
    // in the database prior to appending fixtures
    
    $executor->execute($loader->getFixtures(), new ORMPurger());