From 1f1aad9e50a3705a4254439189d0dbd9b5a5a91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ausw=C3=B6ger?= Date: Sun, 5 Nov 2023 20:21:38 +0100 Subject: [PATCH] Fix MariaDB list columns performance (#6202) | Q | A |------------- | ----------- | Type | bug | Fixed issues | https://github.com/contao/contao/issues/6409 #### Summary Starting with version 3.7.0 we noticed that the `listTableColumns()` method got very slow on some systems, our migrate command went from taking around 2 seconds with version 3.6 to 260 seconds with version3.7. This is due to the `LEFT JOIN information_schema.CHECK_CONSTRAINTS` not getting optimized by MariaDB which causes the database server to scan every table of every database. This pull request fixes that by using a subquery that is limited by `WHERE CONSTRAINT_SCHEMA = $databaseName`. This brings the performance back to where it was with version 3.6. --- src/Platforms/AbstractMySQLPlatform.php | 12 +++++- src/Platforms/MariaDb1043Platform.php | 39 ++++++++++--------- src/Schema/MySQLSchemaManager.php | 3 +- .../Schema/MySQLSchemaManagerTest.php | 6 +++ 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/Platforms/AbstractMySQLPlatform.php b/src/Platforms/AbstractMySQLPlatform.php index d1e3e5aff52..284194d9dc7 100644 --- a/src/Platforms/AbstractMySQLPlatform.php +++ b/src/Platforms/AbstractMySQLPlatform.php @@ -406,7 +406,17 @@ public function getListTableColumnsSQL($table, $database = null) */ public function getColumnTypeSQLSnippets(string $tableAlias = 'c'): array { - return [$tableAlias . '.COLUMN_TYPE', '']; + return [$this->getColumnTypeSQLSnippet(...func_get_args()), '']; + } + + /** + * The SQL snippet required to elucidate a column type + * + * Returns a column type SELECT snippet string + */ + public function getColumnTypeSQLSnippet(string $tableAlias = 'c', ?string $databaseName = null): string + { + return $tableAlias . '.COLUMN_TYPE'; } /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ diff --git a/src/Platforms/MariaDb1043Platform.php b/src/Platforms/MariaDb1043Platform.php index 926c6b9529d..8ba6792ed01 100644 --- a/src/Platforms/MariaDb1043Platform.php +++ b/src/Platforms/MariaDb1043Platform.php @@ -40,7 +40,8 @@ public function getJsonTypeDeclarationSQL(array $column): string */ public function getListTableColumnsSQL($table, $database = null): string { - [$columnTypeSQL, $joinCheckConstraintSQL] = $this->getColumnTypeSQLSnippets(); + // @todo 4.0 - call getColumnTypeSQLSnippet() instead + [$columnTypeSQL, $joinCheckConstraintSQL] = $this->getColumnTypeSQLSnippets('c', $database); return sprintf( <<getJsonTypeDeclarationSQL([]) !== 'JSON') { - return parent::getColumnTypeSQLSnippets($tableAlias); + return parent::getColumnTypeSQLSnippet($tableAlias, $databaseName); } - $columnTypeSQL = <<getDatabaseNameSQL($databaseName); + + // The check for `CONSTRAINT_SCHEMA = $databaseName` is mandatory here to prevent performance issues + return <<_platform->getColumnTypeSQLSnippets(); + // @todo 4.0 - call getColumnTypeSQLSnippet() instead + [$columnTypeSQL, $joinCheckConstraintSQL] = $this->_platform->getColumnTypeSQLSnippets('c', $databaseName); $sql = 'SELECT'; diff --git a/tests/Functional/Schema/MySQLSchemaManagerTest.php b/tests/Functional/Schema/MySQLSchemaManagerTest.php index 3e90d6aabed..519360928bd 100644 --- a/tests/Functional/Schema/MySQLSchemaManagerTest.php +++ b/tests/Functional/Schema/MySQLSchemaManagerTest.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MariaDb1027Platform; +use Doctrine\DBAL\Platforms\MariaDb1043Platform; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Tests\Functional\Schema\MySQL\PointType; @@ -401,6 +402,11 @@ public function testJsonColumnType(): void $table->addColumn('col_json', Types::JSON); $this->dropAndCreateTable($table); + // Remove the comment from the column to ensure the type is detected correctly from the check constraints. + if ($this->connection->getDatabasePlatform() instanceof MariaDb1043Platform) { + $this->connection->executeStatement('ALTER TABLE test_mysql_json CHANGE COLUMN col_json col_json JSON'); + } + $columns = $this->schemaManager->listTableColumns('test_mysql_json'); self::assertSame(Types::JSON, $columns['col_json']->getType()->getName());