self.config.db.dialect.sequences_optional
], "no sequence support, or sequences not optional")
+
+
+
+
@property
def reflects_pk_names(self):
return exclusions.closed()
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() "
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
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')
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(
-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