From: Mike Bayer Date: Mon, 13 Apr 2020 14:25:27 +0000 (-0400) Subject: Separate Numeric and Float X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=311e142ea6eef34106830bce520c6be8edd1e5da;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Separate Numeric and Float the :class:`.Numeric` and :class:`.Float` SQL types have been separated out so that :class:`.Float` no longer inherits from :class:`.Numeric`; instead, they both extend from a common mixin :class:`.NumericCommon`. This corrects for some architectural shortcomings where numeric and float types are typically separate, and establishes more consistency with :class:`.Integer` also being a distinct type. The change should not have any end-user implications except for code that may be using ``isinstance()`` to test for the :class:`.Numeric` datatype; third party dialects which rely upon specific implementation types for numeric and/or float may also require adjustment to maintain compatibility. Fixes: #5252 Change-Id: Iadc841340b3d97e3eb5f7e63f0a0cc3cb4e30f74 --- diff --git a/doc/build/changelog/unreleased_21/5252.rst b/doc/build/changelog/unreleased_21/5252.rst new file mode 100644 index 0000000000..79d77b4623 --- /dev/null +++ b/doc/build/changelog/unreleased_21/5252.rst @@ -0,0 +1,14 @@ +.. change:: + :tags: change, sql + :tickets: 5252 + + the :class:`.Numeric` and :class:`.Float` SQL types have been separated out + so that :class:`.Float` no longer inherits from :class:`.Numeric`; instead, + they both extend from a common mixin :class:`.NumericCommon`. This + corrects for some architectural shortcomings where numeric and float types + are typically separate, and establishes more consistency with + :class:`.Integer` also being a distinct type. The change should not have + any end-user implications except for code that may be using + ``isinstance()`` to test for the :class:`.Numeric` datatype; third party + dialects which rely upon specific implementation types for numeric and/or + float may also require adjustment to maintain compatibility. diff --git a/doc/build/core/type_basics.rst b/doc/build/core/type_basics.rst index 817bca601a..c12dd99441 100644 --- a/doc/build/core/type_basics.rst +++ b/doc/build/core/type_basics.rst @@ -217,6 +217,9 @@ type is emitted in ``CREATE TABLE``, such as ``VARCHAR`` see .. autoclass:: Numeric :members: +.. autoclass:: NumericCommon + :members: + .. autoclass:: PickleType :members: diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py index 2672fed514..c3ab0b6d5d 100644 --- a/lib/sqlalchemy/__init__.py +++ b/lib/sqlalchemy/__init__.py @@ -246,6 +246,7 @@ from .types import LargeBinary as LargeBinary from .types import NCHAR as NCHAR from .types import NUMERIC as NUMERIC from .types import Numeric as Numeric +from .types import NumericCommon as NumericCommon from .types import NVARCHAR as NVARCHAR from .types import PickleType as PickleType from .types import REAL as REAL diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index a617acf3de..f03bddbdf7 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -2440,7 +2440,7 @@ class MSSQLCompiler(compiler.SQLCompiler): self.process(binary.left, **kw), self.process(binary.right, **kw), ) - elif binary.type._type_affinity is sqltypes.Numeric: + elif binary.type._type_affinity in (sqltypes.Numeric, sqltypes.Float): type_expression = "ELSE CAST(JSON_VALUE(%s, %s) AS %s)" % ( self.process(binary.left, **kw), self.process(binary.right, **kw), @@ -3710,7 +3710,7 @@ where ) coltype = sqltypes.NULLTYPE else: - if issubclass(coltype, sqltypes.Numeric): + if issubclass(coltype, sqltypes.NumericCommon): kwargs["precision"] = numericprec if not issubclass(coltype, sqltypes.Float): diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index c834495759..bdaccb72ad 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1017,6 +1017,7 @@ from .reserved_words import RESERVED_WORDS_MYSQL from .types import _FloatType from .types import _IntegerType from .types import _MatchType +from .types import _NumericCommonType from .types import _NumericType from .types import _StringType from .types import BIGINT @@ -1114,6 +1115,7 @@ MSInteger = INTEGER colspecs = { _IntegerType: _IntegerType, + _NumericCommonType: _NumericCommonType, _NumericType: _NumericType, _FloatType: _FloatType, sqltypes.Numeric: NUMERIC, @@ -1277,7 +1279,7 @@ class MySQLCompiler(compiler.SQLCompiler): self.process(binary.right, **kw), ) ) - elif binary.type._type_affinity is sqltypes.Numeric: + elif binary.type._type_affinity in (sqltypes.Numeric, sqltypes.Float): if ( binary.type.scale is not None and binary.type.precision is not None @@ -2145,7 +2147,7 @@ class MySQLTypeCompiler(compiler.GenericTypeCompiler): ) def _mysql_type(self, type_): - return isinstance(type_, (_StringType, _NumericType)) + return isinstance(type_, (_StringType, _NumericCommonType)) def visit_NUMERIC(self, type_, **kw): if type_.precision is None: diff --git a/lib/sqlalchemy/dialects/mysql/types.py b/lib/sqlalchemy/dialects/mysql/types.py index 734f6ae372..cd848c5efc 100644 --- a/lib/sqlalchemy/dialects/mysql/types.py +++ b/lib/sqlalchemy/dialects/mysql/types.py @@ -14,7 +14,7 @@ from ... import util from ...sql import sqltypes -class _NumericType: +class _NumericCommonType: """Base for MySQL numeric types. This is the base both for NUMERIC as well as INTEGER, hence @@ -27,13 +27,18 @@ class _NumericType: self.zerofill = zerofill super().__init__(**kw) + +class _NumericType(_NumericCommonType, sqltypes.Numeric): + def __repr__(self): return util.generic_repr( - self, to_inspect=[_NumericType, sqltypes.Numeric] + self, + to_inspect=[_NumericType, _NumericCommonType, sqltypes.Numeric], ) -class _FloatType(_NumericType, sqltypes.Float): +class _FloatType(_NumericCommonType, sqltypes.Float): + def __init__(self, precision=None, scale=None, asdecimal=True, **kw): if isinstance(self, (REAL, DOUBLE)) and ( (precision is None and scale is not None) @@ -48,18 +53,19 @@ class _FloatType(_NumericType, sqltypes.Float): def __repr__(self): return util.generic_repr( - self, to_inspect=[_FloatType, _NumericType, sqltypes.Float] + self, to_inspect=[_FloatType, _NumericCommonType, sqltypes.Float] ) -class _IntegerType(_NumericType, sqltypes.Integer): +class _IntegerType(_NumericCommonType, sqltypes.Integer): def __init__(self, display_width=None, **kw): self.display_width = display_width super().__init__(**kw) def __repr__(self): return util.generic_repr( - self, to_inspect=[_IntegerType, _NumericType, sqltypes.Integer] + self, + to_inspect=[_IntegerType, _NumericCommonType, sqltypes.Integer], ) diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index babb916a60..081f3595cd 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -499,7 +499,7 @@ class _OracleInteger(sqltypes.Integer): return handler -class _OracleNumeric(sqltypes.Numeric): +class _OracleNumericCommon(sqltypes.NumericCommon, sqltypes.TypeEngine): is_number = False def bind_processor(self, dialect): @@ -575,12 +575,20 @@ class _OracleNumeric(sqltypes.Numeric): return handler +class _OracleNumeric(_OracleNumericCommon, sqltypes.Numeric): + pass + + +class _OracleFloat(_OracleNumericCommon, sqltypes.Float): + pass + + class _OracleUUID(sqltypes.Uuid): def get_dbapi_type(self, dbapi): return dbapi.STRING -class _OracleBinaryFloat(_OracleNumeric): +class _OracleBinaryFloat(_OracleNumericCommon): def get_dbapi_type(self, dbapi): return dbapi.NATIVE_FLOAT @@ -593,7 +601,7 @@ class _OracleBINARY_DOUBLE(_OracleBinaryFloat, oracle.BINARY_DOUBLE): pass -class _OracleNUMBER(_OracleNumeric): +class _OracleNUMBER(_OracleNumericCommon, sqltypes.Numeric): is_number = True @@ -852,7 +860,7 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext): arraysize=len_params, ) elif ( - isinstance(type_impl, _OracleNumeric) + isinstance(type_impl, _OracleNumericCommon) and type_impl.asdecimal ): out_parameters[name] = self.cursor.var( @@ -1017,7 +1025,7 @@ class OracleDialect_cx_oracle(OracleDialect): { sqltypes.TIMESTAMP: _CXOracleTIMESTAMP, sqltypes.Numeric: _OracleNumeric, - sqltypes.Float: _OracleNumeric, + sqltypes.Float: _OracleFloat, oracle.BINARY_FLOAT: _OracleBINARY_FLOAT, oracle.BINARY_DOUBLE: _OracleBINARY_DOUBLE, sqltypes.Integer: _OracleInteger, diff --git a/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py b/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py index 46858c9f22..16d569b59b 100644 --- a/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py +++ b/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py @@ -27,7 +27,7 @@ from ...engine import processors _server_side_id = util.counter() -class _PsycopgNumeric(sqltypes.Numeric): +class _PsycopgNumericCommon(sqltypes.NumericCommon): def bind_processor(self, dialect): return None @@ -56,8 +56,12 @@ class _PsycopgNumeric(sqltypes.Numeric): ) -class _PsycopgFloat(_PsycopgNumeric): - __visit_name__ = "float" +class _PsycopgNumeric(_PsycopgNumericCommon, sqltypes.Numeric): + pass + + +class _PsycopgFloat(_PsycopgNumericCommon, sqltypes.Float): + pass class _PsycopgHStore(HSTORE): diff --git a/lib/sqlalchemy/dialects/postgresql/asyncpg.py b/lib/sqlalchemy/dialects/postgresql/asyncpg.py index a362c616e1..c7c20ee029 100644 --- a/lib/sqlalchemy/dialects/postgresql/asyncpg.py +++ b/lib/sqlalchemy/dialects/postgresql/asyncpg.py @@ -314,7 +314,7 @@ class AsyncpgJSONPathType(json.JSONPathType): return process -class AsyncpgNumeric(sqltypes.Numeric): +class _AsyncpgNumericCommon(sqltypes.NumericCommon): render_bind_cast = True def bind_processor(self, dialect): @@ -345,9 +345,12 @@ class AsyncpgNumeric(sqltypes.Numeric): ) -class AsyncpgFloat(AsyncpgNumeric, sqltypes.Float): - __visit_name__ = "float" - render_bind_cast = True +class AsyncpgNumeric(_AsyncpgNumericCommon, sqltypes.Numeric): + pass + + +class AsyncpgFloat(_AsyncpgNumericCommon, sqltypes.Float): + pass class AsyncpgREGCLASS(REGCLASS): diff --git a/lib/sqlalchemy/dialects/postgresql/pg8000.py b/lib/sqlalchemy/dialects/postgresql/pg8000.py index 0151be0253..1f3b3c250d 100644 --- a/lib/sqlalchemy/dialects/postgresql/pg8000.py +++ b/lib/sqlalchemy/dialects/postgresql/pg8000.py @@ -122,7 +122,7 @@ class _PGString(sqltypes.String): render_bind_cast = True -class _PGNumeric(sqltypes.Numeric): +class _PGNumericCommon(sqltypes.NumericCommon): render_bind_cast = True def result_processor(self, dialect, coltype): @@ -150,9 +150,12 @@ class _PGNumeric(sqltypes.Numeric): ) -class _PGFloat(_PGNumeric, sqltypes.Float): - __visit_name__ = "float" - render_bind_cast = True +class _PGNumeric(_PGNumericCommon, sqltypes.Numeric): + pass + + +class _PGFloat(_PGNumericCommon, sqltypes.Float): + pass class _PGNumericNoBind(_PGNumeric): diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 647d38e640..dacbfc38af 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -3106,7 +3106,7 @@ class SQLCompiler(Compiled): ( binary.right.type if binary.right.type._type_affinity - is sqltypes.Numeric + in (sqltypes.Numeric, sqltypes.Float) else sqltypes.Numeric() ), ), diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 95d94a27de..deb98311cb 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -21,6 +21,7 @@ from typing import Any from typing import Callable from typing import cast from typing import Dict +from typing import Generic from typing import List from typing import Optional from typing import overload @@ -363,15 +364,25 @@ class Integer(HasExpressionLookup, TypeEngine[int]): Date: Date, Integer: self.__class__, Numeric: Numeric, + Float: Float, }, operators.mul: { Interval: Interval, Integer: self.__class__, Numeric: Numeric, + Float: Float, + }, + operators.truediv: { + Integer: Numeric, + Numeric: Numeric, + Float: Float, }, - operators.truediv: {Integer: Numeric, Numeric: Numeric}, operators.floordiv: {Integer: self.__class__, Numeric: Numeric}, - operators.sub: {Integer: self.__class__, Numeric: Numeric}, + operators.sub: { + Integer: self.__class__, + Numeric: Numeric, + Float: Float, + }, } @@ -400,7 +411,93 @@ class BigInteger(Integer): _N = TypeVar("_N", bound=Union[decimal.Decimal, float]) -class Numeric(HasExpressionLookup, TypeEngine[_N]): +class NumericCommon(HasExpressionLookup, TypeEngineMixin, Generic[_N]): + """common mixin for the :class:`.Numeric` and :class:`.Float` types. + + + .. versionadded:: 2.1 + + """ + + _default_decimal_return_scale = 10 + + if TYPE_CHECKING: + + @util.ro_memoized_property + def _type_affinity(self) -> Type[NumericCommon[_N]]: ... + + def __init__( + self, + *, + precision: Optional[int], + scale: Optional[int], + decimal_return_scale: Optional[int], + asdecimal: bool, + ): + self.precision = precision + self.scale = scale + self.decimal_return_scale = decimal_return_scale + self.asdecimal = asdecimal + + @property + def _effective_decimal_return_scale(self): + if self.decimal_return_scale is not None: + return self.decimal_return_scale + elif getattr(self, "scale", None) is not None: + return self.scale + else: + return self._default_decimal_return_scale + + def get_dbapi_type(self, dbapi): + return dbapi.NUMBER + + def literal_processor(self, dialect): + def process(value): + return str(value) + + return process + + @property + def python_type(self): + if self.asdecimal: + return decimal.Decimal + else: + return float + + def bind_processor(self, dialect): + if dialect.supports_native_decimal: + return None + else: + return processors.to_float + + @util.memoized_property + def _expression_adaptations(self): + return { + operators.mul: { + Interval: Interval, + Numeric: self.__class__, + Float: self.__class__, + Integer: self.__class__, + }, + operators.truediv: { + Numeric: self.__class__, + Float: self.__class__, + Integer: self.__class__, + }, + operators.add: { + Numeric: self.__class__, + Float: self.__class__, + Integer: self.__class__, + }, + operators.sub: { + Numeric: self.__class__, + Float: self.__class__, + Integer: self.__class__, + }, + } + + +class Numeric(NumericCommon[_N], TypeEngine[_N]): """Base for non-integer numeric types, such as ``NUMERIC``, ``FLOAT``, ``DECIMAL``, and other variants. @@ -434,13 +531,6 @@ class Numeric(HasExpressionLookup, TypeEngine[_N]): __visit_name__ = "numeric" - if TYPE_CHECKING: - - @util.ro_memoized_property - def _type_affinity(self) -> Type[Numeric[_N]]: ... - - _default_decimal_return_scale = 10 - @overload def __init__( self: Numeric[decimal.Decimal], @@ -508,41 +598,16 @@ class Numeric(HasExpressionLookup, TypeEngine[_N]): conversion overhead. """ - self.precision = precision - self.scale = scale - self.decimal_return_scale = decimal_return_scale - self.asdecimal = asdecimal - - @property - def _effective_decimal_return_scale(self): - if self.decimal_return_scale is not None: - return self.decimal_return_scale - elif getattr(self, "scale", None) is not None: - return self.scale - else: - return self._default_decimal_return_scale - - def get_dbapi_type(self, dbapi): - return dbapi.NUMBER - - def literal_processor(self, dialect): - def process(value): - return str(value) - - return process + super().__init__( + precision=precision, + scale=scale, + decimal_return_scale=decimal_return_scale, + asdecimal=asdecimal, + ) @property - def python_type(self): - if self.asdecimal: - return decimal.Decimal - else: - return float - - def bind_processor(self, dialect): - if dialect.supports_native_decimal: - return None - else: - return processors.to_float + def _type_affinity(self): + return Numeric def result_processor(self, dialect, coltype): if self.asdecimal: @@ -565,24 +630,8 @@ class Numeric(HasExpressionLookup, TypeEngine[_N]): else: return None - @util.memoized_property - def _expression_adaptations(self): - return { - operators.mul: { - Interval: Interval, - Numeric: self.__class__, - Integer: self.__class__, - }, - operators.truediv: { - Numeric: self.__class__, - Integer: self.__class__, - }, - operators.add: {Numeric: self.__class__, Integer: self.__class__}, - operators.sub: {Numeric: self.__class__, Integer: self.__class__}, - } - -class Float(Numeric[_N]): +class Float(NumericCommon[_N], TypeEngine[_N]): """Type representing floating point types, such as ``FLOAT`` or ``REAL``. This type returns Python ``float`` objects by default, unless the @@ -669,9 +718,16 @@ class Float(Numeric[_N]): as the default for decimal_return_scale, if not otherwise specified. """ # noqa: E501 - self.precision = precision - self.asdecimal = asdecimal - self.decimal_return_scale = decimal_return_scale + super().__init__( + precision=precision, + scale=None, + asdecimal=asdecimal, + decimal_return_scale=decimal_return_scale, + ) + + @property + def _type_affinity(self): + return Float def result_processor(self, dialect, coltype): if self.asdecimal: @@ -2018,8 +2074,11 @@ class _AbstractInterval(HasExpressionLookup, TypeEngine[dt.timedelta]): Time: Time, }, operators.sub: {Interval: self.__class__}, - operators.mul: {Numeric: self.__class__}, - operators.truediv: {Numeric: self.__class__}, + operators.mul: {Numeric: self.__class__, Float: self.__class__}, + operators.truediv: { + Numeric: self.__class__, + Float: self.__class__, + }, } @util.ro_non_memoized_property diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py index 91113be9b4..d738f76893 100644 --- a/lib/sqlalchemy/testing/suite/test_reflection.py +++ b/lib/sqlalchemy/testing/suite/test_reflection.py @@ -1649,6 +1649,7 @@ class ComponentReflectionTest(ComparesTables, OneConnectionTablesTest): [ sql_types.Integer, sql_types.Numeric, + sql_types.Float, sql_types.DateTime, sql_types.Date, sql_types.Time, diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index a5bb56cf66..a1374d94f6 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -47,6 +47,7 @@ from .sql.sqltypes import NULLTYPE as NULLTYPE from .sql.sqltypes import NullType as NullType from .sql.sqltypes import NUMERIC as NUMERIC from .sql.sqltypes import Numeric as Numeric +from .sql.sqltypes import NumericCommon as NumericCommon from .sql.sqltypes import NVARCHAR as NVARCHAR from .sql.sqltypes import PickleType as PickleType from .sql.sqltypes import REAL as REAL diff --git a/test/dialect/mysql/test_reflection.py b/test/dialect/mysql/test_reflection.py index 92cf3818e2..f3210ad615 100644 --- a/test/dialect/mysql/test_reflection.py +++ b/test/dialect/mysql/test_reflection.py @@ -7,6 +7,7 @@ from sqlalchemy import DDL from sqlalchemy import DefaultClause from sqlalchemy import event from sqlalchemy import exc +from sqlalchemy import Float from sqlalchemy import ForeignKey from sqlalchemy import ForeignKeyConstraint from sqlalchemy import Index @@ -298,7 +299,7 @@ class ReflectionTest(fixtures.TestBase, AssertsCompiledSQL): col = insp.get_columns("t1")[0] if hasattr(expected, "match"): assert expected.match(col["default"]) - elif isinstance(datatype_inst, (Integer, Numeric)): + elif isinstance(datatype_inst, (Integer, Numeric, Float)): pattern = re.compile(r"\'?%s\'?" % expected) assert pattern.match(col["default"]) else: diff --git a/test/ext/test_horizontal_shard.py b/test/ext/test_horizontal_shard.py index 9aa38c9c69..8215c44e5d 100644 --- a/test/ext/test_horizontal_shard.py +++ b/test/ext/test_horizontal_shard.py @@ -6,10 +6,10 @@ from sqlalchemy import DateTime from sqlalchemy import delete from sqlalchemy import event from sqlalchemy import exc -from sqlalchemy import Float from sqlalchemy import ForeignKey from sqlalchemy import inspect from sqlalchemy import Integer +from sqlalchemy import Numeric from sqlalchemy import select from sqlalchemy import sql from sqlalchemy import String @@ -81,7 +81,7 @@ class ShardTest: metadata, Column("id", Integer, primary_key=True), Column("location_id", Integer, ForeignKey(weather_locations.c.id)), - Column("temperature", Float), + Column("temperature", Numeric(asdecimal=False)), Column("report_time", DateTime, default=datetime.datetime.now), schema=cls.schema, ) diff --git a/test/requirements.py b/test/requirements.py index a5f4ee11ec..ce19f1bcc4 100644 --- a/test/requirements.py +++ b/test/requirements.py @@ -1488,10 +1488,6 @@ class DefaultRequirements(SuiteRequirements): def fetch_null_from_numeric(self): return skip_if(("mssql+pyodbc", None, None, "crashes due to bug #351")) - @property - def float_is_numeric(self): - return exclusions.fails_if(["oracle"]) - @property def duplicate_key_raises_integrity_error(self): return exclusions.open() diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index c9c6c55c02..b7a2dedbf1 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -2136,13 +2136,19 @@ class PKAutoIncrementTest(fixtures.TestBase): ) def test_float_illegal_autoinc(self): - """test that Float is not acceptable if autoincrement=True""" + """test that Float is not acceptable if autoincrement=True + + note this changed in 2.1 with #5252 where Numeric/Float were split out + + """ t = Table("t", MetaData(), Column("a", Float, autoincrement=True)) pk = PrimaryKeyConstraint(t.c.a) t.append_constraint(pk) with expect_raises_message( - exc.ArgumentError, "Column type FLOAT with non-zero scale " + exc.ArgumentError, + "Column type FLOAT on column 't.a' is not compatible " + "with autoincrement=True", ): pk._autoincrement_column, diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py index 1804d02ca9..fbe9ba3900 100644 --- a/test/sql/test_operators.py +++ b/test/sql/test_operators.py @@ -8,6 +8,7 @@ from sqlalchemy import and_ from sqlalchemy import between from sqlalchemy import bindparam from sqlalchemy import exc +from sqlalchemy import Float from sqlalchemy import Integer from sqlalchemy import join from sqlalchemy import LargeBinary @@ -1105,7 +1106,7 @@ class JSONIndexOpTest(fixtures.TestBase, testing.AssertsCompiledSQL): return testing.combinations( ("integer", Integer), ("boolean", Boolean), - ("float", Numeric), + ("float", Float), ("string", String), )(fn)