From f432bc8fe15507653caff6f57e54d4bf6a78fbd7 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 3 Apr 2010 10:58:13 -0400 Subject: [PATCH] - the Numeric type raises an *enormous* warning when expected to convert floats to Decimal from a DBAPI that returns floats. This includes SQLite, Oracle, Sybase, MS-SQL. [ticket:1759] --- CHANGES | 5 +++++ lib/sqlalchemy/dialects/oracle/cx_oracle.py | 1 + lib/sqlalchemy/types.py | 10 +++++++++- test/dialect/test_oracle.py | 5 +++-- test/sql/test_types.py | 7 ++++--- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index a7c0ef4d48..6ad85a17c2 100644 --- a/CHANGES +++ b/CHANGES @@ -85,6 +85,11 @@ CHANGES render inside of column expressions already assigned a label. [ticket:1747] + - the Numeric type raises an *enormous* warning when expected + to convert floats to Decimal from a DBAPI that returns floats. + This includes SQLite, Oracle, Sybase, MS-SQL. + [ticket:1759] + - postgresql - The psycopg2 dialect will log NOTICE messages via the "sqlalchemy.dialects.postgresql" logger name. diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index 91af6620bc..c02f188861 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -345,6 +345,7 @@ class ReturningResultProxy(base.FullyBufferedResultProxy): class OracleDialect_cx_oracle(OracleDialect): execution_ctx_cls = OracleExecutionContext_cx_oracle statement_compiler = OracleCompiler_cx_oracle + driver = "cx_oracle" colspecs = colspecs = { diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 16cd57f268..dba75b36e2 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -939,6 +939,13 @@ class Numeric(_DateAffinity, TypeEngine): # we're a "numeric", DBAPI will give us Decimal directly return None else: + util.warn("Dialect %s+%s does *not* support Decimal objects natively, " + "and SQLAlchemy must convert from floating point - " + "rounding errors and other issues may occur. " + "Please consider storing Decimal numbers as strings or " + "integers on this platform for lossless storage." % + (dialect.name, dialect.driver)) + # we're a "numeric", DBAPI returns floats, convert. if self.scale is not None: return processors.to_decimal_processor_factory(_python_Decimal, self.scale) @@ -976,7 +983,8 @@ class Float(Numeric): :param precision: the numeric precision for use in DDL ``CREATE TABLE``. :param asdecimal: the same flag as that of :class:`Numeric`, but - defaults to ``False``. + defaults to ``False``. Note that setting this flag to ``True`` + results in floating point conversion. """ self.precision = precision diff --git a/test/dialect/test_oracle.py b/test/dialect/test_oracle.py index bcb34b05c7..29014799a6 100644 --- a/test/dialect/test_oracle.py +++ b/test/dialect/test_oracle.py @@ -34,9 +34,9 @@ create or replace procedure foo(x_in IN number, x_out OUT number, y_out OUT numb def test_out_params(self): result = testing.db.execute(text("begin foo(:x_in, :x_out, :y_out, :z_out); end;", bindparams=[ - bindparam('x_in', Numeric), + bindparam('x_in', Float), outparam('x_out', Integer), - outparam('y_out', Numeric), + outparam('y_out', Float), outparam('z_out', String)]), x_in=5) eq_( @@ -627,6 +627,7 @@ class TypesTest(TestBase, AssertsCompiledSQL): finally: metadata.drop_all() + @testing.emits_warning(r".*does \*not\* support Decimal objects natively") def test_numerics(self): m = MetaData(testing.db) t1 = Table('t1', m, diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 089ef727d2..764ac75848 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -1057,6 +1057,7 @@ class NumericTest(TestBase): def teardown(self): metadata.drop_all() + @testing.emits_warning(r".*does \*not\* support Decimal objects natively") def _do_test(self, type_, input_, output, filter_ = None): t = Table('t', metadata, Column('x', type_)) t.create() @@ -1067,10 +1068,10 @@ class NumericTest(TestBase): if filter_: result = set(filter_(x) for x in result) output = set(filter_(x) for x in output) - print result - print output + #print result + #print output eq_(result, output) - + def test_numeric_as_decimal(self): self._do_test( Numeric(precision=8, scale=4), -- 2.47.3