From: Federico Caselli Date: Wed, 26 Apr 2023 19:40:38 +0000 (+0200) Subject: Ensure float are not implemented as numeric X-Git-Tag: rel_2_0_13~6^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ff821e57c960f095ab2988a0f892b3127374f498;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Ensure float are not implemented as numeric Fixed the base class for dialect-specific float/double types; Oracle :class:`_oracle.BINARY_DOUBLE` now subclasses :class:`_sqltypes.Double`, and internal types for :class:`_sqltypes.Float` for asyncpg and pg8000 now correctly subclass :class:`_sqltypes.Float`. Added suite tests to ensure that floating point types, such as class:`_types.Float` and :class:`_types.Double` are not resolved as class:`_types.Numeric` in the dialect, since it may not compatible in all cases, such as when casting a value. Change-Id: I20b814e8e029d57921d9728a55f2570f74c35c87 --- diff --git a/doc/build/changelog/unreleased_20/suite_float_tests.rst b/doc/build/changelog/unreleased_20/suite_float_tests.rst new file mode 100644 index 0000000000..06e7fcd55b --- /dev/null +++ b/doc/build/changelog/unreleased_20/suite_float_tests.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: bug, sql + + Fixed the base class for dialect-specific float/double types; Oracle + :class:`_oracle.BINARY_DOUBLE` now subclasses :class:`_sqltypes.Double`, + and internal types for :class:`_sqltypes.Float` for asyncpg and pg8000 now + correctly subclass :class:`_sqltypes.Float`. diff --git a/lib/sqlalchemy/dialects/oracle/types.py b/lib/sqlalchemy/dialects/oracle/types.py index 1c252ab897..62028c7673 100644 --- a/lib/sqlalchemy/dialects/oracle/types.py +++ b/lib/sqlalchemy/dialects/oracle/types.py @@ -97,7 +97,7 @@ class FLOAT(sqltypes.FLOAT): self.binary_precision = binary_precision -class BINARY_DOUBLE(sqltypes.Float): +class BINARY_DOUBLE(sqltypes.Double): __visit_name__ = "BINARY_DOUBLE" diff --git a/lib/sqlalchemy/dialects/postgresql/asyncpg.py b/lib/sqlalchemy/dialects/postgresql/asyncpg.py index c879205e4a..a25502b907 100644 --- a/lib/sqlalchemy/dialects/postgresql/asyncpg.py +++ b/lib/sqlalchemy/dialects/postgresql/asyncpg.py @@ -322,7 +322,7 @@ class AsyncpgNumeric(sqltypes.Numeric): ) -class AsyncpgFloat(AsyncpgNumeric): +class AsyncpgFloat(AsyncpgNumeric, sqltypes.Float): __visit_name__ = "float" render_bind_cast = True diff --git a/lib/sqlalchemy/dialects/postgresql/pg8000.py b/lib/sqlalchemy/dialects/postgresql/pg8000.py index 3f01b00e85..a32d375c7b 100644 --- a/lib/sqlalchemy/dialects/postgresql/pg8000.py +++ b/lib/sqlalchemy/dialects/postgresql/pg8000.py @@ -148,7 +148,7 @@ class _PGNumeric(sqltypes.Numeric): ) -class _PGFloat(_PGNumeric): +class _PGFloat(_PGNumeric, sqltypes.Float): __visit_name__ = "float" render_bind_cast = True diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index b59cce3748..c1d6a14aaa 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -1240,6 +1240,12 @@ class SuiteRequirements(Requirements): return exclusions.open() + @property + def float_is_numeric(self): + """target backend uses Numeric for Float/Dual""" + + return exclusions.open() + @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 72f1e8c10f..92781cc1b3 100644 --- a/lib/sqlalchemy/testing/suite/test_types.py +++ b/lib/sqlalchemy/testing/suite/test_types.py @@ -13,6 +13,7 @@ from .. import fixtures from .. import mock from ..assertions import eq_ from ..assertions import is_ +from ..assertions import ne_ from ..config import requirements from ..schema import Column from ..schema import Table @@ -47,6 +48,7 @@ from ... import UUID from ... import Uuid from ...orm import declarative_base from ...orm import Session +from ...sql import sqltypes from ...sql.sqltypes import LargeBinary from ...sql.sqltypes import PickleType @@ -1090,6 +1092,15 @@ class NumericTest(_LiteralRoundTripFixture, fixtures.TestBase): Numeric(precision=5, scale=3), numbers, numbers, check_scale=True ) + @testing.combinations(sqltypes.Float, sqltypes.Double, argnames="cls_") + @testing.requires.float_is_numeric + def test_float_is_not_numeric(self, connection, cls_): + target_type = cls_().dialect_impl(connection.dialect) + numeric_type = sqltypes.Numeric().dialect_impl(connection.dialect) + + ne_(target_type.__visit_name__, numeric_type.__visit_name__) + ne_(target_type.__class__, numeric_type.__class__) + class BooleanTest(_LiteralRoundTripFixture, fixtures.TablesTest): __backend__ = True diff --git a/test/requirements.py b/test/requirements.py index 3c72cd07df..ae72002382 100644 --- a/test/requirements.py +++ b/test/requirements.py @@ -1446,6 +1446,10 @@ 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()