Skip to content

Commit

Permalink
Pass configured database collation to table creation statements
Browse files Browse the repository at this point in the history
  • Loading branch information
mneudert committed Sep 10, 2024
1 parent 1d7429a commit 1907ff7
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 4 deletions.
5 changes: 3 additions & 2 deletions config/global.ini.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@
; Matomo should work correctly without this setting but we recommend to have a charset set.
charset = utf8

; In some database setups the collation for the connection changes after a "SET NAMES" statement
; is issued, unless a "COLLATE" argument is added.
; In some database setups the collation used for queries and creating tables can have unexpected
; values, or change after a database version upgrade.
; If you encounter "Illegal mix of collation" errors, setting this config to the value matching
; your existing database tables can help.
; This setting will only be used if "charset" is also set.
; Matomo should work correctly without this setting but we recommend to have a collation set.
collation =

; Database error codes to ignore during updates
Expand Down
10 changes: 10 additions & 0 deletions core/Db/Schema/Mysql.php
Original file line number Diff line number Diff line change
Expand Up @@ -678,10 +678,15 @@ public function getTableCreateOptions(): string
{
$engine = $this->getTableEngine();
$charset = $this->getUsedCharset();
$collation = $this->getUsedCollation();
$rowFormat = $this->getTableRowFormat();

$options = "ENGINE=$engine DEFAULT CHARSET=$charset";

if ('' !== $collation) {
$options .= " COLLATE=$collation";
}

if ('' !== $rowFormat) {
$options .= " $rowFormat";
}
Expand Down Expand Up @@ -784,6 +789,11 @@ protected function getUsedCharset(): string
return $this->getDbSettings()->getUsedCharset();
}

protected function getUsedCollation(): string
{
return $this->getDbSettings()->getUsedCollation();
}

private function getTablePrefix()
{
return $this->getDbSettings()->getTablePrefix();
Expand Down
9 changes: 7 additions & 2 deletions core/Db/Schema/Tidb.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@ public function getTableCreateOptions(): string
{
$engine = $this->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 ('utf8mb4' === $charset) {
$options .= ' COLLATE=utf8mb4_0900_ai_ci';
if ('' !== $collation) {
$options .= " COLLATE=$collation";
}

if ('' !== $rowFormat) {
Expand Down
5 changes: 5 additions & 0 deletions core/Db/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public function getUsedCharset()
return strtolower($this->getDbSetting('charset'));
}

public function getUsedCollation()
{
return strtolower($this->getDbSetting('collation') ?? '');
}

public function getRowFormat()
{
return $this->getUsedCharset() === 'utf8mb4' ? 'ROW_FORMAT=DYNAMIC' : '';
Expand Down
41 changes: 41 additions & 0 deletions tests/PHPUnit/Integration/Db/Schema/MariadbTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,45 @@ public function testOptimize()
// should optimize when forced
$this->assertTrue($schema->optimizeTables(['table3', 'table4'], true), true);
}

/**
* @dataProvider getTableCreateOptionsTestData
*/
public function testTableCreateOptions(array $optionOverrides, string $expected): void
{
if (DatabaseConfig::getConfigValue('schema') !== 'Mariadb') {
self::markTestSkipped('Mariadb 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 'defaults' => [
[],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC'
];

yield 'override charset' => [
['charset' => 'utf8mb3'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3'
];

yield 'override collation' => [
['collation' => 'utf8mb4_general_ci'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC'
];

yield 'override charset and collation' => [
['charset' => 'utf8mb3', 'collation' => 'utf8mb3_general_ci'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci'
];
}
}
41 changes: 41 additions & 0 deletions tests/PHPUnit/Integration/Db/Schema/MysqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,45 @@ public function testOptimize()
// should optimize when forced
$this->assertTrue($schema->optimizeTables(['table3', 'table4'], true));
}

/**
* @dataProvider getTableCreateOptionsTestData
*/
public function testTableCreateOptions(array $optionOverrides, string $expected): void
{
if (DatabaseConfig::getConfigValue('schema') !== 'Mysql') {
self::markTestSkipped('Mysql 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 'defaults' => [
[],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC'
];

yield 'override charset' => [
['charset' => 'utf8mb3'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3'
];

yield 'override collation' => [
['collation' => 'utf8mb4_general_ci'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC'
];

yield 'override charset and collation' => [
['charset' => 'utf8mb3', 'collation' => 'utf8mb3_general_ci'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci'
];
}
}
41 changes: 41 additions & 0 deletions tests/PHPUnit/Integration/Db/Schema/TidbTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,45 @@ public function testOptimize()
$this->assertFalse($schema->optimizeTables(['table3', 'table4']));
$this->assertFalse($schema->optimizeTables(['table3', 'table4'], true));
}

/**
* @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 'defaults' => [
[],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC'
];

yield 'override charset' => [
['charset' => 'utf8mb3'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb4_0900_ai_ci'
];

yield 'override collation' => [
['collation' => 'utf8mb4_general_ci'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC'
];

yield 'override charset and collation' => [
['charset' => 'utf8mb3', 'collation' => 'utf8mb3_general_ci'],
'ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci'
];
}
}

0 comments on commit 1907ff7

Please sign in to comment.