diff --git a/.ddev/config.yaml b/.ddev/config.yaml index 636dcbe5b11..8cc63b51950 100644 --- a/.ddev/config.yaml +++ b/.ddev/config.yaml @@ -8,7 +8,7 @@ additional_hostnames: [] additional_fqdns: [] database: type: mysql - version: "5.7" + version: "8.0" hooks: pre-start: - exec-host: bash .ddev/scripts/configure-platform.sh diff --git a/.github/workflows/matomo-tests.yml b/.github/workflows/matomo-tests.yml index f54d3944ff3..e4e0cece28e 100644 --- a/.github/workflows/matomo-tests.yml +++ b/.github/workflows/matomo-tests.yml @@ -64,11 +64,11 @@ jobs: - php: '8.1' adapter: 'PDO_MYSQL' mysql-engine: 'Mysql' - mysql-version: '5.7' + mysql-version: '8.0' - php: '8.2' adapter: 'PDO_MYSQL' mysql-engine: 'Mariadb' - mysql-version: '11.4' + mysql-version: '10.6' - php: '8.5' adapter: 'MYSQLI' mysql-engine: 'Mysql' @@ -400,6 +400,8 @@ jobs: ui-test-options: '--plugin=${{ matrix.plugin }}' test-type: 'UI' php-version: '8.1' + mysql-engine: 'Mysql' + mysql-version: '8.0' node-version: '16' redis-service: true artifacts-pass: ${{ secrets.ARTIFACTS_PASS }} @@ -435,6 +437,8 @@ jobs: --test-group=${{ strategy.job-index }} test-type: 'UI' php-version: '8.1' + mysql-engine: 'Mysql' + mysql-version: '8.0' node-version: '16' redis-service: true artifacts-pass: ${{ secrets.ARTIFACTS_PASS }} diff --git a/README.md b/README.md index 36b71a2ebb4..8dcee2296ff 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Matomo is released under the GPL v3 (or later) license, see [LICENSE](LICENSE). ## Requirements * PHP 8.1.0 or greater - * MySQL version 5.5 or greater, or MariaDB + * MySQL version 8.0 or greater, or MariaDB version 10.6 or greater * PHP extension pdo and pdo_mysql, or the MySQLi extension * Matomo is OS / server independent diff --git a/config/environment/ui-test-tidb.php b/config/environment/ui-test-tidb.php deleted file mode 100644 index 9587f7f2c65..00000000000 --- a/config/environment/ui-test-tidb.php +++ /dev/null @@ -1,15 +0,0 @@ - DI::add([ - ['API.ScheduledReports.getReports.end', DI::value(function (&$result, $parameters) { - // TiDb uses a different collation, causing the report order to be slightly different. - // To have a consistent sorting, we manually sort here in PHP again. - usort($result, function ($a, $b) { - return strcmp($a['description'], $b['description']); - }); - })], - ]), -]; diff --git a/config/global.ini.php b/config/global.ini.php index 12513ba8116..00d3bcc2243 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -248,7 +248,7 @@ ; whether to enable segment archiving cache ; Note: if you use any plugins, this need to be compliant with Matomo and -; * depending on the segment you create you may need a newer MySQL version (eg 5.7 or newer) +; * depending on the segment you create you may need a newer MySQL version (eg 8.0 or newer) ; * use a reader database for archiving in case you have configured a database reader enable_segments_cache = 1 @@ -410,7 +410,7 @@ archiving_custom_ranges[] = ; If configured, archiving queries will be aborted after the configured amount of seconds. Set it to -1 if the query time -; should not be limited. Note: This feature requires a recent MySQL version (5.7 or newer) and the PDO\MYSQL extension +; should not be limited. Note: This feature requires a recent database version and the PDO\MYSQL extension ; must be used. Some MySQL forks like MariaDB might not support this feature which uses the MAX_EXECUTION_TIME hint. ; This feature will not work with the MYSQLI extension. archiving_query_max_execution_time = 7200 @@ -683,7 +683,7 @@ ; If configured, will abort a MySQL query after the configured amount of seconds and show an error in the UI to for ; example lower the date range or tweak the segment (if one is applied). Set it to -1 if the query time should not be -; limited. Note: This feature requires a recent MySQL version (5.7 or newer) and the PDO\MYSQL extension must be used. +; limited. Note: This feature requires a recent database version and the PDO\MYSQL extension must be used. ; Some MySQL forks like MariaDB might not support this feature which uses the MAX_EXECUTION_TIME hint. This feature will ; not work with the MYSQLI extension. live_query_max_execution_time = -1 diff --git a/core/Config/DatabaseConfig.php b/core/Config/DatabaseConfig.php index 24cc2610b9a..c14a135fc17 100644 --- a/core/Config/DatabaseConfig.php +++ b/core/Config/DatabaseConfig.php @@ -15,9 +15,4 @@ public static function getSectionName(): string { return 'database'; } - - public static function isTiDb(): bool - { - return self::getConfigValue('schema') === 'Tidb'; - } } diff --git a/core/Db/Adapter/Mysqli.php b/core/Db/Adapter/Mysqli.php index 137453f16b3..798a9c09aca 100644 --- a/core/Db/Adapter/Mysqli.php +++ b/core/Db/Adapter/Mysqli.php @@ -90,22 +90,25 @@ protected function _connect() // phpcs:ignore PSR2.Methods.MethodDeclaration.Und } /** - * Check MySQL version + * Checks the database server version against the required minimum for the + * detected server type (MySQL or MariaDB). * * @throws Exception */ public function checkServerVersion() { - $requiredVersion = Schema::getInstance()->getMinimumSupportedVersion(); - $serverVersion = $this->getServerVersion(); + $serverVersion = (string) $this->getServerVersion(); + $requiredVersion = Schema::getMinimumSupportedVersionForServer($serverVersion); + $comparableVersion = Schema::getComparableVersion($serverVersion); + $databaseType = Schema::getServerTypeFromVersion($serverVersion); - if (version_compare($serverVersion, $requiredVersion) === -1) { - throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion))); + if (version_compare($comparableVersion, $requiredVersion) === -1) { + throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array($databaseType, $comparableVersion, $requiredVersion))); } } /** - * Returns the MySQL server version + * Returns the raw database server version * * @return null|string */ diff --git a/core/Db/Adapter/Pdo/Mysql.php b/core/Db/Adapter/Pdo/Mysql.php index 21cae846efb..2dc016d1a0f 100644 --- a/core/Db/Adapter/Pdo/Mysql.php +++ b/core/Db/Adapter/Pdo/Mysql.php @@ -131,22 +131,25 @@ public static function getDefaultPort() } /** - * Check MySQL version + * Checks the database server version against the required minimum for the + * detected server type (MySQL or MariaDB). * * @throws Exception */ public function checkServerVersion() { - $requiredVersion = Schema::getInstance()->getMinimumSupportedVersion(); - $serverVersion = $this->getServerVersion(); + $serverVersion = (string) $this->getServerVersion(); + $requiredVersion = Schema::getMinimumSupportedVersionForServer($serverVersion); + $comparableVersion = Schema::getComparableVersion($serverVersion); + $databaseType = Schema::getServerTypeFromVersion($serverVersion); - if (version_compare($serverVersion, $requiredVersion) === -1) { - throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion))); + if (version_compare($comparableVersion, $requiredVersion) === -1) { + throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array($databaseType, $comparableVersion, $requiredVersion))); } } /** - * Returns the MySQL server version + * Returns the raw database server version * * @return null|string */ diff --git a/core/Db/BatchInsert.php b/core/Db/BatchInsert.php index a00241c6610..40e1727a7bc 100644 --- a/core/Db/BatchInsert.php +++ b/core/Db/BatchInsert.php @@ -12,7 +12,6 @@ use Exception; use Piwik\Common; use Piwik\Config; -use Piwik\Config\DatabaseConfig; use Piwik\Container\StaticContainer; use Piwik\Db; use Piwik\ExceptionHandler; @@ -99,11 +98,6 @@ public static function tableInsertBatch($tableName, $fields, $values, $throwExce } $filePath = $path . $tableName . '-' . $instanceId . Common::generateUniqId() . '.csv'; - // always use utf8 for TiDb, as TiDb has problems with latin1 - if (DatabaseConfig::isTiDb()) { - $charset = 'utf8'; - } - try { $fileSpec = array( 'delim' => "\t", diff --git a/core/Db/Schema.php b/core/Db/Schema.php index c7215bb6120..59ef34a14de 100644 --- a/core/Db/Schema.php +++ b/core/Db/Schema.php @@ -58,6 +58,61 @@ public static function getDefaultPortForSchema(string $schemaName): int return $schemaClass->getDefaultPort(); } + /** + * Extracts a comparable semantic version (e.g. "10.6.19") from a raw database + * server version string. + * + * MariaDB reports its version with a leading "5.5.5-" compatibility prefix for + * old MySQL clients (e.g. "5.5.5-10.6.19-MariaDB"); that prefix is stripped so + * the actual server version is returned. + */ + public static function getComparableVersion(string $databaseVersion): string + { + if ( + stripos($databaseVersion, 'mariadb') !== false + && preg_match('/5\.5\.5-(\d+(?:\.\d+){0,2})-MariaDB/i', $databaseVersion, $matches) + ) { + return $matches[1]; + } + + if (preg_match('/\d+(?:\.\d+){0,2}/', $databaseVersion, $matches)) { + return $matches[0]; + } + + return $databaseVersion; + } + + /** + * Detects the database server type ("MySQL" or "MariaDB") from a raw server + * version string, matching the values returned by getDatabaseType(). + */ + public static function getServerTypeFromVersion(string $serverVersion): string + { + if (stripos($serverVersion, 'mariadb') !== false) { + return 'MariaDB'; + } + + return 'MySQL'; + } + + /** + * Returns the minimum supported server version to compare the given raw server + * version string against. + * + * The minimum is derived from the actual server type reported in the version + * string rather than from the configured schema, so a server is always held to + * the requirement matching what it really is: a MariaDB server is never checked + * against the MySQL minimum and vice versa, regardless of the schema configured + * in config.ini.php (running MariaDB with the MySQL schema is a supported setup). + */ + public static function getMinimumSupportedVersionForServer(string $serverVersion): string + { + $schemaClassName = self::getSchemaClassName(self::getServerTypeFromVersion($serverVersion)); + /** @var SchemaInterface $schemaClass */ + $schemaClass = new $schemaClassName(); + return $schemaClass->getMinimumSupportedVersion(); + } + /** * Load schema */ diff --git a/core/Db/Schema/Mariadb.php b/core/Db/Schema/Mariadb.php index fea779aea27..fa2bcb4c7ea 100644 --- a/core/Db/Schema/Mariadb.php +++ b/core/Db/Schema/Mariadb.php @@ -16,7 +16,7 @@ */ class Mariadb extends Mysql { - protected $minimumSupportedVersion = '5.5'; + protected $minimumSupportedVersion = '10.6'; public function getDatabaseType(): string { diff --git a/core/Db/Schema/Mysql.php b/core/Db/Schema/Mysql.php index 40cccdfcaca..a95ba634933 100644 --- a/core/Db/Schema/Mysql.php +++ b/core/Db/Schema/Mysql.php @@ -31,7 +31,7 @@ class Mysql implements SchemaInterface public const OPTION_NAME_MATOMO_INSTALL_VERSION = 'install_version'; public const MAX_TABLE_NAME_LENGTH = 64; private $tablesInstalled = null; - protected $minimumSupportedVersion = '5.5'; + protected $minimumSupportedVersion = '8.0'; public function getDatabaseType(): string { diff --git a/core/Db/Schema/Tidb.php b/core/Db/Schema/Tidb.php deleted file mode 100644 index 007534eefe6..00000000000 --- a/core/Db/Schema/Tidb.php +++ /dev/null @@ -1,100 +0,0 @@ -getTableEngine(); - $charset = $this->getUsedCharset(); - $collation = $this->getUsedCollation(); - $rowFormat = $this->getTableRowFormat(); - - if ('utf8mb4' === $charset && '' === $collation) { - $collation = 'utf8mb4_0900_ai_ci'; - } - - $options = "ENGINE=$engine DEFAULT CHARSET=$charset"; - - if ('' !== $collation) { - $options .= " COLLATE=$collation"; - } - - if ('' !== $rowFormat) { - $options .= " $rowFormat"; - } - - return $options; - } - - public function isOptimizeInnoDBSupported(): bool - { - return false; - } - - public function optimizeTables(array $tables, bool $force = false): bool - { - // OPTIMIZE TABLE not supported for TiDb - return false; - } - - public function supportsSortingInSubquery(): bool - { - // TiDb optimizer removes all sorting from subqueries - return false; - } - - public function getSupportedReadIsolationTransactionLevel(): string - { - // TiDB doesn't support READ UNCOMMITTED - return 'READ COMMITTED'; - } - - public function hasReachedEOL(): bool - { - // ignore in EOL checks - return false; - } -} diff --git a/core/Plugin/ControllerAdmin.php b/core/Plugin/ControllerAdmin.php index deabaccadab..33548b9e7e0 100644 --- a/core/Plugin/ControllerAdmin.php +++ b/core/Plugin/ControllerAdmin.php @@ -323,18 +323,7 @@ private static function getEffectiveDatabaseType(string $databaseType, string $d private static function getComparableDatabaseVersion(string $databaseVersion): string { - if ( - stripos($databaseVersion, 'mariadb') !== false - && preg_match('/5\.5\.5-(\d+(?:\.\d+){0,2})-MariaDB/i', $databaseVersion, $matches) - ) { - return $matches[1]; - } - - if (preg_match('/\d+(?:\.\d+){0,2}/', $databaseVersion, $matches)) { - return $matches[0]; - } - - return $databaseVersion; + return Schema::getComparableVersion($databaseVersion); } private static function notifyWhenDatabaseVersionIsNotCompatibleWithNextMajorPiwik(): void diff --git a/plugins/CustomVariables b/plugins/CustomVariables index 535258a9692..f45ec8e82e0 160000 --- a/plugins/CustomVariables +++ b/plugins/CustomVariables @@ -1 +1 @@ -Subproject commit 535258a96928de18de099023acce5c186f43c4ff +Subproject commit f45ec8e82e0e3b724e61f2d06cb3e6ca4a5bfb8d diff --git a/plugins/Diagnostics/Diagnostic/DatabaseInformational.php b/plugins/Diagnostics/Diagnostic/DatabaseInformational.php index 1dd4eb6c522..c26f1e12b42 100644 --- a/plugins/Diagnostics/Diagnostic/DatabaseInformational.php +++ b/plugins/Diagnostics/Diagnostic/DatabaseInformational.php @@ -12,6 +12,7 @@ use Piwik\Common; use Piwik\Config; use Piwik\Db; +use Piwik\Db\Schema; use Piwik\SettingsPiwik; use Piwik\Translation\Translator; @@ -40,14 +41,16 @@ public function execute() $results[] = DiagnosticResult::informationalResult('DB Charset', $dbConfig['charset']); $results[] = DiagnosticResult::informationalResult('DB Collation', $dbConfig['collation']); $results[] = DiagnosticResult::informationalResult('DB Adapter', $dbConfig['adapter']); - $results[] = DiagnosticResult::informationalResult('MySQL Version', $this->getServerVersion()); + $serverVersion = (string) $this->getServerVersion(); + $databaseType = Schema::getServerTypeFromVersion($serverVersion); + $results[] = DiagnosticResult::informationalResult($databaseType . ' Version', $serverVersion); $results[] = DiagnosticResult::informationalResult('Num Tables', $this->getNumMatomoTables()); } return $results; } - private function getServerVErsion() + private function getServerVersion() { try { return Db::get()->getServerVersion(); diff --git a/plugins/TestRunner/templates/matomo-tests.yml.twig b/plugins/TestRunner/templates/matomo-tests.yml.twig index 861964bf3c1..200b31ca9e6 100644 --- a/plugins/TestRunner/templates/matomo-tests.yml.twig +++ b/plugins/TestRunner/templates/matomo-tests.yml.twig @@ -49,7 +49,7 @@ concurrency: jobs: {% if not plugin %} PHP: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -75,6 +75,8 @@ jobs: with: test-type: ${{ '{{' }} matrix.type {{ '}}' }} mysql-driver: ${{ '{{' }} matrix.adapter {{ '}}' }} + mysql-engine: 'Mysql' + mysql-version: '8.0' php-version: ${{ '{{' }} matrix.php {{ '}}' }} redis-service: true artifacts-pass: ${{ '{{' }} secrets.ARTIFACTS_PASS {{ '}}' }} @@ -84,12 +86,15 @@ jobs: {% endif %} {% elseif hasPluginTests %} PluginTests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: php: [ '{{ phpVersions|join("', '") }}' ] target: [{% if plugin != 'TagManager' %}'minimum_required_matomo', {% endif %}'maximum_supported_matomo'] + database: + - { engine: 'Mysql', version: '8.0' } + - { engine: 'Mariadb', version: '10.6' } steps: - uses: actions/checkout@v3 with: @@ -104,6 +109,8 @@ jobs: plugin-name: '{{ plugin }}' php-version: ${{ '{{' }} matrix.php {{ '}}' }} test-type: 'PluginTests' + mysql-engine: ${{ '{{' }} matrix.database.engine {{ '}}' }} + mysql-version: ${{ '{{' }} matrix.database.version {{ '}}' }} matomo-test-branch: ${{ '{{' }} matrix.target {{ '}}' }} {% if setupScript %} setup-script: '{{ setupScript }}' @@ -112,7 +119,7 @@ jobs: redis-service: true {% endif %} artifacts-pass: ${{ '{{' }} secrets.ARTIFACTS_PASS {{ '}}' }} - upload-artifacts: ${{ '{{' }} matrix.php == '{{ phpVersions|first }}' && matrix.target == 'maximum_supported_matomo' {{ '}}' }} + upload-artifacts: ${{ '{{' }} matrix.php == '{{ phpVersions|first }}' && matrix.target == 'maximum_supported_matomo' && matrix.database.engine == 'Mysql' {{ '}}' }} {% if protectArtifacts %} artifacts-protected: true {% endif %} @@ -124,7 +131,7 @@ jobs: {%- if not plugin or hasJavaScriptTests %} Javascript: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 timeout-minutes: 15 {% if plugin %} strategy: @@ -153,6 +160,8 @@ jobs: test-type: 'JS' php-version: '{{ phpVersions|first }}' node-version: '12' + mysql-engine: 'Mysql' + mysql-version: '8.0' {% if dependentPlugins %} dependent-plugins: '{{ dependentPlugins|join(',') }}' github-token: ${{ '{{' }} secrets.TESTS_ACCESS_TOKEN || secrets.GITHUB_TOKEN {{ '}}' }} @@ -161,7 +170,7 @@ jobs: {%- if not plugin or hasClientTests %} Client: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 timeout-minutes: 15 {% if plugin %} strategy: @@ -188,7 +197,7 @@ jobs: matomo-test-branch: ${{ '{{' }} matrix.target {{ '}}' }} {% endif %} test-type: 'Client' - node-version: '16' + node-version: '24' mysql-service: false {% if dependentPlugins %} dependent-plugins: '{{ dependentPlugins|join(',') }}' @@ -198,7 +207,7 @@ jobs: {%- if not plugin or hasUITests %} UI: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 {% if not plugin %} strategy: fail-fast: false @@ -232,6 +241,8 @@ jobs: test-type: 'UI' php-version: '{{ phpVersions|first }}' node-version: '16' + mysql-engine: 'Mysql' + mysql-version: '8.0' {% if enableRedis %} redis-service: true {% endif %} diff --git a/tests/PHPUnit/Fixtures/SqlDump.php b/tests/PHPUnit/Fixtures/SqlDump.php index 3861d18a0d0..7cae5e53d5f 100644 --- a/tests/PHPUnit/Fixtures/SqlDump.php +++ b/tests/PHPUnit/Fixtures/SqlDump.php @@ -12,7 +12,6 @@ use Piwik\Access; use Piwik\ArchiveProcessor\Rules; use Piwik\Config; -use Piwik\Config\DatabaseConfig; use Piwik\Db; use Piwik\Tests\Framework\Fixture; use Exception; @@ -70,11 +69,6 @@ public function setUp(): void $cmd = "mysql --defaults-extra-file=\"$defaultsFile\" -h \"$host\" {$this->dbName} < \"" . $deflatedDumpPath . "\" 2>&1"; - if (DatabaseConfig::isTiDb()) { - // For TiDb we need to remove the default charset from the create table statements, otherwise it will use the default charset collation, which differs from database default collation - $cmd = "sed 's/ DEFAULT CHARSET=utf8mb4//' \"$deflatedDumpPath\" | mysql --defaults-extra-file=\"$defaultsFile\" -h \"$host\" {$this->dbName} 2>&1"; - } - exec($cmd, $output, $return); if ($return !== 0) { throw new Exception("Failed to load sql dump: " . implode("\n", $output)); diff --git a/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php b/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php index 73418fe0953..a356ac04617 100644 --- a/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php +++ b/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php @@ -10,6 +10,7 @@ namespace Piwik\Tests\Core\DataTable\Filter; use Piwik\API\Proxy; +use Piwik\Container\StaticContainer; use Piwik\Plugin\Manager; use Piwik\Plugins\CustomVariables\CustomVariables; use Piwik\Tests\Framework\Fixture; @@ -53,6 +54,17 @@ public function setUp(): void $this->segmentTableCount = 0; } + public function tearDown(): void + { + parent::tearDown(); + + // provideContainerConfig() replaces the Proxy singleton with a mock; that mock can otherwise + // outlive this test's environment and be picked up by a later test in the same process that + // reads Proxy::getInstance() without rebuilding the container (e.g. DocumentationGeneratorTest). + // Restore a real Proxy so the override can't leak. + StaticContainer::getContainer()->set(Proxy::class, new Proxy()); + } + public function testConstructionShouldFailWhenReportHasNoSubtableAndSegmentFetchingIsDisabled() { $this->expectException(\Exception::class); diff --git a/tests/PHPUnit/Integration/Db/Schema/MariadbTest.php b/tests/PHPUnit/Integration/Db/Schema/MariadbTest.php index 8b672b6b0fa..fcb364521c4 100644 --- a/tests/PHPUnit/Integration/Db/Schema/MariadbTest.php +++ b/tests/PHPUnit/Integration/Db/Schema/MariadbTest.php @@ -32,7 +32,6 @@ public function getIsOptimizeInnoDBTestData() array("10.1.1-MariaDB-1~trusty", true), array("10.2.0-MariaDB-1~trusty", true), array("10.6.19-0ubuntu0.14.04.1", true), // we expect true, as the version is high enough - array("8.0.11-TiDB-v8.1.0", false), array("", false), array("0", false), array("slkdf(@*#lkesjfMariaDB", false), diff --git a/tests/PHPUnit/Integration/Db/Schema/MysqlTest.php b/tests/PHPUnit/Integration/Db/Schema/MysqlTest.php index ff48a9c7a67..4c23665d2de 100644 --- a/tests/PHPUnit/Integration/Db/Schema/MysqlTest.php +++ b/tests/PHPUnit/Integration/Db/Schema/MysqlTest.php @@ -32,7 +32,6 @@ public function getIsOptimizeInnoDBTestData() array("10.1.1-MariaDB-1~trusty", true), array("10.2.0-MariaDB-1~trusty", true), array("10.6.19-0ubuntu0.14.04.1", false), - array("8.0.11-TiDB-v8.1.0", false), array("", false), array("0", false), array("slkdf(@*#lkesjfMariaDB", false), diff --git a/tests/PHPUnit/Integration/Db/Schema/TidbTest.php b/tests/PHPUnit/Integration/Db/Schema/TidbTest.php deleted file mode 100644 index 166f4ab5097..00000000000 --- a/tests/PHPUnit/Integration/Db/Schema/TidbTest.php +++ /dev/null @@ -1,101 +0,0 @@ -assertFalse($schema->isOptimizeInnoDBSupported()); - } - - public function testOptimize() - { - if (!DatabaseConfig::isTiDb()) { - self::markTestSkipped('Tidb is not available'); - } - - // create two myisam tables - Db::exec("CREATE TABLE table1 (a INT) ENGINE=MYISAM"); - Db::exec("CREATE TABLE table2 (b INT) ENGINE=MYISAM"); - - // create two innodb tables - Db::exec("CREATE TABLE table3 (c INT) ENGINE=InnoDB"); - Db::exec("CREATE TABLE table4 (d INT) ENGINE=InnoDB"); - - $schema = Db\Schema::getInstance(); - - // optimizing not available for TiDb - $this->assertFalse($schema->optimizeTables(['table1', 'table2'])); - $this->assertFalse($schema->optimizeTables(['table1', 'table2', 'table3', 'table4'])); - $this->assertFalse($schema->optimizeTables(['table3', 'table4'])); - $this->assertFalse($schema->optimizeTables(['table3', 'table4'], true)); - } - - public function testGetDefaultCollationForCharsetReplacesUtf8mb4Binary(): void - { - if (DatabaseConfig::getConfigValue('schema') !== 'Tidb') { - self::markTestSkipped('Tidb is not available'); - } - - $schema = Db\Schema::getInstance(); - - self::assertSame( - 'utf8mb4_0900_ai_ci', - $schema->getDefaultCollationForCharset('utf8mb4') - ); - } - - /** - * @dataProvider getTableCreateOptionsTestData - */ - public function testTableCreateOptions(array $optionOverrides, string $expected): void - { - if (DatabaseConfig::getConfigValue('schema') !== 'Tidb') { - self::markTestSkipped('Tidb is not available'); - } - - foreach ($optionOverrides as $name => $value) { - DatabaseConfig::setConfigValue($name, $value); - } - - $schema = Db\Schema::getInstance(); - - self::assertSame($expected, $schema->getTableCreateOptions()); - } - - public function getTableCreateOptionsTestData(): iterable - { - yield 'default charset, empty collation' => [ - ['collation' => ''], - 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC', - ]; - - yield 'override charset, empty collation' => [ - ['charset' => 'utf8mb3', 'collation' => ''], - 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3', - ]; - - yield 'default charset, override collation' => [ - ['collation' => 'utf8mb4_swedish_ci'], - 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_swedish_ci ROW_FORMAT=DYNAMIC', - ]; - - yield 'override charset and collation' => [ - ['charset' => 'utf8mb3', 'collation' => 'utf8mb3_general_ci'], - 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci', - ]; - } -} diff --git a/tests/PHPUnit/Integration/Updates/Updates590b2Test.php b/tests/PHPUnit/Integration/Updates/Updates590b2Test.php index 051cab67077..79da5ba8583 100644 --- a/tests/PHPUnit/Integration/Updates/Updates590b2Test.php +++ b/tests/PHPUnit/Integration/Updates/Updates590b2Test.php @@ -82,8 +82,8 @@ public function getMigrationTestData(): iterable ]; yield 'unsupported database type' => [ - 'TiDB', - '8.0.11-TiDB-v8.1.0', + 'PostgreSQL', + '15.2', [ './console plugin:activate "BotTracking"', ], diff --git a/tests/PHPUnit/Unit/Db/Adapter/MysqliTest.php b/tests/PHPUnit/Unit/Db/Adapter/MysqliTest.php index 018624f1773..d6ed0d80eae 100644 --- a/tests/PHPUnit/Unit/Db/Adapter/MysqliTest.php +++ b/tests/PHPUnit/Unit/Db/Adapter/MysqliTest.php @@ -12,53 +12,49 @@ use Exception; use PHPUnit\Framework\TestCase; use Piwik\Db\Adapter\Mysqli; -use Piwik\Db\Schema; class MysqliTest extends TestCase { - protected function tearDown(): void + public function testCheckServerVersionThrowsWhenMysqlVersionTooLow(): void { - Schema::unsetInstance(); - parent::tearDown(); + $adapter = $this->createMockAdapter('5.7.44'); + + $this->expectException(Exception::class); + $adapter->checkServerVersion(); + } + + public function testCheckServerVersionAllowsSupportedMysqlVersion(): void + { + $adapter = $this->createMockAdapter('8.0.32'); + + $adapter->checkServerVersion(); + $this->addToAssertionCount(1); } - public function testCheckServerVersionThrowsWhenServerVersionTooLow(): void + public function testCheckServerVersionThrowsWhenMariaDbVersionTooLow(): void { - Schema::setSingletonInstance($this->createMockSchema('8.0.0')); - $adapter = $this->createMockAdapter('5.7.0'); + // 10.5.22 clears the MySQL minimum (8.0) but is below the MariaDB minimum (10.6); + // this can only throw if the server is correctly detected as MariaDB. + $adapter = $this->createMockAdapter('5.5.5-10.5.22-MariaDB-1:10.5.22+maria~ubu2004'); $this->expectException(Exception::class); $adapter->checkServerVersion(); } - public function testCheckServerVersionAllowsSupportedVersion(): void + public function testCheckServerVersionAllowsSupportedMariaDbVersion(): void { - Schema::setSingletonInstance($this->createMockSchema('5.7.0')); - $adapter = $this->createMockAdapter('8.0.32'); + $adapter = $this->createMockAdapter('10.6.19-MariaDB-1:10.6.19+maria~ubu2004'); $adapter->checkServerVersion(); $this->addToAssertionCount(1); } - /** - * This will 'mock' the Schema class to return a specific minimum version. - */ - private function createMockSchema(string $minimumVersion): Schema + public function testCheckServerVersionAllowsSupportedMariaDbVersionWithLegacyPrefix(): void { - return new class ($minimumVersion) extends Schema { - private $minimumVersion; - - public function __construct(string $minimumVersion) - { - parent::__construct(); - $this->minimumVersion = $minimumVersion; - } - - public function getMinimumSupportedVersion(): string - { - return $this->minimumVersion; - } - }; + $adapter = $this->createMockAdapter('5.5.5-10.6.19-MariaDB-1:10.6.19+maria~ubu2004'); + + $adapter->checkServerVersion(); + $this->addToAssertionCount(1); } private function createMockAdapter(string $serverVersion): Mysqli diff --git a/tests/PHPUnit/Unit/Db/Adapter/Pdo/MysqlTest.php b/tests/PHPUnit/Unit/Db/Adapter/Pdo/MysqlTest.php index 8a2234ccfdf..e9a84929ad0 100644 --- a/tests/PHPUnit/Unit/Db/Adapter/Pdo/MysqlTest.php +++ b/tests/PHPUnit/Unit/Db/Adapter/Pdo/MysqlTest.php @@ -12,53 +12,49 @@ use Exception; use PHPUnit\Framework\TestCase; use Piwik\Db\Adapter\Pdo\Mysql; -use Piwik\Db\Schema; class MysqlTest extends TestCase { - protected function tearDown(): void + public function testCheckServerVersionThrowsWhenMysqlVersionTooLow(): void { - Schema::unsetInstance(); - parent::tearDown(); + $adapter = $this->createMockAdapter('5.7.44'); + + $this->expectException(Exception::class); + $adapter->checkServerVersion(); + } + + public function testCheckServerVersionAllowsSupportedMysqlVersion(): void + { + $adapter = $this->createMockAdapter('8.0.32'); + + $adapter->checkServerVersion(); + $this->addToAssertionCount(1); } - public function testCheckServerVersionThrowsWhenServerVersionTooLow(): void + public function testCheckServerVersionThrowsWhenMariaDbVersionTooLow(): void { - Schema::setSingletonInstance($this->createMockSchema('8.0.0')); - $adapter = $this->createMockAdapter('5.7.0'); + // 10.5.22 clears the MySQL minimum (8.0) but is below the MariaDB minimum (10.6); + // this can only throw if the server is correctly detected as MariaDB. + $adapter = $this->createMockAdapter('5.5.5-10.5.22-MariaDB-1:10.5.22+maria~ubu2004'); $this->expectException(Exception::class); $adapter->checkServerVersion(); } - public function testCheckServerVersionAllowsSupportedVersion(): void + public function testCheckServerVersionAllowsSupportedMariaDbVersion(): void { - Schema::setSingletonInstance($this->createMockSchema('5.7.0')); - $adapter = $this->createMockAdapter('8.0.32'); + $adapter = $this->createMockAdapter('10.6.19-MariaDB-1:10.6.19+maria~ubu2004'); $adapter->checkServerVersion(); $this->addToAssertionCount(1); } - /** - * This will 'mock' the Schema class to return a specific minimum version. - */ - private function createMockSchema(string $minimumVersion): Schema + public function testCheckServerVersionAllowsSupportedMariaDbVersionWithLegacyPrefix(): void { - return new class ($minimumVersion) extends Schema { - private $minimumVersion; - - public function __construct(string $minimumVersion) - { - parent::__construct(); - $this->minimumVersion = $minimumVersion; - } - - public function getMinimumSupportedVersion(): string - { - return $this->minimumVersion; - } - }; + $adapter = $this->createMockAdapter('5.5.5-10.6.19-MariaDB-1:10.6.19+maria~ubu2004'); + + $adapter->checkServerVersion(); + $this->addToAssertionCount(1); } private function createMockAdapter(string $serverVersion): Mysql diff --git a/tests/PHPUnit/Unit/Db/SchemaTest.php b/tests/PHPUnit/Unit/Db/SchemaTest.php new file mode 100644 index 00000000000..57bf1139558 --- /dev/null +++ b/tests/PHPUnit/Unit/Db/SchemaTest.php @@ -0,0 +1,66 @@ +assertSame($expected, Schema::getComparableVersion($rawVersion)); + } + + public function getComparableVersions(): iterable + { + yield 'plain mysql' => ['8.0.32', '8.0.32']; + yield 'mysql with distro suffix' => ['8.0.36-0ubuntu0.22.04.1', '8.0.36']; + yield 'percona' => ['8.0.36-28', '8.0.36']; + yield 'mariadb modern string' => ['10.6.19-MariaDB-1:10.6.19+maria~ubu2004', '10.6.19']; + yield 'mariadb legacy compatibility prefix' => ['5.5.5-10.6.19-MariaDB-1:10.6.19', '10.6.19']; + yield 'no digits returns input unchanged' => ['unknown', 'unknown']; + yield 'empty string returns input unchanged' => ['', '']; + } + + /** + * @dataProvider getServerTypes + */ + public function testGetServerTypeFromVersion(string $rawVersion, string $expected): void + { + $this->assertSame($expected, Schema::getServerTypeFromVersion($rawVersion)); + } + + public function getServerTypes(): iterable + { + yield 'mysql' => ['8.0.32', 'MySQL']; + yield 'mariadb' => ['10.6.19-MariaDB', 'MariaDB']; + yield 'mariadb legacy compatibility prefix' => ['5.5.5-10.6.19-MariaDB', 'MariaDB']; + yield 'unrecognised string defaults to mysql' => ['', 'MySQL']; + } + + /** + * @dataProvider getServerMinimums + */ + public function testGetMinimumSupportedVersionForServer(string $rawVersion, string $expected): void + { + $this->assertSame($expected, Schema::getMinimumSupportedVersionForServer($rawVersion)); + } + + public function getServerMinimums(): iterable + { + yield 'mysql uses the mysql minimum' => ['8.0.32', '8.0']; + yield 'mariadb uses the mariadb minimum' => ['10.6.19-MariaDB', '10.6']; + yield 'mariadb with legacy prefix uses the mariadb minimum' => ['5.5.5-10.6.19-MariaDB', '10.6']; + } +} diff --git a/tests/PHPUnit/Unit/Plugin/ControllerAdminTest.php b/tests/PHPUnit/Unit/Plugin/ControllerAdminTest.php index 991d4176fa5..9f32ffb441e 100644 --- a/tests/PHPUnit/Unit/Plugin/ControllerAdminTest.php +++ b/tests/PHPUnit/Unit/Plugin/ControllerAdminTest.php @@ -63,7 +63,7 @@ public function getDatabaseVersions(): iterable yield 'MariaDB above Matomo 6 requirement' => ['MariaDB', '11.4.0-MariaDB', true]; yield 'MariaDB detected through MySQL schema version' => ['MySQL', '10.5.22-MariaDB', false]; yield 'MariaDB detected with compatibility prefix' => ['MySQL', '5.5.5-10.6.12-MariaDB', true]; - yield 'Unknown database type is treated as compatible' => ['Tidb', '7.5.0', true]; + yield 'Unknown database type is treated as compatible' => ['PostgreSQL', '15.2', true]; } /** @@ -88,7 +88,7 @@ public function getDatabaseRequirements(): iterable yield 'MySQL' => ['MySQL', '5.7.44', '8.0']; yield 'MariaDB' => ['MariaDB', '10.5.22-MariaDB', '10.6']; yield 'MariaDB detected through version' => ['MySQL', '10.5.22-MariaDB', '10.6']; - yield 'Unknown database type' => ['Tidb', '7.5.0', null]; + yield 'Unknown database type' => ['PostgreSQL', '15.2', null]; } private function invokeControllerAdminMethod(string $methodName, array $arguments) diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png b/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png index 003eefa7860..924ffb40d81 100644 --- a/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png +++ b/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eac0341643addaa32415beed4a610a00ddcf8b7440a9ec1c6edb001f355c0ce7 -size 5348595 +oid sha256:3b04cd57e4d270b9b64349578cdb444701e6064fdfa158852bdec34376c479cf +size 5348001 diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_email_reports.png b/tests/UI/expected-screenshots/UIIntegrationTest_email_reports.png index e25e17f2cab..896dd5fdc57 100644 --- a/tests/UI/expected-screenshots/UIIntegrationTest_email_reports.png +++ b/tests/UI/expected-screenshots/UIIntegrationTest_email_reports.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3374198df293d3c2f1b6e57b05cfdac9d783c95696231f6f0412be2abfabb6c0 -size 284331 +oid sha256:94451ac47a816444592d95ccefa15537a2c80c14f87c6583afd4d5da4555d10e +size 284306