]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Coerce float Python type to Float; ensure Python float coming back
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 23 Jun 2017 03:51:52 +0000 (23:51 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 26 Jun 2017 15:05:38 +0000 (11:05 -0400)
Added some extra strictness to the handling of Python "float" values
passed to SQL statements.  A "float" value will be associated with the
:class:`.Float` datatype and not the Decimal-coercing :class:`.Numeric`
datatype as was the case before, eliminating a confusing warning
emitted on SQLite as well as unecessary coercion to Decimal.

Change-Id: I1bb1810ff1d198c0d929ccba5656e55401d74119
Fixes: #4017
doc/build/changelog/changelog_12.rst
doc/build/changelog/migration_12.rst
lib/sqlalchemy/sql/sqltypes.py
lib/sqlalchemy/testing/suite/test_types.py
test/sql/test_types.py

index a963b96f070c760efbaa38a2751a6111e675803b..5dc83da2d50898e5e3dad32eeb1d943dcdb745ec 100644 (file)
 .. changelog::
     :version: 1.2.0b1
 
+    .. change:: 4017
+        :tags: bug, sql
+        :tickets: 4017
+
+        Added some extra strictness to the handling of Python "float" values
+        passed to SQL statements.  A "float" value will be associated with the
+        :class:`.Float` datatype and not the Decimal-coercing :class:`.Numeric`
+        datatype as was the case before, eliminating a confusing warning
+        emitted on SQLite as well as unecessary coercion to Decimal.
+
+        .. seealso::
+
+            :ref:`change_floats_12`
+
     .. change:: 3058
         :tags: feature, orm
         :tickets: 3058
index f0857c5314bb969f8f5ac1b62a4dd7932c96656d..add12a50c304d73ad3c3ea30fe63b9c2aced95ec 100644 (file)
@@ -764,6 +764,31 @@ Where the value of the parameter "x_1" is ``'total/%score'``.
 
 :ticket:`2694`
 
+.. _change_floats_12:
+
+Stronger typing added to "float" datatypes
+------------------------------------------
+
+A series of changes allow for use of the :class:`.Float` datatype to more
+strongly link itself to Python floating point values, instead of the more
+generic :class:`.Numeric`.  The changes are mostly related to ensuring
+that Python floating point values are not erroneously coerced to
+``Decimal()``, and are coerced to ``float`` if needed, on the result side,
+if the application is working with plain floats.
+
+* A plain Python "float" value passed to a SQL expression will now be
+  pulled into a literal parameter with the type :class:`.Float`; previously,
+  the type was :class:`.Numeric`, with the default "asdecimal=True" flag, which
+  meant the result type would coerce to ``Decimal()``.  In particular,
+  this would emit a confusing warning on SQLite::
+
+        float_value = connection.scalar(
+            select([literal(4.56)])   # the "BindParameter" will now be
+                                      # Float, not Numeric(asdecimal=True)
+        )
+
+:ticket:`4017`
+
 Key Behavioral Changes - ORM
 ============================
 
index 7a3c50549e1caf9ac9e9c672f868cc31f560eb9a..06b5e5c19c02b545d84c5d3cd61757bc88cd1e57 100644 (file)
@@ -2604,7 +2604,7 @@ MATCHTYPE = MatchType()
 
 _type_map = {
     int: Integer(),
-    float: Numeric(),
+    float: Float(),
     bool: BOOLEANTYPE,
     decimal.Decimal: Numeric(),
     dt.date: Date(),
index ee757e1cafc948ec026610333f086f7e26c33fd8..de32e77a4ce71de07bdcaf3a1e31d9d8057210ed 100644 (file)
@@ -431,6 +431,24 @@ class NumericTest(_LiteralRoundTripFixture, fixtures.TestBase):
             filter_=lambda n: n is not None and round(n, 5) or None
         )
 
+    @testing.fails_on("mysql", "until we do #4020")
+    def test_float_coerce_round_trip(self):
+        expr = 15.7563
+
+        val = testing.db.scalar(
+            select([literal(expr)])
+        )
+        eq_(val, expr)
+
+    # TODO: this one still breaks on MySQL
+    # def test_decimal_coerce_round_trip(self):
+    #    expr = decimal.Decimal("15.7563")
+    #
+    #    val = testing.db.scalar(
+    #        select([literal(expr)])
+    #    )
+    #    eq_(val, expr)
+
     @testing.requires.precision_numerics_general
     def test_precision_decimal(self):
         numbers = set([
index f46ef21cd8b6571741a3d46f5970b9998e8eea6c..9107adaca0b84d04157fe6714314eab21c57c51a 100644 (file)
@@ -2025,6 +2025,23 @@ class ExpressionTest(
         expr = column('foo', CHAR) == "asdf"
         eq_(expr.right.type.__class__, CHAR)
 
+    def test_actual_literal_adapters(self):
+        for data, expected in [
+            (5, Integer),
+            (2.65, Float),
+            (True, Boolean),
+            (decimal.Decimal("2.65"), Numeric),
+            (datetime.date(2015, 7, 20), Date),
+            (datetime.time(10, 15, 20), Time),
+            (datetime.datetime(2015, 7, 20, 10, 15, 20), DateTime),
+            (datetime.timedelta(seconds=5), Interval),
+            (None, types.NullType)
+        ]:
+            is_(
+                literal(data).type.__class__,
+                expected
+            )
+
     def test_typedec_operator_adapt(self):
         expr = test_table.c.bvalue + "hi"