diff --git a/core/assets/scaffold/files/default.settings.php b/core/assets/scaffold/files/default.settings.php index 1c8dbee9e7..0bcded0271 100644 --- a/core/assets/scaffold/files/default.settings.php +++ b/core/assets/scaffold/files/default.settings.php @@ -105,14 +105,6 @@ * webserver. For most other drivers, you must specify a * username, password, host, and database name. * - * Transaction support is enabled by default for all drivers that support it, - * including MySQL. To explicitly disable it, set the 'transactions' key to - * FALSE. - * Note that some configurations of MySQL, such as the MyISAM engine, don't - * support it and will proceed silently even if enabled. If you experience - * transaction related crashes with such configuration, set the 'transactions' - * key to FALSE. - * * For each database, you may optionally specify multiple "target" databases. * A target database allows Drupal to try to send certain queries to a * different database if it can but fall back to the default connection if not. diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php index 0f27230940..e79e38d201 100644 --- a/core/lib/Drupal/Core/Database/Connection.php +++ b/core/lib/Drupal/Core/Database/Connection.php @@ -66,13 +66,6 @@ abstract class Connection { */ protected $statementClass = 'Drupal\Core\Database\Statement'; - /** - * Whether this database connection supports transactions. - * - * @var bool - */ - protected $transactionSupport = TRUE; - /** * Whether this database connection supports transactional DDL. * @@ -173,6 +166,12 @@ abstract class Connection { * - Other driver-specific options. */ public function __construct(\PDO $connection, array $connection_options) { + // The 'transactions' option is deprecated. + if (isset($connection_options['transactions'])) { + @trigger_error('Passing a \'transactions\' connection option to ' . __METHOD__ . ' is deprecated in drupal:9.1.0 and is removed in drupal:10.0.0. All database drivers should support transactions. See xxx', E_USER_DEPRECATED); + unset($connection_options['transactions']); + } + // Initialize and prepare the connection prefix. $this->setPrefix(isset($connection_options['prefix']) ? $connection_options['prefix'] : ''); @@ -1103,9 +1102,6 @@ public function startTransaction($name = '') { * @see \Drupal\Core\Database\Transaction::rollBack() */ public function rollBack($savepoint_name = 'drupal_transaction') { - if (!$this->supportsTransactions()) { - return; - } if (!$this->inTransaction()) { throw new TransactionNoActiveException(); } @@ -1165,9 +1161,6 @@ public function rollBack($savepoint_name = 'drupal_transaction') { * @see \Drupal\Core\Database\Transaction */ public function pushTransaction($name) { - if (!$this->supportsTransactions()) { - return; - } if (isset($this->transactionLayers[$name])) { throw new TransactionNameNonUniqueException($name . " is already in use."); } @@ -1198,9 +1191,6 @@ public function pushTransaction($name) { * @see \Drupal\Core\Database\Transaction */ public function popTransaction($name) { - if (!$this->supportsTransactions()) { - return; - } // The transaction has already been committed earlier. There is nothing we // need to do. If this transaction was part of an earlier out-of-order // rollback, an exception would already have been thrown by @@ -1381,9 +1371,15 @@ public function clientVersion() { * * @return bool * TRUE if this connection supports transactions, FALSE otherwise. + * + * @deprecated in drupal:9.1.0 and is removed in drupal:10.0.0. All database + * drivers should support transactions. + * + * @see xxx */ public function supportsTransactions() { - return $this->transactionSupport; + @trigger_error(__METHOD__ . ' is deprecated in drupal:9.1.0 and is removed in drupal:10.0.0. All database drivers should support transactions. See xxx', E_USER_DEPRECATED); + return TRUE; } /** diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php index 42b5feb033..3bcacde929 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php @@ -334,21 +334,6 @@ class Connection extends DatabaseConnection { 'zerofill', ]; - /** - * Constructs a Connection object. - */ - public function __construct(\PDO $connection, array $connection_options = []) { - parent::__construct($connection, $connection_options); - - // This driver defaults to transaction support, except if explicitly passed FALSE. - $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE); - - // MySQL never supports transactional DDL. - $this->transactionalDDLSupport = FALSE; - - $this->connectionOptions = $connection_options; - } - /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php index 025b25039d..bb4643928f 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php @@ -71,21 +71,15 @@ class Connection extends DatabaseConnection { 'when', 'where', 'window', 'with', ]; + /** + * {@inheritdoc} + */ + protected $transactionalDDLSupport = TRUE; + /** * Constructs a connection object. */ public function __construct(\PDO $connection, array $connection_options) { - parent::__construct($connection, $connection_options); - - // This driver defaults to transaction support, except if explicitly passed FALSE. - $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE); - - // Transactional DDL is always available in PostgreSQL, - // but we'll only enable it if standard transactions are. - $this->transactionalDDLSupport = $this->transactionSupport; - - $this->connectionOptions = $connection_options; - // Force PostgreSQL to use the UTF-8 character set by default. $this->connection->exec("SET NAMES 'UTF8'"); diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php index fe007cfe05..67f03cd417 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php @@ -55,6 +55,11 @@ class Connection extends DatabaseConnection { */ public $tableDropped = FALSE; + /** + * {@inheritdoc} + */ + protected $transactionalDDLSupport = TRUE; + /** * Constructs a \Drupal\Core\Database\Driver\sqlite\Connection object. */ @@ -65,11 +70,6 @@ public function __construct(\PDO $connection, array $connection_options) { parent::__construct($connection, $connection_options); - // This driver defaults to transaction support, except if explicitly passed FALSE. - $this->transactionSupport = $this->transactionalDDLSupport = !isset($connection_options['transactions']) || $connection_options['transactions'] !== FALSE; - - $this->connectionOptions = $connection_options; - // Attach one database for each registered prefix. $prefixes = $this->prefixes; foreach ($prefixes as &$prefix) { diff --git a/core/modules/block_content/tests/src/Functional/BlockContentCreationTest.php b/core/modules/block_content/tests/src/Functional/BlockContentCreationTest.php index 7f3b251381..2ab0fa2d13 100644 --- a/core/modules/block_content/tests/src/Functional/BlockContentCreationTest.php +++ b/core/modules/block_content/tests/src/Functional/BlockContentCreationTest.php @@ -208,28 +208,14 @@ public function testFailedBlockCreation() { } $connection = Database::getConnection(); - if ($connection->supportsTransactions()) { - // Check that the block does not exist in the database. - $id = $connection->select('block_content_field_data', 'b') - ->fields('b', ['id']) - ->condition('info', 'fail_creation') - ->execute() - ->fetchField(); - $this->assertFalse($id, 'Transactions supported, and block not found in database.'); - } - else { - // Check that the block exists in the database. - $id = $connection->select('block_content_field_data', 'b') - ->fields('b', ['id']) - ->condition('info', 'fail_creation') - ->execute() - ->fetchField(); - $this->assertTrue($id, 'Transactions not supported, and block found in database.'); - - // Check that the failed rollback was logged. - $records = $connection->query("SELECT wid FROM {watchdog} WHERE message LIKE 'Explicit rollback failed%'")->fetchAll(); - $this->assertTrue(count($records) > 0, 'Transactions not supported, and rollback error logged to watchdog.'); - } + + // Check that the block does not exist in the database. + $id = $connection->select('block_content_field_data', 'b') + ->fields('b', ['id']) + ->condition('info', 'fail_creation') + ->execute() + ->fetchField(); + $this->assertFalse($id); } /** diff --git a/core/modules/node/tests/src/Functional/NodeCreationTest.php b/core/modules/node/tests/src/Functional/NodeCreationTest.php index c64ace0afe..52e2e8f0f5 100644 --- a/core/modules/node/tests/src/Functional/NodeCreationTest.php +++ b/core/modules/node/tests/src/Functional/NodeCreationTest.php @@ -108,20 +108,9 @@ public function testFailedPageCreation() { $this->pass('Expected exception has been thrown.'); } - if (Database::getConnection()->supportsTransactions()) { - // Check that the node does not exist in the database. - $node = $this->drupalGetNodeByTitle($edit['title']); - $this->assertFalse($node, 'Transactions supported, and node not found in database.'); - } - else { - // Check that the node exists in the database. - $node = $this->drupalGetNodeByTitle($edit['title']); - $this->assertTrue($node, 'Transactions not supported, and node found in database.'); - - // Check that the failed rollback was logged. - $records = static::getWatchdogIdsForFailedExplicitRollback(); - $this->assertTrue(count($records) > 0, 'Transactions not supported, and rollback error logged to watchdog.'); - } + // Check that the node does not exist in the database. + $node = $this->drupalGetNodeByTitle($edit['title']); + $this->assertFalse($node); // Check that the rollback error was logged. $records = static::getWatchdogIdsForTestExceptionRollback(); @@ -289,15 +278,4 @@ protected static function getWatchdogIdsForTestExceptionRollback() { return $matches; } - /** - * Gets the log records with the explicit rollback failed exception message. - * - * @return \Drupal\Core\Database\StatementInterface - * A prepared statement object (already executed), which contains the log - * records with the explicit rollback failed exception message. - */ - protected static function getWatchdogIdsForFailedExplicitRollback() { - return Database::getConnection()->query("SELECT wid FROM {watchdog} WHERE message LIKE 'Explicit rollback failed%'")->fetchAll(); - } - } diff --git a/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php index c9ab16a551..e0d294c40a 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php @@ -3,6 +3,7 @@ namespace Drupal\KernelTests\Core\Database; use Drupal\Component\Render\FormattableMarkup; +use Drupal\Core\Database\Connection; use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseExceptionWrapper; @@ -117,6 +118,22 @@ public function testConnectionOptions() { $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.'); } + /** + * Tests the deprecation of the 'transactions' connection option. + * + * @group legacy + * @expectedDeprecation Passing a 'transactions' connection option to Drupal\Core\Database\Connection::__construct is deprecated in drupal:9.1.0 and is removed in drupal:10.0.0. All database drivers should support transactions. See xxx + * @expectedDeprecation Drupal\Core\Database\Connection::supportsTransactions is deprecated in drupal:9.1.0 and is removed in drupal:10.0.0. All database drivers should support transactions. See xxx + */ + public function testTransactionsOptionDeprecation() { + $connection_info = Database::getConnectionInfo('default'); + $connection_info['default']['transactions'] = FALSE; + Database::addConnectionInfo('default', 'foo', $connection_info['default']); + $foo_connection = Database::getConnection('foo', 'default'); + $this->assertInstanceOf(Connection, $foo_connection); + $this->assertTrue($foo_connection->supportsTransactions()); + } + /** * Ensure that you cannot execute multiple statements on MySQL. */ diff --git a/core/tests/Drupal/KernelTests/Core/Database/DeleteTruncateTest.php b/core/tests/Drupal/KernelTests/Core/Database/DeleteTruncateTest.php index b4aa0a9b14..be6069edc6 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/DeleteTruncateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/DeleteTruncateTest.php @@ -70,11 +70,6 @@ public function testTruncate() { * Confirms that we can truncate a whole table while in transaction. */ public function testTruncateInTransaction() { - // This test won't work right if transactions are not supported. - if (!$this->connection->supportsTransactions()) { - $this->markTestSkipped('The database driver does not support transactions.'); - } - $num_records_before = $this->connection->select('test')->countQuery()->execute()->fetchField(); $this->assertGreaterThan(0, $num_records_before, 'The table is not empty.'); @@ -109,11 +104,6 @@ public function testTruncateInTransaction() { * Confirms that transaction rollback voids a truncate operation. */ public function testTruncateTransactionRollback() { - // This test won't work right if transactions are not supported. - if (!$this->connection->supportsTransactions()) { - $this->markTestSkipped('The database driver does not support transactions.'); - } - $num_records_before = $this->connection->select('test')->countQuery()->execute()->fetchField(); $this->assertGreaterThan(0, $num_records_before, 'The table is not empty.'); diff --git a/core/tests/Drupal/KernelTests/Core/Database/InvalidDataTest.php b/core/tests/Drupal/KernelTests/Core/Database/InvalidDataTest.php index 708ca9781d..cf0e02b76f 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/InvalidDataTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/InvalidDataTest.php @@ -2,7 +2,6 @@ namespace Drupal\KernelTests\Core\Database; -use Drupal\Core\Database\Database; use Drupal\Core\Database\IntegrityConstraintViolationException; /** @@ -43,16 +42,7 @@ public function testInsertDuplicateData() { $name = $this->connection->query('SELECT name FROM {test} WHERE age = :age', [':age' => 63])->fetchField(); if ($name == 'Elvis') { - if (!Database::getConnection()->supportsTransactions()) { - // This is an expected fail. - // Database engines that don't support transactions can leave partial - // inserts in place when an error occurs. This is the case for MySQL - // when running on a MyISAM table. - $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions"); - } - else { - $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.'); - } + $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.'); } else { $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.'); @@ -120,16 +110,7 @@ public function testInsertDuplicateDataFromSelect() { $name = $this->connection->query('SELECT name FROM {test} WHERE age = :age', [':age' => 75])->fetchField(); if ($name == 'Frank') { - if (!Database::getConnection()->supportsTransactions()) { - // This is an expected fail. - // Database engines that don't support transactions can leave partial - // inserts in place when an error occurs. This is the case for MySQL - // when running on a MyISAM table. - $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions"); - } - else { - $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.'); - } + $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.'); } else { $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.'); diff --git a/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php b/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php index fae1f69bf8..0298116056 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php @@ -144,11 +144,6 @@ protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statem * nothing. */ public function testTransactionRollBackSupported() { - // This test won't work right if transactions are not supported. - if (!$this->connection->supportsTransactions()) { - $this->markTestSkipped("The '{$this->connection->driver()}' database driver does not support transactions."); - } - try { // Create two nested transactions. Roll back from the inner one. $this->transactionOuterLayer('B', TRUE); @@ -165,33 +160,6 @@ public function testTransactionRollBackSupported() { } } - /** - * Tests transaction rollback on a database that doesn't support transactions. - * - * If the active driver supports transactions, this test does nothing. - */ - public function testTransactionRollBackNotSupported() { - // This test won't work right if transactions are supported. - if ($this->connection->supportsTransactions()) { - $this->markTestSkipped("The '{$this->connection->driver()}' database driver supports transactions."); - } - - try { - // Create two nested transactions. Attempt to roll back from the inner one. - $this->transactionOuterLayer('B', TRUE); - - // Because our current database claims to not support transactions, - // the inserted rows should be present despite the attempt to roll back. - $saved_age = $this->connection->query('SELECT age FROM {test} WHERE name = :name', [':name' => 'DavidB'])->fetchField(); - $this->assertIdentical($saved_age, '24', 'DavidB not rolled back, since transactions are not supported.'); - $saved_age = $this->connection->query('SELECT age FROM {test} WHERE name = :name', [':name' => 'DanielB'])->fetchField(); - $this->assertIdentical($saved_age, '19', 'DanielB not rolled back, since transactions are not supported.'); - } - catch (\Exception $e) { - $this->fail($e->getMessage()); - } - } - /** * Tests a committed transaction. * @@ -379,11 +347,6 @@ public function assertRowAbsent($name, $message = NULL) { * Tests transaction stacking, commit, and rollback. */ public function testTransactionStacking() { - // This test won't work right if transactions are not supported. - if (!$this->connection->supportsTransactions()) { - $this->markTestSkipped("The '{$this->connection->driver()}' database driver does not support transactions."); - } - // Standard case: pop the inner transaction before the outer transaction. $transaction = $this->connection->startTransaction(); $this->insertRow('outer'); diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php index a3566fdd6c..dd5c8da927 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php @@ -5,7 +5,6 @@ use Drupal\comment\Entity\Comment; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\comment\Tests\CommentTestTrait; -use Drupal\Core\Database\Database; use Drupal\Core\Language\LanguageInterface; use Drupal\block\Entity\Block; use Drupal\entity_test\Entity\EntityTest; @@ -547,16 +546,9 @@ public function testEntityRollback() { $this->pass('Expected exception has been thrown.'); } - if (Database::getConnection()->supportsTransactions()) { - // Check that the block does not exist in the database. - $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute(); - $this->assertTrue(empty($ids), 'Transactions supported, and entity not found in database.'); - } - else { - // Check that the block exists in the database. - $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute(); - $this->assertFalse(empty($ids), 'Transactions not supported, and entity found in database.'); - } + // Check that the block does not exist in the database. + $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute(); + $this->assertEmpty($ids); } } diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 1c8dbee9e7..0bcded0271 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -105,14 +105,6 @@ * webserver. For most other drivers, you must specify a * username, password, host, and database name. * - * Transaction support is enabled by default for all drivers that support it, - * including MySQL. To explicitly disable it, set the 'transactions' key to - * FALSE. - * Note that some configurations of MySQL, such as the MyISAM engine, don't - * support it and will proceed silently even if enabled. If you experience - * transaction related crashes with such configuration, set the 'transactions' - * key to FALSE. - * * For each database, you may optionally specify multiple "target" databases. * A target database allows Drupal to try to send certain queries to a * different database if it can but fall back to the default connection if not.