diff -u b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php --- b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php +++ b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php @@ -510,6 +510,7 @@ unset($connection_info['default']['autoload']); unset($connection_info['default']['pdo']); unset($connection_info['default']['init_commands']); + unset($connection_info['default']['isolation_level']); // Remove database connection info that is not used by SQLite. if ($driver === 'sqlite') { unset($connection_info['default']['username']); @@ -520,7 +521,6 @@ unset($connection_info['default']['autoload']); unset($connection_info['default']['pdo']); unset($connection_info['default']['init_commands']); - unset($connection_info['default']['isolation_level']); // Remove database connection info that is not used by SQLite. if ($driver === 'sqlite') { unset($connection_info['default']['username']); only in patch2: unchanged: --- a/core/modules/mysql/mysql.install +++ b/core/modules/mysql/mysql.install @@ -41,6 +41,31 @@ function mysql_requirements($phase) { ':performance_doc' => 'https://www.drupal.org/docs/system-requirements/setting-the-mysql-transaction-isolation-level', ]), ]; + + $tables_missing_keys = []; + $missing_primary_key = false; + $tables = $connection->schema()->findTables('%'); + $description = 'To insure correct operation with READ-COMMITTED, ensure all tables have a primary key.'; + + foreach($tables as $table) { + $primary_key_column = Database::getConnection()->query("SHOW KEYS FROM {" . $table . "} WHERE Key_name = 'PRIMARY'")->fetchAllAssoc('Column_name'); + if (empty($primary_key_column)) { + $missing_primary_key = true; + $tables_missing_keys[] = $table; + } + } + + if ($missing_primary_key) { + $tables_missing_key_text = implode(', ', $tables_missing_keys); + $description = "To insure correct operation with READ-COMMITTED, ensure all tables have a primary key. The following table(s) do not contain a primary key: $tables_missing_key_text"; + } + + $requirements['mysql_primary_key_existence'] = [ + 'title' => t('MySQL primary keys'), + 'severity' => (!$missing_primary_key) ? REQUIREMENT_OK : REQUIREMENT_WARNING, + 'value' => (!$missing_primary_key) ? "Primary keys exist" : "Primary keys missing", + 'description' => t($description) + ]; } } only in patch2: unchanged: --- /dev/null +++ b/core/modules/mysql/tests/src/Functional/mysql/PrimaryKeyExistsTest.php @@ -0,0 +1,66 @@ +drupalCreateUser([ + 'administer site configuration', + 'access site reports', + ]); + $this->drupalLogin($admin_user); + + // Check the message is not a warning. + $this->drupalGet('admin/reports/status'); + $elements = $this->xpath('//details[@class="system-status-report__entry"]//div[contains(text(), :text)]', [ + ':text' => 'To insure correct operation with READ-COMMITTED, ensure all tables have a primary key.', + ]); + $this->assertCount(1, $elements); + $this->assertStringNotContainsString('warning', $elements[0]->getParent()->getParent()->find('css', 'summary')->getAttribute('class')); + + $connection->schema()->createTable('test_primary_key', [ + 'fields' => [ + 'id' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + ], + ]); + + $this->assertTrue($connection->schema()->tableExists('test_primary_key')); + + // Check the message is not a warning. + $this->drupalGet('admin/reports/status'); + $elements = $this->xpath('//details[@class="system-status-report__entry"]//div[contains(text(), :text)]', [ + ':text' => 'To insure correct operation with READ-COMMITTED, ensure all tables have a primary key. The following table(s) do not contain a primary key: test_primary_key', + ]); + $this->assertCount(1, $elements); + // Ensure it is a warning. + $this->assertStringContainsString('warning', $elements[0]->getParent()->getParent()->find('css', 'summary')->getAttribute('class')); + } +} only in patch2: unchanged: --- /dev/null +++ b/core/tests/Drupal/FunctionalTests/Core/Database/DriverSpecificBrowserTestBase.php @@ -0,0 +1,20 @@ +root = static::getDrupalRoot(); - $connectionInfo = $this->getDatabaseConnectionInfo(); - $test_class_parts = explode('\\', get_class($this)); - $expected_provider = $test_class_parts[2] ?? ''; - for ($i = 3; $i < count($test_class_parts); $i++) { - if ($test_class_parts[$i] === 'Kernel') { - $expected_driver = $test_class_parts[$i + 1] ?? ''; - break; - } - } - if ($connectionInfo['default']['driver'] !== $expected_driver) { - $this->markTestSkipped("This test only runs for the database driver '$expected_driver'. Current database driver is '{$connectionInfo['default']['driver']}'."); - } - - parent::setUp(); - $this->connection = Database::getConnection(); - - // After database initialization, the database driver may be not provided - // by the expected module; skip test in that case. - $running_provider = $this->connection->getProvider(); - $running_driver = $this->connection->driver(); - if ($running_provider !== $expected_provider || $running_driver !== $expected_driver) { - $this->markTestSkipped("This test only runs for the database driver '$expected_driver' provided by the '$expected_provider' module. Connected database driver is '$running_driver' provided by '$running_provider'."); - } - } - + use DriverSpecificTrait; } only in patch2: unchanged: --- /dev/null +++ b/core/tests/Drupal/Tests/DriverSpecificTrait.php @@ -0,0 +1,53 @@ +root = static::getDrupalRoot(); + + if (method_exists($this, 'getDatabaseConnectionInfo')) { + $connectionInfo = $this->getDatabaseConnectionInfo(); + } + else { + $connectionInfo = Database::getConnectionInfo(); + } + $test_class_parts = explode('\\', get_class($this)); + $expected_provider = $test_class_parts[2] ?? ''; + for ($i = 3; $i < count($test_class_parts); $i++) { + if (in_array($test_class_parts[$i], ['Kernel', 'Functional'], TRUE)) { + $expected_driver = $test_class_parts[$i + 1] ?? ''; + break; + } + } + if ($connectionInfo['default']['driver'] !== $expected_driver) { + $this->markTestSkipped("This test only runs for the database driver '$expected_driver'. Current database driver is '{$connectionInfo['default']['driver']}'."); + } + + parent::setUp(); + $this->connection = Database::getConnection(); + + // After database initialization, the database driver may be not provided + // by the expected module; skip test in that case. + $running_provider = $this->connection->getProvider(); + $running_driver = $this->connection->driver(); + if ($running_provider !== $expected_provider || $running_driver !== $expected_driver) { + $this->markTestSkipped("This test only runs for the database driver '$expected_driver' provided by the '$expected_provider' module. Connected database driver is '$running_driver' provided by '$running_provider'."); + } + } +}