diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php index d5f253880c..30f977a2d8 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php @@ -11,6 +11,7 @@ use Drupal\Core\Database\DatabaseException; use Drupal\Core\Database\Connection as DatabaseConnection; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Database\TransactionNoActiveException; /** * @addtogroup database @@ -425,12 +426,15 @@ public function rollBack($savepoint_name = 'drupal_transaction') { return parent::rollBack($savepoint_name); } catch (\PdoException $e) { - // @todo - if ($e->getMessage() !== 'There is no active transaction') { - throw $e; - } } - return FALSE; + + // Convert the \PdoException to a Drupal database exception. + if ($e->getMessage() === 'There is no active transaction') { + // Note that on PHP 7 this code is never called as an exception is not + // thrown by PDO in this case. + throw new TransactionNoActiveException($e->getMessage(), $e->getCode(), $e); + } + throw new DatabaseExceptionWrapper($e->getMessage(), $e->getCode(), $e); } /** @@ -441,10 +445,14 @@ protected function doCommit() { $success = parent::doCommit(); } catch (\PdoException $e) { - // @todo if ($e->getMessage() !== 'There is no active transaction') { - throw $e; + throw new DatabaseExceptionWrapper($e->getMessage(), $e->getCode(), $e); } + // On PHP 7 $this->connection->commit() does not throw an exception if + // there is no active transaction.In order to maintain consistent + // behaviour on PHP 8 we have to assume that all queries have been + // committed. This situation occurs when tables are altered or created + // (DDL transactions are not supported). $success = TRUE; if (!empty($this->rootTransactionEndCallbacks)) { $callbacks = $this->rootTransactionEndCallbacks; diff --git a/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php b/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php index 2592a9894d..7831307af2 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php @@ -258,14 +258,16 @@ public function testTransactionWithDdlStatement() { $transaction = $this->connection->startTransaction(); $this->insertRow('row'); $this->executeDDLStatement(); + // Rollback the outer transaction. try { $transaction->rollBack(); unset($transaction); - // @todo An exception should be triggered here, but is not because - // "ROLLBACK" fails silently in MySQL if there is no transaction active. - // @see https://www.drupal.org/project/drupal/issues/2736777 - // $this->fail('Rolling back a transaction containing DDL should fail.'); + // The PHP 7 MySQL driver does not trigger an exception when calling + // rollback and there is no active transaction. + if (PHP_VERSION_ID >= 80000) { + $this->fail('Rolling back a transaction containing DDL should fail.'); + } } catch (TransactionNoActiveException $e) { // Expected exception; just continue testing.