From 39a543768ece7efcef4ef91e198779d9d5f48465 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Thu, 5 Mar 2020 08:06:37 +0000 Subject: [PATCH] =?UTF-8?q?Issue=20#3109534=20by=20TravisCarden,=20mondrak?= =?UTF-8?q?e,=20andypost,=20G=C3=A1bor=20Hojtsy,=20daffie,=20effulgentsia,?= =?UTF-8?q?=20xjm:=20Raise=20the=20minimum=20MySQL=20version=20to=205.7=20?= =?UTF-8?q?and=20MariaDB=20version=20to=2010.2=20in=20Drupal=209?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/Database/Driver/mysql/Connection.php | 47 ++++++ .../Database/Driver/mysql/Install/Tasks.php | 30 +++- .../Drupal/Core/Database/Install/Tasks.php | 20 +++ .../Database/Driver/mysql/ConnectionTest.php | 134 ++++++++++++++++++ .../Driver/mysql/install/TasksTest.php | 115 +++++++++++++++ 5 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 core/tests/Drupal/Tests/Core/Database/Driver/mysql/ConnectionTest.php create mode 100644 core/tests/Drupal/Tests/Core/Database/Driver/mysql/install/TasksTest.php diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php index 42b5feb033e5..d08fafebb375 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php @@ -546,6 +546,53 @@ public function driver() { return 'mysql'; } + /** + * {@inheritdoc} + */ + public function version() { + if ($this->isMariaDb()) { + return $this->getMariaDbVersionMatch(); + } + + return $this->getServerVersion(); + } + + /** + * Determines whether the MySQL distribution is MariaDB or not. + * + * @return bool + * Returns TRUE if the distribution is MariaDB, or FALSE if not. + */ + public function isMariaDb(): bool { + return (bool) $this->getMariaDbVersionMatch(); + } + + /** + * Gets the MariaDB portion of the server version. + * + * @return string + * The MariaDB portion of the server version if present, or NULL if not. + */ + protected function getMariaDbVersionMatch(): ?string { + // MariaDB may prefix its version string with '5.5.5-', which should be + // ignored. + // @see https://github.com/MariaDB/server/blob/f6633bf058802ad7da8196d01fd19d75c53f7274/include/mysql_com.h#L42. + $regex = '/^(?:(?:5\.5\.5-)|)(\d+\.\d+\.\d+.*-mariadb.*)/i'; + + preg_match($regex, $this->getServerVersion(), $matches); + return (empty($matches[1])) ? NULL : $matches[1]; + } + + /** + * Gets the server version. + * + * @return string + * The PDO server version. + */ + protected function getServerVersion(): string { + return $this->connection->getAttribute(\PDO::ATTR_SERVER_VERSION); + } + public function databaseType() { return 'mysql'; } diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php b/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php index ff441d3cae8f..c83b36ccbe03 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Database\Driver\mysql\Install; +use Drupal\Core\Database\ConnectionNotDefinedException; use Drupal\Core\Database\Install\Tasks as InstallTasks; use Drupal\Core\Database\Database; use Drupal\Core\Database\Driver\mysql\Connection; @@ -12,6 +13,16 @@ */ class Tasks extends InstallTasks { + /** + * Minimum required MySQL version. + */ + const MYSQL_MINIMUM_VERSION = '5.7.0'; + + /** + * Minimum required MariaDB version. + */ + const MARIADB_MINIMUM_VERSION = '10.2.0'; + /** * Minimum required MySQLnd version. */ @@ -43,18 +54,25 @@ public function __construct() { * {@inheritdoc} */ public function name() { - return t('MySQL, MariaDB, Percona Server, or equivalent'); + try { + if ($this->getConnection()->isMariaDb()) { + return $this->t('MariaDB'); + } + return $this->t('MySQL, Percona Server, or equivalent'); + } + catch (ConnectionNotDefinedException $e) { + return $this->t('MySQL, MariaDB, Percona Server, or equivalent'); + } } /** * {@inheritdoc} */ public function minimumVersion() { - // This can not be increased above '5.5.5' without dropping support for all - // MariaDB versions. MariaDB prefixes its version string with '5.5.5-'. For - // more information, see - // https://github.com/MariaDB/server/blob/f6633bf058802ad7da8196d01fd19d75c53f7274/include/mysql_com.h#L42. - return '5.5.3'; + if ($this->getConnection()->isMariaDb()) { + return static::MARIADB_MINIMUM_VERSION; + } + return static::MYSQL_MINIMUM_VERSION; } /** diff --git a/core/lib/Drupal/Core/Database/Install/Tasks.php b/core/lib/Drupal/Core/Database/Install/Tasks.php index 6ba3ba733c67..ef071efc508f 100644 --- a/core/lib/Drupal/Core/Database/Install/Tasks.php +++ b/core/lib/Drupal/Core/Database/Install/Tasks.php @@ -3,6 +3,7 @@ namespace Drupal\Core\Database\Install; use Drupal\Core\Database\Database; +use Drupal\Core\StringTranslation\TranslatableMarkup; /** * Database installer structure. @@ -300,4 +301,23 @@ public function validateDatabaseSettings($database) { return $errors; } + /** + * Translates a string to the current language or to a given language. + * + * @see \Drupal\Core\StringTranslation\TranslatableMarkup::__construct() + */ + protected function t($string, array $args = [], array $options = []) { + return new TranslatableMarkup($string, $args, $options); + } + + /** + * Returns the database connection. + * + * @return \Drupal\Core\Database\Connection + * The database connection. + */ + protected function getConnection() { + return Database::getConnection(); + } + } diff --git a/core/tests/Drupal/Tests/Core/Database/Driver/mysql/ConnectionTest.php b/core/tests/Drupal/Tests/Core/Database/Driver/mysql/ConnectionTest.php new file mode 100644 index 000000000000..be9081db3d17 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/Driver/mysql/ConnectionTest.php @@ -0,0 +1,134 @@ +<?php + +namespace Drupal\Tests\Core\Database\Driver\mysql; + +use Drupal\Core\Database\Driver\mysql\Connection; +use Drupal\Tests\UnitTestCase; + +/** + * Tests MySQL database connections. + * + * @coversDefaultClass \Drupal\Core\Database\Driver\mysql\Connection + * @group Database + */ +class ConnectionTest extends UnitTestCase { + + /** + * A PDO object prophecy. + * + * @var \PDO|\Prophecy\Prophecy\ObjectProphecy + */ + private $pdoConnection; + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->pdoConnection = $this->prophesize(\PDO::class); + } + + /** + * Creates a Connection object for testing. + * + * @return \Drupal\Core\Database\Driver\mysql\Connection + */ + private function createConnection(): Connection { + /** @var \PDO $pdo_connection */ + $pdo_connection = $this->pdoConnection->reveal(); + + return new class($pdo_connection) extends Connection { + + public function __construct(\PDO $connection) { + $this->connection = $connection; + } + + }; + } + + /** + * @covers ::version + * @covers ::isMariaDb + * @dataProvider providerVersionAndIsMariaDb + */ + public function testVersionAndIsMariaDb(bool $expected_is_mariadb, string $server_version, string $expected_version): void { + $this->pdoConnection + ->getAttribute(\PDO::ATTR_SERVER_VERSION) + ->shouldBeCalled() + ->willReturn($server_version); + $connection = $this->createConnection(); + + $is_mariadb = $connection->isMariaDb(); + $version = $connection->version(); + + $this->assertSame($expected_is_mariadb, $is_mariadb); + $this->assertSame($expected_version, $version); + } + + /** + * Provides test data. + * + * @return array + */ + public function providerVersionAndIsMariaDb(): array { + return [ + // MariaDB. + [ + TRUE, + '10.2.0-MariaDB', + '10.2.0-MariaDB', + ], + [ + TRUE, + '10.2.1-MARIADB', + '10.2.1-MARIADB', + ], + [ + TRUE, + '10.2.2-alphaX-MARIADB', + '10.2.2-alphaX-MARIADB', + ], + [ + TRUE, + '5.5.5-10.2.20-MariaDB-1:10.2.20+maria~bionic', + '10.2.20-MariaDB-1:10.2.20+maria~bionic', + ], + [ + TRUE, + '5.5.5-10.3.22-MariaDB-0+deb10u1', + '10.3.22-MariaDB-0+deb10u1', + ], + [ + TRUE, + '5.5.5-10.3.22-buzz+-MariaDB-0+deb10u1', + '10.3.22-buzz+-MariaDB-0+deb10u1', + ], + // MySQL. + [ + FALSE, + '5.5.5-10.2.20-notMariaDB', + '5.5.5-10.2.20-notMariaDB', + ], + [ + FALSE, + '5.5.5', + '5.5.5', + ], + [ + FALSE, + '5.5.5-', + '5.5.5-', + ], + [ + FALSE, + '5.7.28', + '5.7.28', + ], + [ + FALSE, + '5.7.28-31', + '5.7.28-31', + ], + ]; + } + +} diff --git a/core/tests/Drupal/Tests/Core/Database/Driver/mysql/install/TasksTest.php b/core/tests/Drupal/Tests/Core/Database/Driver/mysql/install/TasksTest.php new file mode 100644 index 000000000000..4dbf3a5e1f0d --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/Driver/mysql/install/TasksTest.php @@ -0,0 +1,115 @@ +<?php + +namespace Drupal\Tests\Core\Database\Driver\mysql\install; + +use Drupal\Core\Database\ConnectionNotDefinedException; +use Drupal\Core\Database\Driver\mysql\Connection; +use Drupal\Core\Database\Driver\mysql\Install\Tasks; +use Drupal\Tests\UnitTestCase; + +/** + * Tests the MySQL install tasks. + * + * @coversDefaultClass \Drupal\Core\Database\Driver\mysql\Install\Tasks + * @group Database + */ +class TasksTest extends UnitTestCase { + + /** + * A connection object prophecy. + * + * @var \Drupal\Core\Database\Driver\mysql\Connection|\Prophecy\Prophecy\ObjectProphecy + */ + private $connection; + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->connection = $this->prophesize(Connection::class); + } + + /** + * Creates a Connection object for testing. + * + * @return \Drupal\Core\Database\Driver\mysql\Connection + */ + private function createTasks(): Tasks { + /** @var \Drupal\Core\Database\Driver\mysql\Connection $connection */ + $connection = $this->connection->reveal(); + + return new class($connection) extends Tasks { + + private $connection; + + public function __construct(Connection $connection) { + $this->connection = $connection; + } + + protected function getConnection() { + return $this->connection; + } + + protected function t($string, array $args = [], array $options = []) { + return $string; + } + + }; + } + + /** + * @covers ::minimumVersion + * @covers ::name + * @dataProvider providerNameAndMinimumVersion + */ + public function testNameAndMinimumVersion(bool $is_mariadb, string $expected_name, string $expected_minimum_version): void { + $this->connection + ->isMariaDb() + ->shouldBeCalledTimes(2) + ->willReturn($is_mariadb); + $tasks = $this->createTasks(); + + $minimum_version = $tasks->minimumVersion(); + $name = $tasks->name(); + + $this->assertSame($expected_minimum_version, $minimum_version); + $this->assertSame($expected_name, $name); + + } + + /** + * Provides test data. + * + * @return array + */ + public function providerNameAndMinimumVersion(): array { + return [ + [ + TRUE, + 'MariaDB', + Tasks::MARIADB_MINIMUM_VERSION, + ], + [ + FALSE, + 'MySQL, Percona Server, or equivalent', + Tasks::MYSQL_MINIMUM_VERSION, + ], + ]; + } + + /** + * @covers ::name + */ + public function testNameWithNoConnection() { + $this->connection + ->isMariaDb() + ->shouldBeCalledOnce() + ->willThrow(ConnectionNotDefinedException::class); + $tasks = $this->createTasks(); + + $name = $tasks->name(); + + $this->assertSame('MySQL, MariaDB, Percona Server, or equivalent', $name); + } + +} -- GitLab