From 726b3af6bf245de6b3a72a3673062577c45fc4ad Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 1 Mar 2026 14:07:36 +0100 Subject: [PATCH] Fix TEXT column variants not round-tripping correctly When using migration_diff, TEXT column variants (TINYTEXT, MEDIUMTEXT, LONGTEXT) were not being properly mapped back from the database. The BLOB type handling already used rawType to distinguish BLOB variants, but TEXT variants were missing equivalent handling. This adds similar rawType-based mapping for TEXT columns in mapColumnType() and includes round-trip tests. Fixes #1029 --- src/Db/Adapter/MysqlAdapter.php | 14 +++++++++ .../TestCase/Db/Adapter/MysqlAdapterTest.php | 31 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/Db/Adapter/MysqlAdapter.php b/src/Db/Adapter/MysqlAdapter.php index b1d8a182..c07cfe6f 100644 --- a/src/Db/Adapter/MysqlAdapter.php +++ b/src/Db/Adapter/MysqlAdapter.php @@ -594,6 +594,20 @@ protected function mapColumnType(array $columnData): array } } // else: keep as binary or varbinary (actual BINARY/VARBINARY column) + } elseif ($type === TableSchema::TYPE_TEXT) { + // CakePHP returns TEXT columns as 'text' with specific lengths + // Check the raw MySQL type to distinguish TEXT variants + $rawType = $columnData['rawType'] ?? ''; + if (str_contains($rawType, 'tinytext')) { + $length = static::TEXT_TINY; + } elseif (str_contains($rawType, 'mediumtext')) { + $length = static::TEXT_MEDIUM; + } elseif (str_contains($rawType, 'longtext')) { + $length = static::TEXT_LONG; + } else { + // Regular TEXT - use null to indicate default TEXT type + $length = null; + } } return [$type, $length]; diff --git a/tests/TestCase/Db/Adapter/MysqlAdapterTest.php b/tests/TestCase/Db/Adapter/MysqlAdapterTest.php index 9b0e0510..85aded71 100644 --- a/tests/TestCase/Db/Adapter/MysqlAdapterTest.php +++ b/tests/TestCase/Db/Adapter/MysqlAdapterTest.php @@ -1369,6 +1369,37 @@ public function testBlobRoundTrip(string $type, ?int $limit, string $expectedTyp $this->adapter->dropTable('blob_round_trip_test'); } + public static function textRoundTripData() + { + return [ + // type, limit, expected type after round-trip, expected limit after round-trip + ['text', null, 'text', null], + ['text', MysqlAdapter::TEXT_TINY, 'text', MysqlAdapter::TEXT_TINY], + ['text', MysqlAdapter::TEXT_MEDIUM, 'text', MysqlAdapter::TEXT_MEDIUM], + ['text', MysqlAdapter::TEXT_LONG, 'text', MysqlAdapter::TEXT_LONG], + ]; + } + + #[DataProvider('textRoundTripData')] + public function testTextRoundTrip(string $type, ?int $limit, string $expectedType, ?int $expectedLimit) + { + // Create a table with a TEXT column + $table = new Table('text_round_trip_test', [], $this->adapter); + $table->addColumn('text_col', $type, ['limit' => $limit]) + ->save(); + + // Read the column back from the database + $columns = $this->adapter->getColumns('text_round_trip_test'); + + $textColumn = $columns[1]; + $this->assertNotNull($textColumn, 'TEXT column not found'); + $this->assertSame($expectedType, $textColumn->getType(), 'Type mismatch after round-trip'); + $this->assertSame($expectedLimit, $textColumn->getLimit(), 'Limit mismatch after round-trip'); + + // Clean up + $this->adapter->dropTable('text_round_trip_test'); + } + public function testTimestampInvalidLimit() { $this->adapter->connect();