From: Mike Bayer Date: Tue, 20 Jan 2026 14:45:25 +0000 (-0500) Subject: change float -> Double in the _type_map. X-Git-Tag: rel_2_1_0b1~1^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1dc49abfea36f6f517b40b15514f10fe439b2926;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git change float -> Double in the _type_map. The :class:`.Double` type is now used when a Python float value is detected as a literal value to be sent as a bound parameter, rather than the :class:`.Float` type. :class:`.Double` has the same implementation as :class:`.Float`, but when rendered in a CAST, produces ``DOUBLE`` or ``DOUBLE PRECISION`` rather than ``FLOAT``. The former better matches Python's ``float`` datatype which uses 8-byte double-precision storage. Third party dialects which don't support the :class:`.Double` type directly may need adjustment so that they render an appropriate keyword (e.g. ``FLOAT``) when the :class:`.Double` datatype is encountered. Fixes: #10300 Change-Id: I6d221ca9206a10c73fefad55f7ab7a6da5c81d6b --- diff --git a/doc/build/changelog/migration_21.rst b/doc/build/changelog/migration_21.rst index 67a979d589..bfc259e62b 100644 --- a/doc/build/changelog/migration_21.rst +++ b/doc/build/changelog/migration_21.rst @@ -1183,6 +1183,61 @@ raise an error, directing users to use :class:`_sql.FrameClause` instead. :ticket:`12596` +.. _change_10300: + +Python float literals now render as DOUBLE in CAST expressions +--------------------------------------------------------------- + +When Python ``float`` values are used as literal bound parameters in SQL +expressions, in the absence of an explicit type being passed to the bound +parameter expression, they now use the :class:`.Double` type instead of +:class:`.Float`. This change affects the SQL keyword that is rendered when +these values appear in CAST expressions. + +The :class:`.Double` and :class:`.Float` types have identical implementations +and behavior at runtime. The only difference is in DDL and CAST rendering: +:class:`.Double` renders as ``DOUBLE`` or ``DOUBLE PRECISION``, while +:class:`.Float` renders as ``FLOAT``. The ``DOUBLE`` keyword better matches +Python's ``float`` datatype, which uses 8-byte double-precision storage. + +This change is most visible in division operations, where a float divisor is +automatically cast to ensure proper floating-point division:: + + from sqlalchemy import table, column, Integer, select + + t = table("t", column("x", Integer)) + + stmt = select(t.c.x / 5.0) + +The above statement will now render with a DOUBLE CAST on most backends: + +.. sourcecode:: sql + + SELECT t.x / CAST(:x_1 AS DOUBLE) AS anon_1 FROM t + +Previously, this would have rendered as ``CAST(:x_1 AS FLOAT)``. + +Notes for Third-Party Dialects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Third-party dialect implementations that don't support the ``DOUBLE`` keyword +will need to ensure they have a ``visit_double()`` method in their type compiler +to render an appropriate alternative keyword for the target database. + +For example, a dialect for a database that doesn't support ``DOUBLE`` might +map it to ``FLOAT``:: + + class MyDialectTypeCompiler(GenericTypeCompiler): + def visit_double(self, type_, **kw): + # Map double to FLOAT for databases that don't support DOUBLE + return "FLOAT" + +The built-in SQLAlchemy dialects (PostgreSQL, MySQL, Oracle, SQL Server, +SQLite) all handle ``visit_double()`` by rendering either ``DOUBLE`` or +``DOUBLE PRECISION``. + +:ticket:`10300` + PostgreSQL ========== diff --git a/doc/build/changelog/unreleased_21/10300.rst b/doc/build/changelog/unreleased_21/10300.rst new file mode 100644 index 0000000000..edc14fa893 --- /dev/null +++ b/doc/build/changelog/unreleased_21/10300.rst @@ -0,0 +1,17 @@ +.. change:: + :tags: bug, sql + :tickets: 10300 + + The :class:`.Double` type is now used when a Python float value is detected + as a literal value to be sent as a bound parameter, rather than the + :class:`.Float` type. :class:`.Double` has the same implementation as + :class:`.Float`, but when rendered in a CAST, produces ``DOUBLE`` or + ``DOUBLE PRECISION`` rather than ``FLOAT``. The former better matches + Python's ``float`` datatype which uses 8-byte double-precision storage. + Third party dialects which don't support the :class:`.Double` type directly + may need adjustment so that they render an appropriate keyword (e.g. + ``FLOAT``) when the :class:`.Double` datatype is encountered. + + .. seealso:: + + :ref:`change_10300` diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 9a43056cdb..6308f3014d 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -3992,7 +3992,7 @@ _UNICODE = Unicode() _type_map: Dict[Type[Any], TypeEngine[Any]] = { int: Integer(), - float: Float(), + float: Double(), bool: BOOLEANTYPE, _python_UUID: Uuid(), decimal.Decimal: Numeric(), diff --git a/test/ext/test_hybrid.py b/test/ext/test_hybrid.py index a184195417..162331906b 100644 --- a/test/ext/test_hybrid.py +++ b/test/ext/test_hybrid.py @@ -1879,7 +1879,7 @@ class DMLTest( } ), "INSERT INTO a (amount, rate) VALUES " - "((:param_1 / CAST(:rate AS FLOAT)), :rate)", + "((:param_1 / CAST(:rate AS DOUBLE)), :rate)", checkparams={"param_1": 25, "rate": 1.5}, ) @@ -1904,7 +1904,7 @@ class DMLTest( ): 25, } ), - "UPDATE a SET amount=(:param_1 / CAST(:rate AS FLOAT)), " + "UPDATE a SET amount=(:param_1 / CAST(:rate AS DOUBLE)), " "rate=:rate", checkparams={"param_1": 25, "rate": 1.5}, ) @@ -1921,7 +1921,7 @@ class DMLTest( ): 25 } ), - "UPDATE a SET amount=(:param_1 / CAST(a.rate AS FLOAT))", + "UPDATE a SET amount=(:param_1 / CAST(a.rate AS DOUBLE))", checkparams={"param_1": 25}, ) diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 44792d135a..826a6f2ec4 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -3699,7 +3699,7 @@ class ExpressionTest( @testing.combinations( (5, Integer), - (2.65, Float), + (2.65, Double), (True, Boolean), (decimal.Decimal("2.65"), Numeric), (datetime.date(2015, 7, 20), Date),