From: Mike Bayer Date: Thu, 7 Feb 2013 01:20:07 +0000 (-0500) Subject: port numeric tests to dialect suite X-Git-Tag: rel_0_8_0~24^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8550a4c38629373baad853b3c37ca3bd92a2fe54;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git port numeric tests to dialect suite --- diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index e6e21d2cb7..6cc1de96df 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -231,6 +231,10 @@ class SuiteRequirements(Requirements): self.config.db.dialect.sequences_optional ], "no sequence support, or sequences not optional") + + + + @property def reflects_pk_names(self): return exclusions.closed() @@ -328,6 +332,40 @@ class SuiteRequirements(Requirements): return exclusions.open() + @property + def precision_numerics_general(self): + """target backend has general support for moderately high-precision + numerics.""" + return exclusions.open() + + @property + def precision_numerics_enotation_small(self): + """target backend supports Decimal() objects using E notation + to represent very small values.""" + return exclusions.closed() + + @property + def precision_numerics_enotation_large(self): + """target backend supports Decimal() objects using E notation + to represent very large values.""" + return exclusions.closed() + + @property + def precision_numerics_many_significant_digits(self): + """target backend supports values with many digits on both sides, + such as 319438950232418390.273596, 87673.594069654243 + + """ + return exclusions.closed() + + @property + def precision_numerics_retains_significant_digits(self): + """A precision numeric type will return empty significant digits, + i.e. a value such as 10.000 will come back in Decimal form with + the .000 maintained.""" + + return exclusions.closed() + @property def text_type(self): """Target database must support an unbounded Text() " diff --git a/lib/sqlalchemy/testing/suite/test_types.py b/lib/sqlalchemy/testing/suite/test_types.py index 5ad26c2f2f..0716b1b914 100644 --- a/lib/sqlalchemy/testing/suite/test_types.py +++ b/lib/sqlalchemy/testing/suite/test_types.py @@ -4,8 +4,11 @@ from .. import fixtures, config from ..assertions import eq_ from ..config import requirements from sqlalchemy import Integer, Unicode, UnicodeText, select -from sqlalchemy import Date, DateTime, Time, MetaData, String, Text +from sqlalchemy import Date, DateTime, Time, MetaData, String, \ + Text, Numeric, Float from ..schema import Table, Column +from ... import testing +import decimal import datetime @@ -243,9 +246,148 @@ class DateHistoricTest(_DateFixture, fixtures.TablesTest): datatype = Date data = datetime.date(1727, 4, 1) +class NumericTest(fixtures.TestBase): + + @testing.emits_warning(r".*does \*not\* support Decimal objects natively") + @testing.provide_metadata + def _do_test(self, type_, input_, output, filter_=None, check_scale=False): + metadata = self.metadata + t = Table('t', metadata, Column('x', type_)) + t.create() + t.insert().execute([{'x':x} for x in input_]) + + result = set([row[0] for row in t.select().execute()]) + output = set(output) + if filter_: + result = set(filter_(x) for x in result) + output = set(filter_(x) for x in output) + eq_(result, output) + if check_scale: + eq_( + [str(x) for x in result], + [str(x) for x in output], + ) + + def test_numeric_as_decimal(self): + self._do_test( + Numeric(precision=8, scale=4), + [15.7563, decimal.Decimal("15.7563"), None], + [decimal.Decimal("15.7563"), None], + ) + + def test_numeric_as_float(self): + self._do_test( + Numeric(precision=8, scale=4, asdecimal=False), + [15.7563, decimal.Decimal("15.7563"), None], + [15.7563, None], + ) + + def test_float_as_decimal(self): + self._do_test( + Float(precision=8, asdecimal=True), + [15.7563, decimal.Decimal("15.7563"), None], + [decimal.Decimal("15.7563"), None], + ) + + def test_float_as_float(self): + self._do_test( + Float(precision=8), + [15.7563, decimal.Decimal("15.7563")], + [15.7563], + filter_=lambda n: n is not None and round(n, 5) or None + ) + + @testing.requires.precision_numerics_general + def test_precision_decimal(self): + numbers = set([ + decimal.Decimal("54.234246451650"), + decimal.Decimal("0.004354"), + decimal.Decimal("900.0"), + ]) + + self._do_test( + Numeric(precision=18, scale=12), + numbers, + numbers, + ) + + @testing.requires.precision_numerics_enotation_large + def test_enotation_decimal(self): + """test exceedingly small decimals. + + Decimal reports values with E notation when the exponent + is greater than 6. + + """ + + numbers = set([ + decimal.Decimal('1E-2'), + decimal.Decimal('1E-3'), + decimal.Decimal('1E-4'), + decimal.Decimal('1E-5'), + decimal.Decimal('1E-6'), + decimal.Decimal('1E-7'), + decimal.Decimal('1E-8'), + decimal.Decimal("0.01000005940696"), + decimal.Decimal("0.00000005940696"), + decimal.Decimal("0.00000000000696"), + decimal.Decimal("0.70000000000696"), + decimal.Decimal("696E-12"), + ]) + self._do_test( + Numeric(precision=18, scale=14), + numbers, + numbers + ) + + @testing.requires.precision_numerics_enotation_large + def test_enotation_decimal_large(self): + """test exceedingly large decimals. + + """ + + numbers = set([ + decimal.Decimal('4E+8'), + decimal.Decimal("5748E+15"), + decimal.Decimal('1.521E+15'), + decimal.Decimal('00000000000000.1E+12'), + ]) + self._do_test( + Numeric(precision=25, scale=2), + numbers, + numbers + ) + + @testing.requires.precision_numerics_many_significant_digits + def test_many_significant_digits(self): + numbers = set([ + decimal.Decimal("31943874831932418390.01"), + decimal.Decimal("319438950232418390.273596"), + decimal.Decimal("87673.594069654243"), + ]) + self._do_test( + Numeric(precision=38, scale=12), + numbers, + numbers + ) + + @testing.requires.precision_numerics_retains_significant_digits + def test_numeric_no_decimal(self): + numbers = set([ + decimal.Decimal("1.000") + ]) + self._do_test( + Numeric(precision=5, scale=3), + numbers, + numbers, + check_scale=True + ) + + __all__ = ('UnicodeVarcharTest', 'UnicodeTextTest', 'DateTest', 'DateTimeTest', 'TextTest', + 'NumericTest', 'DateTimeHistoricTest', 'DateTimeCoercedToDateTimeTest', 'TimeMicrosecondsTest', 'TimeTest', 'DateTimeMicrosecondsTest', 'DateHistoricTest', 'StringTest') diff --git a/test/requirements.py b/test/requirements.py index 8dde55d6af..738429158d 100644 --- a/test/requirements.py +++ b/test/requirements.py @@ -465,6 +465,61 @@ class DefaultRequirements(SuiteRequirements): return skip_if(['mssql', 'mysql', 'firebird', '+zxjdbc', 'oracle', 'sybase']) + @property + def precision_numerics_general(self): + """target backend has general support for moderately high-precision + numerics.""" + return fails_if('mssql+pymssql', 'FIXME: improve pymssql dec handling') + + @property + def precision_numerics_enotation_small(self): + """target backend supports Decimal() objects using E notation + to represent very small values.""" + return fails_if('mssql+pymssql', 'FIXME: improve pymssql dec handling') + + @property + def precision_numerics_enotation_large(self): + """target backend supports Decimal() objects using E notation + to represent very large values.""" + + return fails_if( + ("sybase+pyodbc", None, None, + "Don't know how do get these values through FreeTDS + Sybase"), + ("firebird", None, None, "Precision must be from 1 to 18"), + ) + + @property + def precision_numerics_many_significant_digits(self): + """target backend supports values with many digits on both sides, + such as 319438950232418390.273596, 87673.594069654243 + + """ + return fails_if( + [('sqlite', None, None, 'TODO'), + ("firebird", None, None, "Precision must be from 1 to 18"), + ("sybase+pysybase", None, None, "TODO"), + ('mssql+pymssql', None, None, 'FIXME: improve pymssql dec handling')] + ) + + @property + def precision_numerics_retains_significant_digits(self): + """A precision numeric type will return empty significant digits, + i.e. a value such as 10.000 will come back in Decimal form with + the .000 maintained.""" + + return fails_if( + [ + ('oracle', None, None, + "this may be a bug due to the difficulty in handling " + "oracle precision numerics"), + ('postgresql+pg8000', None, None, + "pg-8000 does native decimal but truncates the decimals."), + ("firebird", None, None, + "database and/or driver truncates decimal places.") + ] + ) + + @property def python2(self): return skip_if( diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 8987743d4a..0e6f1b5f03 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -1326,168 +1326,6 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): -class NumericTest(fixtures.TestBase): - def setup(self): - global metadata - metadata = MetaData(testing.db) - - 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, check_scale=False): - t = Table('t', metadata, Column('x', type_)) - t.create() - t.insert().execute([{'x':x} for x in input_]) - - result = set([row[0] for row in t.select().execute()]) - output = set(output) - if filter_: - result = set(filter_(x) for x in result) - output = set(filter_(x) for x in output) - #print result - #print output - eq_(result, output) - if check_scale: - eq_( - [str(x) for x in result], - [str(x) for x in output], - ) - - def test_numeric_as_decimal(self): - self._do_test( - Numeric(precision=8, scale=4), - [15.7563, decimal.Decimal("15.7563"), None], - [decimal.Decimal("15.7563"), None], - ) - - def test_numeric_as_float(self): - if testing.against("oracle+cx_oracle"): - filter_ = lambda n:n is not None and round(n, 5) or None - else: - filter_ = None - - self._do_test( - Numeric(precision=8, scale=4, asdecimal=False), - [15.7563, decimal.Decimal("15.7563"), None], - [15.7563, None], - filter_ = filter_ - ) - - def test_float_as_decimal(self): - self._do_test( - Float(precision=8, asdecimal=True), - [15.7563, decimal.Decimal("15.7563"), None], - [decimal.Decimal("15.7563"), None], - filter_ = lambda n:n is not None and round(n, 5) or None - ) - - def test_float_as_float(self): - self._do_test( - Float(precision=8), - [15.7563, decimal.Decimal("15.7563")], - [15.7563], - filter_ = lambda n:n is not None and round(n, 5) or None - ) - - @testing.fails_on('mssql+pymssql', 'FIXME: improve pymssql dec handling') - def test_precision_decimal(self): - numbers = set([ - decimal.Decimal("54.234246451650"), - decimal.Decimal("0.004354"), - decimal.Decimal("900.0"), - ]) - - self._do_test( - Numeric(precision=18, scale=12), - numbers, - numbers, - ) - - @testing.fails_on('mssql+pymssql', 'FIXME: improve pymssql dec handling') - def test_enotation_decimal(self): - """test exceedingly small decimals. - - Decimal reports values with E notation when the exponent - is greater than 6. - - """ - - numbers = set([ - decimal.Decimal('1E-2'), - decimal.Decimal('1E-3'), - decimal.Decimal('1E-4'), - decimal.Decimal('1E-5'), - decimal.Decimal('1E-6'), - decimal.Decimal('1E-7'), - decimal.Decimal('1E-8'), - decimal.Decimal("0.01000005940696"), - decimal.Decimal("0.00000005940696"), - decimal.Decimal("0.00000000000696"), - decimal.Decimal("0.70000000000696"), - decimal.Decimal("696E-12"), - ]) - self._do_test( - Numeric(precision=18, scale=14), - numbers, - numbers - ) - - @testing.fails_on("sybase+pyodbc", - "Don't know how do get these values through FreeTDS + Sybase") - @testing.fails_on("firebird", "Precision must be from 1 to 18") - def test_enotation_decimal_large(self): - """test exceedingly large decimals. - - """ - - numbers = set([ - decimal.Decimal('4E+8'), - decimal.Decimal("5748E+15"), - decimal.Decimal('1.521E+15'), - decimal.Decimal('00000000000000.1E+12'), - ]) - self._do_test( - Numeric(precision=25, scale=2), - numbers, - numbers - ) - - @testing.fails_on('sqlite', 'TODO') - @testing.fails_on("firebird", "Precision must be from 1 to 18") - @testing.fails_on("sybase+pysybase", "TODO") - @testing.fails_on('mssql+pymssql', 'FIXME: improve pymssql dec handling') - def test_many_significant_digits(self): - numbers = set([ - decimal.Decimal("31943874831932418390.01"), - decimal.Decimal("319438950232418390.273596"), - decimal.Decimal("87673.594069654243"), - ]) - self._do_test( - Numeric(precision=38, scale=12), - numbers, - numbers - ) - - @testing.fails_on('oracle+cx_oracle', - "this may be a bug due to the difficulty in handling " - "oracle precision numerics" - ) - @testing.fails_on('postgresql+pg8000', - "pg-8000 does native decimal but truncates the decimals.") - @testing.fails_on("firebird", - "database and/or driver truncates decimal places." - ) - def test_numeric_no_decimal(self): - numbers = set([ - decimal.Decimal("1.000") - ]) - self._do_test( - Numeric(precision=5, scale=3), - numbers, - numbers, - check_scale=True - ) class NumericRawSQLTest(fixtures.TestBase): """Test what DBAPIs and dialects return without any typing