diff --git a/docs/en/reference/types.rst b/docs/en/reference/types.rst index deaf5c85d7d..21dd2669911 100644 --- a/docs/en/reference/types.rst +++ b/docs/en/reference/types.rst @@ -125,13 +125,21 @@ or ``null`` if no data is present. it approximates precision which can lead to false assumptions in applications. +smallfloat +++++++++++ + +Maps and converts single precision floating-point values. +This type is suitable for storing numeric data with approximate precision, where 4-byte (32-bit) storage is sufficient. +Single precision values typically have a wide range, accommodating most numerical requirements with a precision of up to 7 decimal digits. +Values retrieved from the database are always converted to PHP's ``float``/``double`` type or ``null`` if no data is present. + float +++++ -Maps and converts numeric data with floating-point precision. -If you only need an approximate precision for numbers with fractions, you should -consider using this type. -Values retrieved from the database are always converted to PHP's +Maps and converts double precision floating-point values. +This type is suitable for storing numeric data with higher precision, requiring 8-byte (64-bit) storage. +Double precision values typically offer an extensive range, meeting the demands of more precise calculations +with a precision of up to 15 decimal digits. Values retrieved from the database are always converted to PHP's ``float``/``double`` type or ``null`` if no data is present. String types @@ -572,6 +580,16 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+ | | | | | **SQLite** | | | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ + | **smallfloat** | ``float`` | **MySQL** | *all* | ``FLOAT`` ``UNSIGNED`` [10] | + | | +--------------------------+---------+----------------------------------------------------------+ + | | | **PostgreSQL** | *all* | ``REAL`` | + | | +--------------------------+ | | + | | | **Oracle** | | | + | | +--------------------------+ | | + | | | **SQL Server** | | | + | | +--------------------------+ | | + | | | **SQLite** | | | + +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **float** | ``float`` | **MySQL** | *all* | ``DOUBLE PRECISION`` ``UNSIGNED`` [10] | | | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``DOUBLE PRECISION`` | diff --git a/src/Platforms/AbstractMySQLPlatform.php b/src/Platforms/AbstractMySQLPlatform.php index e30817be93c..e94fbaefd07 100644 --- a/src/Platforms/AbstractMySQLPlatform.php +++ b/src/Platforms/AbstractMySQLPlatform.php @@ -646,6 +646,14 @@ public function getFloatDeclarationSQL(array $column): string return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column); } + /** + * {@inheritDoc} + */ + public function getSmallFloatDeclarationSQL(array $column): string + { + return 'FLOAT' . $this->getUnsignedDeclaration($column); + } + /** * {@inheritDoc} */ @@ -727,7 +735,7 @@ protected function initializeDoctrineTypeMappings(): void 'datetime' => Types::DATETIME_MUTABLE, 'decimal' => Types::DECIMAL, 'double' => Types::FLOAT, - 'float' => Types::FLOAT, + 'float' => Types::SMALLFLOAT, 'int' => Types::INTEGER, 'integer' => Types::INTEGER, 'json' => Types::JSON, diff --git a/src/Platforms/AbstractPlatform.php b/src/Platforms/AbstractPlatform.php index c1612376ecf..31b3c102762 100644 --- a/src/Platforms/AbstractPlatform.php +++ b/src/Platforms/AbstractPlatform.php @@ -1877,6 +1877,12 @@ public function getFloatDeclarationSQL(array $column): string return 'DOUBLE PRECISION'; } + /** @param mixed[] $column */ + public function getSmallFloatDeclarationSQL(array $column): string + { + return 'REAL'; + } + /** * Gets the default transaction isolation level of the platform. * diff --git a/src/Platforms/DB2Platform.php b/src/Platforms/DB2Platform.php index a00b24bb702..1167620dab8 100644 --- a/src/Platforms/DB2Platform.php +++ b/src/Platforms/DB2Platform.php @@ -52,7 +52,7 @@ protected function initializeDoctrineTypeMappings(): void 'decimal' => Types::DECIMAL, 'double' => Types::FLOAT, 'integer' => Types::INTEGER, - 'real' => Types::FLOAT, + 'real' => Types::SMALLFLOAT, 'smallint' => Types::SMALLINT, 'time' => Types::TIME_MUTABLE, 'timestamp' => Types::DATETIME_MUTABLE, diff --git a/src/Platforms/OraclePlatform.php b/src/Platforms/OraclePlatform.php index 314f6ee7d40..2e123feabf2 100644 --- a/src/Platforms/OraclePlatform.php +++ b/src/Platforms/OraclePlatform.php @@ -750,6 +750,7 @@ protected function initializeDoctrineTypeMappings(): void 'nvarchar2' => Types::STRING, 'pls_integer' => Types::BOOLEAN, 'raw' => Types::BINARY, + 'real' => Types::SMALLFLOAT, 'rowid' => Types::STRING, 'timestamp' => Types::DATETIME_MUTABLE, 'timestamptz' => Types::DATETIMETZ_MUTABLE, diff --git a/src/Platforms/PostgreSQLPlatform.php b/src/Platforms/PostgreSQLPlatform.php index 166ee7547fb..329a2b1a53a 100644 --- a/src/Platforms/PostgreSQLPlatform.php +++ b/src/Platforms/PostgreSQLPlatform.php @@ -701,7 +701,7 @@ protected function initializeDoctrineTypeMappings(): void 'double' => Types::FLOAT, 'double precision' => Types::FLOAT, 'float' => Types::FLOAT, - 'float4' => Types::FLOAT, + 'float4' => Types::SMALLFLOAT, 'float8' => Types::FLOAT, 'inet' => Types::STRING, 'int' => Types::INTEGER, @@ -717,7 +717,7 @@ protected function initializeDoctrineTypeMappings(): void 'serial' => Types::INTEGER, 'serial4' => Types::INTEGER, 'serial8' => Types::BIGINT, - 'real' => Types::FLOAT, + 'real' => Types::SMALLFLOAT, 'smallint' => Types::SMALLINT, 'text' => Types::TEXT, 'time' => Types::TIME_MUTABLE, diff --git a/src/Platforms/SQLServerPlatform.php b/src/Platforms/SQLServerPlatform.php index 7a10a32e3be..c1fcb8fcb98 100644 --- a/src/Platforms/SQLServerPlatform.php +++ b/src/Platforms/SQLServerPlatform.php @@ -1057,7 +1057,7 @@ protected function initializeDoctrineTypeMappings(): void 'ntext' => Types::TEXT, 'numeric' => Types::DECIMAL, 'nvarchar' => Types::STRING, - 'real' => Types::FLOAT, + 'real' => Types::SMALLFLOAT, 'smalldatetime' => Types::DATETIME_MUTABLE, 'smallint' => Types::SMALLINT, 'smallmoney' => Types::INTEGER, diff --git a/src/Platforms/SQLitePlatform.php b/src/Platforms/SQLitePlatform.php index f1792e07173..aec916f10d5 100644 --- a/src/Platforms/SQLitePlatform.php +++ b/src/Platforms/SQLitePlatform.php @@ -459,7 +459,7 @@ protected function initializeDoctrineTypeMappings(): void 'ntext' => 'string', 'numeric' => 'decimal', 'nvarchar' => 'string', - 'real' => 'float', + 'real' => 'smallfloat', 'serial' => 'integer', 'smallint' => 'smallint', 'string' => 'string', diff --git a/src/Schema/OracleSchemaManager.php b/src/Schema/OracleSchemaManager.php index f973eaa3ed2..3de6892c81c 100644 --- a/src/Schema/OracleSchemaManager.php +++ b/src/Schema/OracleSchemaManager.php @@ -150,6 +150,13 @@ protected function _getPortableTableColumnDefinition(array $tableColumn): Column break; + case 'float': + if ($precision === 63) { + $type = 'smallfloat'; + } + + break; + case 'varchar': case 'varchar2': case 'nvarchar2': diff --git a/src/Types/SmallFloatType.php b/src/Types/SmallFloatType.php new file mode 100644 index 00000000000..431ddb2870e --- /dev/null +++ b/src/Types/SmallFloatType.php @@ -0,0 +1,30 @@ +getSmallFloatDeclarationSQL($column); + } + + /** + * @param T $value + * + * @return (T is null ? null : float) + * + * @template T + */ + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?float + { + return $value === null ? null : (float) $value; + } +} diff --git a/src/Types/Type.php b/src/Types/Type.php index 9e058b13d92..bc4d3aaf417 100644 --- a/src/Types/Type.php +++ b/src/Types/Type.php @@ -39,6 +39,7 @@ abstract class Type Types::INTEGER => IntegerType::class, Types::JSON => JsonType::class, Types::SIMPLE_ARRAY => SimpleArrayType::class, + Types::SMALLFLOAT => SmallFloatType::class, Types::SMALLINT => SmallIntType::class, Types::STRING => StringType::class, Types::TEXT => TextType::class, diff --git a/src/Types/Types.php b/src/Types/Types.php index 07cf1ffefe4..6fef4cfce08 100644 --- a/src/Types/Types.php +++ b/src/Types/Types.php @@ -27,6 +27,7 @@ final class Types public const INTEGER = 'integer'; public const JSON = 'json'; public const SIMPLE_ARRAY = 'simple_array'; + public const SMALLFLOAT = 'smallfloat'; public const SMALLINT = 'smallint'; public const STRING = 'string'; public const TEXT = 'text'; diff --git a/tests/Functional/Driver/PgSQL/ResultTest.php b/tests/Functional/Driver/PgSQL/ResultTest.php index 99ef5095845..a61ab026400 100644 --- a/tests/Functional/Driver/PgSQL/ResultTest.php +++ b/tests/Functional/Driver/PgSQL/ResultTest.php @@ -143,8 +143,10 @@ public static function typedValueProvider(): Generator yield 'text' => ['TEXT', 'some value', Types::STRING]; yield 'boolean true' => ['BOOLEAN', true, Types::BOOLEAN]; yield 'boolean false' => ['BOOLEAN', false, Types::BOOLEAN]; - yield 'float' => ['REAL', 47.11, Types::FLOAT]; - yield 'negative float with exponent' => ['REAL', -8.15e10, Types::FLOAT]; + yield 'float' => ['DOUBLE PRECISION', 47.11, Types::FLOAT]; + yield 'real' => ['REAL', 47.11, Types::SMALLFLOAT]; + yield 'negative float with exponent' => ['DOUBLE PRECISION', -8.15e10, Types::FLOAT]; + yield 'negative real with exponent' => ['REAL', -8.15e5, Types::SMALLFLOAT]; yield 'double' => ['DOUBLE PRECISION', 47.11, Types::FLOAT]; yield 'decimal' => ['NUMERIC (6, 2)', '47.11', Types::DECIMAL]; yield 'binary' => ['BYTEA', chr(0x8b), Types::BINARY]; diff --git a/tests/Functional/Schema/MySQLSchemaManagerTest.php b/tests/Functional/Schema/MySQLSchemaManagerTest.php index 2b17b6a25be..852eb555423 100644 --- a/tests/Functional/Schema/MySQLSchemaManagerTest.php +++ b/tests/Functional/Schema/MySQLSchemaManagerTest.php @@ -15,7 +15,9 @@ use Doctrine\DBAL\Tests\Functional\Schema\MySQL\PointType; use Doctrine\DBAL\Tests\TestUtil; use Doctrine\DBAL\Types\BlobType; +use Doctrine\DBAL\Types\FloatType; use Doctrine\DBAL\Types\JsonType; +use Doctrine\DBAL\Types\SmallFloatType; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; @@ -370,22 +372,22 @@ public function testListDecimalTypeColumns(): void self::assertTrue($columns['col_unsigned']->getUnsigned()); } - public function testListFloatTypeColumns(): void + public function testListUnsignedFloatTypeColumns(): void { - $tableName = 'test_list_float_columns'; + $tableName = 'test_unsigned_float_columns'; $table = new Table($tableName); - $table->addColumn('col', Types::FLOAT); $table->addColumn('col_unsigned', Types::FLOAT, ['unsigned' => true]); + $table->addColumn('col_smallfloat_unsigned', Types::SMALLFLOAT, ['unsigned' => true]); $this->dropAndCreateTable($table); $columns = $this->schemaManager->listTableColumns($tableName); - self::assertArrayHasKey('col', $columns); - self::assertArrayHasKey('col_unsigned', $columns); - self::assertFalse($columns['col']->getUnsigned()); + self::assertInstanceOf(FloatType::class, $columns['col_unsigned']->getType()); + self::assertInstanceOf(SmallFloatType::class, $columns['col_smallfloat_unsigned']->getType()); self::assertTrue($columns['col_unsigned']->getUnsigned()); + self::assertTrue($columns['col_smallfloat_unsigned']->getUnsigned()); } public function testJsonColumnType(): void diff --git a/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php b/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php index 22f2a20dd66..3cf2ee70708 100644 --- a/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php +++ b/tests/Functional/Schema/PostgreSQLSchemaManagerTest.php @@ -415,6 +415,7 @@ public function testListNegativeColumnDefaultValue(): void $table->addColumn('col_integer', Types::INTEGER, ['default' => -1]); $table->addColumn('col_bigint', Types::BIGINT, ['default' => -1]); $table->addColumn('col_float', Types::FLOAT, ['default' => -1.1]); + $table->addColumn('col_smallfloat', Types::SMALLFLOAT, ['default' => -1.1]); $table->addColumn('col_decimal', Types::DECIMAL, [ 'precision' => 2, 'scale' => 1, @@ -430,6 +431,7 @@ public function testListNegativeColumnDefaultValue(): void self::assertEquals(-1, $columns['col_integer']->getDefault()); self::assertEquals(-1, $columns['col_bigint']->getDefault()); self::assertEquals(-1.1, $columns['col_float']->getDefault()); + self::assertEquals(-1.1, $columns['col_smallfloat']->getDefault()); self::assertEquals(-1.1, $columns['col_decimal']->getDefault()); self::assertEquals('(-1)', $columns['col_string']->getDefault()); } diff --git a/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php index 3c3404a21dd..7ca35dee0d4 100644 --- a/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -27,7 +27,9 @@ use Doctrine\DBAL\Types\DateTimeType; use Doctrine\DBAL\Types\DateType; use Doctrine\DBAL\Types\DecimalType; +use Doctrine\DBAL\Types\FloatType; use Doctrine\DBAL\Types\IntegerType; +use Doctrine\DBAL\Types\SmallFloatType; use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\TextType; use Doctrine\DBAL\Types\TimeType; @@ -859,6 +861,24 @@ public function testListTableWithBlob(): void self::assertInstanceOf(BlobType::class, $created->getColumn('binarydata')->getType()); } + public function testListTableFloatTypeColumns(): void + { + $tableName = 'test_float_columns'; + $table = new Table($tableName); + + $table->addColumn('col_float', Types::FLOAT); + $table->addColumn('col_smallfloat', Types::SMALLFLOAT); + + $this->dropAndCreateTable($table); + + $columns = $this->schemaManager->listTableColumns($tableName); + + self::assertInstanceOf(FloatType::class, $columns['col_float']->getType()); + self::assertInstanceOf(SmallFloatType::class, $columns['col_smallfloat']->getType()); + self::assertFalse($columns['col_float']->getUnsigned()); + self::assertFalse($columns['col_smallfloat']->getUnsigned()); + } + /** @param mixed[] $data */ protected function createTestTable(string $name = 'test_table', array $data = []): Table { diff --git a/tests/Functional/TypeConversionTest.php b/tests/Functional/TypeConversionTest.php index f98e746ad91..7a577faee92 100644 --- a/tests/Functional/TypeConversionTest.php +++ b/tests/Functional/TypeConversionTest.php @@ -36,6 +36,7 @@ protected function setUp(): void $table->addColumn('test_text', Types::TEXT, ['notnull' => false]); $table->addColumn('test_json', Types::JSON, ['notnull' => false]); $table->addColumn('test_float', Types::FLOAT, ['notnull' => false]); + $table->addColumn('test_smallfloat', Types::SMALLFLOAT, ['notnull' => false]); $table->addColumn('test_decimal', Types::DECIMAL, ['notnull' => false, 'scale' => 2, 'precision' => 10]); $table->setPrimaryKey(['id']); @@ -91,6 +92,7 @@ public static function floatProvider(): iterable { return [ 'float' => [Types::FLOAT, 1.5], + 'smallfloat' => [Types::SMALLFLOAT, 1.5], ]; } diff --git a/tests/Platforms/AbstractMySQLPlatformTestCase.php b/tests/Platforms/AbstractMySQLPlatformTestCase.php index 4a48e89f0cf..684af042256 100644 --- a/tests/Platforms/AbstractMySQLPlatformTestCase.php +++ b/tests/Platforms/AbstractMySQLPlatformTestCase.php @@ -621,6 +621,21 @@ public static function getGeneratesFloatDeclarationSQL(): iterable ]; } + /** + * {@inheritDoc} + */ + public static function getGeneratesSmallFloatDeclarationSQL(): iterable + { + return [ + [[], 'FLOAT'], + [['unsigned' => true], 'FLOAT UNSIGNED'], + [['unsigned' => false], 'FLOAT'], + [['precision' => 5], 'FLOAT'], + [['scale' => 5], 'FLOAT'], + [['precision' => 4, 'scale' => 2], 'FLOAT'], + ]; + } + public function testQuotesDatabaseNameInListViewsSQL(): void { self::assertStringContainsStringIgnoringCase( diff --git a/tests/Platforms/AbstractPlatformTestCase.php b/tests/Platforms/AbstractPlatformTestCase.php index 54f5022b2d2..a2ce07aebb9 100644 --- a/tests/Platforms/AbstractPlatformTestCase.php +++ b/tests/Platforms/AbstractPlatformTestCase.php @@ -978,6 +978,26 @@ public static function getGeneratesFloatDeclarationSQL(): iterable ]; } + /** @param mixed[] $column */ + #[DataProvider('getGeneratesSmallFloatDeclarationSQL')] + public function testGeneratesSmallFloatDeclarationSQL(array $column, string $expectedSql): void + { + self::assertSame($expectedSql, $this->platform->getSmallFloatDeclarationSQL($column)); + } + + /** @return mixed[][] */ + public static function getGeneratesSmallFloatDeclarationSQL(): iterable + { + return [ + [[], 'REAL'], + [['unsigned' => true], 'REAL'], + [['unsigned' => false], 'REAL'], + [['precision' => 5], 'REAL'], + [['scale' => 5], 'REAL'], + [['precision' => 4, 'scale' => 2], 'REAL'], + ]; + } + public function testItEscapesStringsForLike(): void { self::assertSame( diff --git a/tests/Platforms/OraclePlatformTest.php b/tests/Platforms/OraclePlatformTest.php index c2040470be9..dfb5b85036b 100644 --- a/tests/Platforms/OraclePlatformTest.php +++ b/tests/Platforms/OraclePlatformTest.php @@ -320,6 +320,9 @@ public function testInitializesDoctrineTypeMappings(): void self::assertTrue($this->platform->hasDoctrineTypeMappingFor('date')); self::assertSame(Types::DATE_MUTABLE, $this->platform->getDoctrineTypeMapping('date')); + + self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real')); + self::assertSame(Types::SMALLFLOAT, $this->platform->getDoctrineTypeMapping('real')); } public function testGetVariableLengthStringTypeDeclarationSQLNoLength(): void diff --git a/tests/Platforms/SQLServerPlatformTest.php b/tests/Platforms/SQLServerPlatformTest.php index c1b6ec7f06b..4c1e25f2f25 100644 --- a/tests/Platforms/SQLServerPlatformTest.php +++ b/tests/Platforms/SQLServerPlatformTest.php @@ -732,7 +732,7 @@ public function testInitializesDoctrineTypeMappings(): void self::assertSame(Types::FLOAT, $this->platform->getDoctrineTypeMapping('float')); self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real')); - self::assertSame(Types::FLOAT, $this->platform->getDoctrineTypeMapping('real')); + self::assertSame(Types::SMALLFLOAT, $this->platform->getDoctrineTypeMapping('real')); self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double')); self::assertSame(Types::FLOAT, $this->platform->getDoctrineTypeMapping('double')); diff --git a/tests/Types/SmallFloatTest.php b/tests/Types/SmallFloatTest.php new file mode 100644 index 00000000000..3c59ad24e44 --- /dev/null +++ b/tests/Types/SmallFloatTest.php @@ -0,0 +1,42 @@ +platform = $this->createMock(AbstractPlatform::class); + $this->type = new SmallFloatType(); + } + + public function testFloatConvertsToPHPValue(): void + { + self::assertSame(5.5, $this->type->convertToPHPValue('5.5', $this->platform)); + } + + public function testFloatNullConvertsToPHPValue(): void + { + self::assertNull($this->type->convertToPHPValue(null, $this->platform)); + } + + public function testFloatConvertToDatabaseValue(): void + { + self::assertSame(5.5, $this->type->convertToDatabaseValue(5.5, $this->platform)); + } + + public function testFloatNullConvertToDatabaseValue(): void + { + self::assertNull($this->type->convertToDatabaseValue(null, $this->platform)); + } +}