summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2017-06-26 11:56:38 -0400
committerGerrit Code Review <gerrit@awstats.zzzcomputing.com>2017-06-26 11:56:38 -0400
commite04594339c19c3cd8b8e0d96ce83e5ded961dbb7 (patch)
tree507edb6a721047f8aa81f760a5299bb18e6bbcb7
parent9f1a375f10cb7af558a9549081a0e792546aca21 (diff)
parent1776597131ef96472b5188cebc72c31a387c90f4 (diff)
downloadsqlalchemy-e04594339c19c3cd8b8e0d96ce83e5ded961dbb7.tar.gz
Merge "Coerce float Python type to Float; ensure Python float coming back"
-rw-r--r--doc/build/changelog/changelog_12.rst14
-rw-r--r--doc/build/changelog/migration_12.rst25
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py2
-rw-r--r--lib/sqlalchemy/testing/suite/test_types.py18
-rw-r--r--test/sql/test_types.py17
5 files changed, 75 insertions, 1 deletions
diff --git a/doc/build/changelog/changelog_12.rst b/doc/build/changelog/changelog_12.rst
index a963b96f0..5dc83da2d 100644
--- a/doc/build/changelog/changelog_12.rst
+++ b/doc/build/changelog/changelog_12.rst
@@ -13,6 +13,20 @@
.. 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
diff --git a/doc/build/changelog/migration_12.rst b/doc/build/changelog/migration_12.rst
index f0857c531..add12a50c 100644
--- a/doc/build/changelog/migration_12.rst
+++ b/doc/build/changelog/migration_12.rst
@@ -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
============================
diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py
index 7a3c50549..06b5e5c19 100644
--- a/lib/sqlalchemy/sql/sqltypes.py
+++ b/lib/sqlalchemy/sql/sqltypes.py
@@ -2604,7 +2604,7 @@ MATCHTYPE = MatchType()
_type_map = {
int: Integer(),
- float: Numeric(),
+ float: Float(),
bool: BOOLEANTYPE,
decimal.Decimal: Numeric(),
dt.date: Date(),
diff --git a/lib/sqlalchemy/testing/suite/test_types.py b/lib/sqlalchemy/testing/suite/test_types.py
index ee757e1ca..de32e77a4 100644
--- a/lib/sqlalchemy/testing/suite/test_types.py
+++ b/lib/sqlalchemy/testing/suite/test_types.py
@@ -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([
diff --git a/test/sql/test_types.py b/test/sql/test_types.py
index f46ef21cd..9107adaca 100644
--- a/test/sql/test_types.py
+++ b/test/sql/test_types.py
@@ -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"