.. 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
: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
============================
_type_map = {
int: Integer(),
- float: Numeric(),
+ float: Float(),
bool: BOOLEANTYPE,
decimal.Decimal: Numeric(),
dt.date: Date(),
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([
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"