diff --git a/WebFiori/Database/Factory/ColumnFactory.php b/WebFiori/Database/Factory/ColumnFactory.php index fd735bf..00e7a39 100644 --- a/WebFiori/Database/Factory/ColumnFactory.php +++ b/WebFiori/Database/Factory/ColumnFactory.php @@ -15,6 +15,7 @@ use WebFiori\Database\Column; use WebFiori\Database\ConnectionInfo; use WebFiori\Database\DatabaseException; +use WebFiori\Database\DataType; use WebFiori\Database\MsSql\MSSQLColumn; use WebFiori\Database\MySql\MySQLColumn; use WebFiori\Database\Util\TypesMap; @@ -72,9 +73,20 @@ public static function create(string $database, string $name, array $options = [ $datatype = 'mixed'; } - $col->setDatatype($datatype); - $size = isset($options['size']) ? intval($options['size']) : 1; - $col->setSize($size); + $resolved = self::resolveDatatype($database, $datatype); + $col->setDatatype($resolved); + + $explicitSize = isset($options['size']) ? intval($options['size']) : null; + + if ($explicitSize !== null) { + $col->setSize($explicitSize); + } else if ($resolved !== $datatype) { + // Type was auto-mapped; use a large default for string/binary types + // to preserve the intent of the original unsized type. + $col->setSize(self::getDefaultMappedSize($resolved)); + } else { + $col->setSize(1); + } self::primaryCheck($col, $options); self::columnAttributesCheck($col, $options); @@ -124,6 +136,57 @@ public static function map(string $to, Column $column) : Column { return self::create($to, $column->getName(), $optionsArr); } + /** + * Resolves a datatype for the target database engine. + * + * If the type is natively supported by the target engine, it is returned + * as-is. Otherwise, the method searches all registered engine mappings in + * {@see TypesMap::MAP} to find an equivalent type for the target engine. + * + * @param string $database Target database engine (e.g. 'mysql', 'mssql'). + * @param string $datatype The requested datatype. + * + * @return string The resolved datatype for the target engine. + */ + private static function resolveDatatype(string $database, string $datatype): string { + $normalized = strtolower(trim($datatype)); + + if (in_array($normalized, DataType::getSupportedDataTypes($database))) { + return $normalized; + } + + foreach (array_keys(TypesMap::MAP) as $sourceEngine) { + if ($sourceEngine === $database) { + continue; + } + + $mapped = TypesMap::getType($sourceEngine, $database, $normalized); + + if ($mapped !== '') { + return $mapped; + } + } + + return $normalized; + } + /** + * Returns a sensible default size for a type that was auto-mapped from + * an unsized type (e.g. TEXT → nvarchar). + * + * @param string $resolvedType The mapped type. + * + * @return int Default size. + */ + private static function getDefaultMappedSize(string $resolvedType): int { + return match ($resolvedType) { + 'nvarchar' => 4000, + 'varchar' => 8000, + 'nchar', 'char' => 255, + 'binary', 'varbinary' => 8000, + 'text', 'mediumtext', 'blob', 'mediumblob', 'longblob', 'tinyblob' => 1, + default => 1 + }; + } /** * * @param MSSQLColumn $col diff --git a/tests/WebFiori/Tests/Database/MsSql/MSSQLColumnTest.php b/tests/WebFiori/Tests/Database/MsSql/MSSQLColumnTest.php index f7a0641..9f44b55 100644 --- a/tests/WebFiori/Tests/Database/MsSql/MSSQLColumnTest.php +++ b/tests/WebFiori/Tests/Database/MsSql/MSSQLColumnTest.php @@ -2,6 +2,7 @@ namespace WebFiori\Tests\Database\MsSql; use PHPUnit\Framework\TestCase; +use WebFiori\Database\DataType; use WebFiori\Database\Factory\ColumnFactory; use WebFiori\Database\MsSql\MSSQLColumn; use WebFiori\Database\MySql\MySQLColumn; @@ -581,4 +582,96 @@ public function testIdentity02() { $col->setIsIdentity(true); $this->assertEquals('[iden] [bigint] identity(1,1) not null',$col.''); } + /** + * @test + * Test that MySQL-only types are auto-mapped when creating MSSQL columns. + */ + public function testAutoMapMySQLTextToMSSQL() { + $col = ColumnFactory::create('mssql', 'content', ['type' => DataType::TEXT]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('nvarchar', $col->getDatatype()); + $this->assertEquals(4000, $col->getSize()); + } + /** + * @test + */ + public function testAutoMapMySQLMediumTextToMSSQL() { + $col = ColumnFactory::create('mssql', 'body', ['type' => DataType::TEXT_MEDIUM]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('nvarchar', $col->getDatatype()); + $this->assertEquals(4000, $col->getSize()); + } + /** + * @test + */ + public function testAutoMapMySQLBlobToMSSQL() { + $col = ColumnFactory::create('mssql', 'data', ['type' => DataType::BLOB]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('binary', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMySQLLongBlobToMSSQL() { + $col = ColumnFactory::create('mssql', 'data', ['type' => DataType::BLOB_LONG]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('binary', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMySQLMediumBlobToMSSQL() { + $col = ColumnFactory::create('mssql', 'data', ['type' => DataType::BLOB_MEDIUM]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('binary', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMySQLTinyBlobToMSSQL() { + $col = ColumnFactory::create('mssql', 'data', ['type' => DataType::BLOB_TINY]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('binary', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMySQLDoubleToMSSQL() { + $col = ColumnFactory::create('mssql', 'price', ['type' => DataType::DOUBLE]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('float', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMySQLTimestampToMSSQL() { + $col = ColumnFactory::create('mssql', 'ts', ['type' => DataType::TIMESTAMP]); + $this->assertInstanceOf(MSSQLColumn::class, $col); + $this->assertEquals('datetime2', $col->getDatatype()); + } + /** + * @test + * Test that explicit size is preserved when type is auto-mapped. + */ + public function testAutoMapPreservesExplicitSize() { + $col = ColumnFactory::create('mssql', 'summary', ['type' => DataType::TEXT, 'size' => 500]); + $this->assertEquals('nvarchar', $col->getDatatype()); + $this->assertEquals(500, $col->getSize()); + } + /** + * @test + * Test that native MSSQL types are not affected by auto-mapping. + */ + public function testNativeTypesUnaffected() { + $col = ColumnFactory::create('mssql', 'name', ['type' => DataType::VARCHAR, 'size' => 100]); + $this->assertEquals('varchar', $col->getDatatype()); + $this->assertEquals(100, $col->getSize()); + + $col2 = ColumnFactory::create('mssql', 'num', ['type' => DataType::INT]); + $this->assertEquals('int', $col2->getDatatype()); + + $col3 = ColumnFactory::create('mssql', 'uname', ['type' => DataType::NVARCHAR, 'size' => 200]); + $this->assertEquals('nvarchar', $col3->getDatatype()); + $this->assertEquals(200, $col3->getSize()); + } } diff --git a/tests/WebFiori/Tests/Database/MySql/MySQLColumnTest.php b/tests/WebFiori/Tests/Database/MySql/MySQLColumnTest.php index 82fc65b..57cb799 100644 --- a/tests/WebFiori/Tests/Database/MySql/MySQLColumnTest.php +++ b/tests/WebFiori/Tests/Database/MySql/MySQLColumnTest.php @@ -796,4 +796,92 @@ public function testGetPHPType05() { $colObj->setIsNull(true); $this->assertEquals('string|null', $colObj->getPHPType()); } + /** + * @test + * Test that MSSQL-only types are auto-mapped when creating MySQL columns. + */ + public function testAutoMapMSSQLBigintToMySQL() { + $col = ColumnFactory::create('mysql', 'big_id', ['type' => DataType::BIGINT]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('int', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLBinaryToMySQL() { + $col = ColumnFactory::create('mysql', 'data', ['type' => DataType::BINARY]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('blob', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLDateToMySQL() { + $col = ColumnFactory::create('mysql', 'd', ['type' => DataType::DATE]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('datetime', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLDatetime2ToMySQL() { + $col = ColumnFactory::create('mysql', 'dt', ['type' => DataType::DATETIME2]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('datetime', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLMoneyToMySQL() { + $col = ColumnFactory::create('mysql', 'amount', ['type' => DataType::MONEY]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('decimal', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLNcharToMySQL() { + $col = ColumnFactory::create('mysql', 'code', ['type' => DataType::NCHAR]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('text', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLNvarcharToMySQL() { + $col = ColumnFactory::create('mysql', 'content', ['type' => DataType::NVARCHAR]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('text', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLTimeToMySQL() { + $col = ColumnFactory::create('mysql', 't', ['type' => DataType::TIME]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('varchar', $col->getDatatype()); + } + /** + * @test + */ + public function testAutoMapMSSQLVarbinaryToMySQL() { + $col = ColumnFactory::create('mysql', 'bin', ['type' => DataType::VARBINARY]); + $this->assertInstanceOf(MySQLColumn::class, $col); + $this->assertEquals('blob', $col->getDatatype()); + } + /** + * @test + * Test that native MySQL types are not affected by auto-mapping. + */ + public function testNativeTypesUnaffected() { + $col = ColumnFactory::create('mysql', 'name', ['type' => DataType::VARCHAR, 'size' => 100]); + $this->assertEquals('varchar', $col->getDatatype()); + $this->assertEquals(100, $col->getSize()); + + $col2 = ColumnFactory::create('mysql', 'body', ['type' => DataType::TEXT]); + $this->assertEquals('text', $col2->getDatatype()); + + $col3 = ColumnFactory::create('mysql', 'num', ['type' => DataType::INT]); + $this->assertEquals('int', $col3->getDatatype()); + } }