Skip to content

Commit efe0225

Browse files
committed
MariaDb1043. Detect the need to migrate JSON columns to native JSON.
MariaDb JSON columns are marked as json with a DC2Type comment hint. The schema manager maps such columns to a json doctrine type which matches the metadata whether the column was declared as LONGTEXT (by the previous MariaDb1027 platform) or JSON. This means the comparator will not detect that a column should be upgraded from LONGTEXT to native JSON and no migration would be generated. Therefore, during column introspection check that the data type of the introspected columns match that which should be set for any doctrine types inferred from DC2Type comments. Set a flag (a platformOption for the relevant column) where it does not. Check whether the flag is set when the comparator diffs the expected and actual tables and mark a column type difference where the flag is set so a migration is generated.
1 parent 2d95423 commit efe0225

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

src/Platforms/AbstractPlatform.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4626,6 +4626,10 @@ public function columnsEqual(Column $column1, Column $column2): bool
46264626
return false;
46274627
}
46284628

4629+
if (! $this->columnDeclarationsMatch($column1, $column2)) {
4630+
return false;
4631+
}
4632+
46294633
// If the platform supports inline comments, all comparison is already done above
46304634
if ($this->supportsInlineColumnComments()) {
46314635
return true;
@@ -4638,6 +4642,17 @@ public function columnsEqual(Column $column1, Column $column2): bool
46384642
return $column1->getType() === $column2->getType();
46394643
}
46404644

4645+
/**
4646+
* Whether the database data type matches that expected for the doctrine type for the given colunms.
4647+
*/
4648+
private function columnDeclarationsMatch(Column $column1, Column $column2): bool
4649+
{
4650+
return ! (
4651+
$column1->hasPlatformOption('declarationMismatch') ||
4652+
$column2->hasPlatformOption('declarationMismatch')
4653+
);
4654+
}
4655+
46414656
/**
46424657
* Creates the schema manager that can be used to inspect and change the underlying
46434658
* database schema according to the dialect of the platform.

src/Schema/MySQLSchemaManager.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,20 @@ protected function _getPortableTableColumnDefinition($tableColumn)
183183
$scale = null;
184184
$precision = null;
185185

186-
$type = $this->_platform->getDoctrineTypeMapping($dbType);
186+
$type = $origType = $this->_platform->getDoctrineTypeMapping($dbType);
187187

188188
// In cases where not connected to a database DESCRIBE $table does not return 'Comment'
189189
if (isset($tableColumn['comment'])) {
190190
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
191191
$tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
192192
}
193193

194+
// Check underlying database type where doctrine type is inferred from DC2Type comment
195+
// and set a flag if it is not as expected.
196+
if ($origType !== $type && $this->expectedDbType($type, $tableColumn) !== $dbType) {
197+
$tableColumn['declarationMismatch'] = true;
198+
}
199+
194200
switch ($dbType) {
195201
case 'char':
196202
case 'binary':
@@ -286,9 +292,35 @@ protected function _getPortableTableColumnDefinition($tableColumn)
286292
$column->setPlatformOption('collation', $tableColumn['collation']);
287293
}
288294

295+
if (isset($tableColumn['declarationMismatch'])) {
296+
$column->setPlatformOption('declarationMismatch', $tableColumn['declarationMismatch']);
297+
}
298+
289299
return $column;
290300
}
291301

302+
/**
303+
* Returns the database data type for a given doctrine type and column
304+
*
305+
* Note that for data types that depend on length where length is not part of the column definition
306+
* and therefore the $tableColumn['length'] will not be set, for example TEXT (which could be LONGTEXT,
307+
* MEDIUMTEXT) or BLOB (LONGBLOB or TINYBLOB), the expectedDbType cannot be inferred exactly, merely
308+
* the default type.
309+
*
310+
* This method is intended to be used to determine underlying database type where doctrine type is
311+
* inferred from a DC2Type comment.
312+
*
313+
* @param mixed[] $tableColumn
314+
*/
315+
private function expectedDbType(string $type, array $tableColumn): string
316+
{
317+
$_type = Type::getType($type);
318+
$expectedDbType = strtolower($_type->getSQLDeclaration($tableColumn, $this->_platform));
319+
$expectedDbType = strtok($expectedDbType, '(), ');
320+
321+
return $expectedDbType === false ? '' : $expectedDbType;
322+
}
323+
292324
/**
293325
* Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers.
294326
*

tests/Functional/Schema/MySQL/ComparatorTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Doctrine\DBAL\Exception;
66
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
77
use Doctrine\DBAL\Platforms\AbstractPlatform;
8+
use Doctrine\DBAL\Platforms\MariaDb1043Platform;
89
use Doctrine\DBAL\Schema\AbstractSchemaManager;
910
use Doctrine\DBAL\Schema\Column;
1011
use Doctrine\DBAL\Schema\Comparator;
@@ -147,6 +148,24 @@ public function testImplicitColumnCharset(): void
147148
));
148149
}
149150

151+
public function testMariaDb1043NativeJsonUpgradeDetected(): void
152+
{
153+
if (! $this->platform instanceof MariaDb1043Platform) {
154+
self::markTestSkipped();
155+
}
156+
157+
$table = new Table('mariadb_json_upgrade');
158+
159+
$table->addColumn('json_col', 'json');
160+
$this->dropAndCreateTable($table);
161+
162+
// Revert column to old LONGTEXT declaration
163+
$sql = 'ALTER TABLE mariadb_json_upgrade CHANGE json_col json_col LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\'';
164+
$this->connection->executeStatement($sql);
165+
166+
ComparatorTestUtils::assertDiffNotEmpty($this->connection, $this->comparator, $table);
167+
}
168+
150169
/**
151170
* @return array{Table,Column}
152171
*

0 commit comments

Comments
 (0)