Skip to content

Commit 311e142

Browse files
committed
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
1 parent 8ef2667 commit 311e142

19 files changed

Lines changed: 211 additions & 102 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.. change::
2+
:tags: change, sql
3+
:tickets: 5252
4+
5+
the :class:`.Numeric` and :class:`.Float` SQL types have been separated out
6+
so that :class:`.Float` no longer inherits from :class:`.Numeric`; instead,
7+
they both extend from a common mixin :class:`.NumericCommon`. This
8+
corrects for some architectural shortcomings where numeric and float types
9+
are typically separate, and establishes more consistency with
10+
:class:`.Integer` also being a distinct type. The change should not have
11+
any end-user implications except for code that may be using
12+
``isinstance()`` to test for the :class:`.Numeric` datatype; third party
13+
dialects which rely upon specific implementation types for numeric and/or
14+
float may also require adjustment to maintain compatibility.

doc/build/core/type_basics.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ type is emitted in ``CREATE TABLE``, such as ``VARCHAR`` see
217217
.. autoclass:: Numeric
218218
:members:
219219

220+
.. autoclass:: NumericCommon
221+
:members:
222+
220223
.. autoclass:: PickleType
221224
:members:
222225

lib/sqlalchemy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
from .types import NCHAR as NCHAR
247247
from .types import NUMERIC as NUMERIC
248248
from .types import Numeric as Numeric
249+
from .types import NumericCommon as NumericCommon
249250
from .types import NVARCHAR as NVARCHAR
250251
from .types import PickleType as PickleType
251252
from .types import REAL as REAL

lib/sqlalchemy/dialects/mssql/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,7 +2440,7 @@ def _render_json_extract_from_binary(self, binary, operator, **kw):
24402440
self.process(binary.left, **kw),
24412441
self.process(binary.right, **kw),
24422442
)
2443-
elif binary.type._type_affinity is sqltypes.Numeric:
2443+
elif binary.type._type_affinity in (sqltypes.Numeric, sqltypes.Float):
24442444
type_expression = "ELSE CAST(JSON_VALUE(%s, %s) AS %s)" % (
24452445
self.process(binary.left, **kw),
24462446
self.process(binary.right, **kw),
@@ -3710,7 +3710,7 @@ def get_columns(self, connection, tablename, dbname, owner, schema, **kw):
37103710
)
37113711
coltype = sqltypes.NULLTYPE
37123712
else:
3713-
if issubclass(coltype, sqltypes.Numeric):
3713+
if issubclass(coltype, sqltypes.NumericCommon):
37143714
kwargs["precision"] = numericprec
37153715

37163716
if not issubclass(coltype, sqltypes.Float):

lib/sqlalchemy/dialects/mysql/base.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ class MyClass(Base):
10171017
from .types import _FloatType
10181018
from .types import _IntegerType
10191019
from .types import _MatchType
1020+
from .types import _NumericCommonType
10201021
from .types import _NumericType
10211022
from .types import _StringType
10221023
from .types import BIGINT
@@ -1114,6 +1115,7 @@ class MyClass(Base):
11141115

11151116
colspecs = {
11161117
_IntegerType: _IntegerType,
1118+
_NumericCommonType: _NumericCommonType,
11171119
_NumericType: _NumericType,
11181120
_FloatType: _FloatType,
11191121
sqltypes.Numeric: NUMERIC,
@@ -1277,7 +1279,7 @@ def _render_json_extract_from_binary(self, binary, operator, **kw):
12771279
self.process(binary.right, **kw),
12781280
)
12791281
)
1280-
elif binary.type._type_affinity is sqltypes.Numeric:
1282+
elif binary.type._type_affinity in (sqltypes.Numeric, sqltypes.Float):
12811283
if (
12821284
binary.type.scale is not None
12831285
and binary.type.precision is not None
@@ -2145,7 +2147,7 @@ def attr(name):
21452147
)
21462148

21472149
def _mysql_type(self, type_):
2148-
return isinstance(type_, (_StringType, _NumericType))
2150+
return isinstance(type_, (_StringType, _NumericCommonType))
21492151

21502152
def visit_NUMERIC(self, type_, **kw):
21512153
if type_.precision is None:

lib/sqlalchemy/dialects/mysql/types.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ...sql import sqltypes
1515

1616

17-
class _NumericType:
17+
class _NumericCommonType:
1818
"""Base for MySQL numeric types.
1919
2020
This is the base both for NUMERIC as well as INTEGER, hence
@@ -27,13 +27,18 @@ def __init__(self, unsigned=False, zerofill=False, **kw):
2727
self.zerofill = zerofill
2828
super().__init__(**kw)
2929

30+
31+
class _NumericType(_NumericCommonType, sqltypes.Numeric):
32+
3033
def __repr__(self):
3134
return util.generic_repr(
32-
self, to_inspect=[_NumericType, sqltypes.Numeric]
35+
self,
36+
to_inspect=[_NumericType, _NumericCommonType, sqltypes.Numeric],
3337
)
3438

3539

36-
class _FloatType(_NumericType, sqltypes.Float):
40+
class _FloatType(_NumericCommonType, sqltypes.Float):
41+
3742
def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
3843
if isinstance(self, (REAL, DOUBLE)) and (
3944
(precision is None and scale is not None)
@@ -48,18 +53,19 @@ def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
4853

4954
def __repr__(self):
5055
return util.generic_repr(
51-
self, to_inspect=[_FloatType, _NumericType, sqltypes.Float]
56+
self, to_inspect=[_FloatType, _NumericCommonType, sqltypes.Float]
5257
)
5358

5459

55-
class _IntegerType(_NumericType, sqltypes.Integer):
60+
class _IntegerType(_NumericCommonType, sqltypes.Integer):
5661
def __init__(self, display_width=None, **kw):
5762
self.display_width = display_width
5863
super().__init__(**kw)
5964

6065
def __repr__(self):
6166
return util.generic_repr(
62-
self, to_inspect=[_IntegerType, _NumericType, sqltypes.Integer]
67+
self,
68+
to_inspect=[_IntegerType, _NumericCommonType, sqltypes.Integer],
6369
)
6470

6571

lib/sqlalchemy/dialects/oracle/cx_oracle.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ def handler(cursor, name, default_type, size, precision, scale):
499499
return handler
500500

501501

502-
class _OracleNumeric(sqltypes.Numeric):
502+
class _OracleNumericCommon(sqltypes.NumericCommon, sqltypes.TypeEngine):
503503
is_number = False
504504

505505
def bind_processor(self, dialect):
@@ -575,12 +575,20 @@ def handler(cursor, name, default_type, size, precision, scale):
575575
return handler
576576

577577

578+
class _OracleNumeric(_OracleNumericCommon, sqltypes.Numeric):
579+
pass
580+
581+
582+
class _OracleFloat(_OracleNumericCommon, sqltypes.Float):
583+
pass
584+
585+
578586
class _OracleUUID(sqltypes.Uuid):
579587
def get_dbapi_type(self, dbapi):
580588
return dbapi.STRING
581589

582590

583-
class _OracleBinaryFloat(_OracleNumeric):
591+
class _OracleBinaryFloat(_OracleNumericCommon):
584592
def get_dbapi_type(self, dbapi):
585593
return dbapi.NATIVE_FLOAT
586594

@@ -593,7 +601,7 @@ class _OracleBINARY_DOUBLE(_OracleBinaryFloat, oracle.BINARY_DOUBLE):
593601
pass
594602

595603

596-
class _OracleNUMBER(_OracleNumeric):
604+
class _OracleNUMBER(_OracleNumericCommon, sqltypes.Numeric):
597605
is_number = True
598606

599607

@@ -852,7 +860,7 @@ def _generate_out_parameter_vars(self):
852860
arraysize=len_params,
853861
)
854862
elif (
855-
isinstance(type_impl, _OracleNumeric)
863+
isinstance(type_impl, _OracleNumericCommon)
856864
and type_impl.asdecimal
857865
):
858866
out_parameters[name] = self.cursor.var(
@@ -1017,7 +1025,7 @@ class OracleDialect_cx_oracle(OracleDialect):
10171025
{
10181026
sqltypes.TIMESTAMP: _CXOracleTIMESTAMP,
10191027
sqltypes.Numeric: _OracleNumeric,
1020-
sqltypes.Float: _OracleNumeric,
1028+
sqltypes.Float: _OracleFloat,
10211029
oracle.BINARY_FLOAT: _OracleBINARY_FLOAT,
10221030
oracle.BINARY_DOUBLE: _OracleBINARY_DOUBLE,
10231031
sqltypes.Integer: _OracleInteger,

lib/sqlalchemy/dialects/postgresql/_psycopg_common.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
_server_side_id = util.counter()
2828

2929

30-
class _PsycopgNumeric(sqltypes.Numeric):
30+
class _PsycopgNumericCommon(sqltypes.NumericCommon):
3131
def bind_processor(self, dialect):
3232
return None
3333

@@ -56,8 +56,12 @@ def result_processor(self, dialect, coltype):
5656
)
5757

5858

59-
class _PsycopgFloat(_PsycopgNumeric):
60-
__visit_name__ = "float"
59+
class _PsycopgNumeric(_PsycopgNumericCommon, sqltypes.Numeric):
60+
pass
61+
62+
63+
class _PsycopgFloat(_PsycopgNumericCommon, sqltypes.Float):
64+
pass
6165

6266

6367
class _PsycopgHStore(HSTORE):

lib/sqlalchemy/dialects/postgresql/asyncpg.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def process(value):
314314
return process
315315

316316

317-
class AsyncpgNumeric(sqltypes.Numeric):
317+
class _AsyncpgNumericCommon(sqltypes.NumericCommon):
318318
render_bind_cast = True
319319

320320
def bind_processor(self, dialect):
@@ -345,9 +345,12 @@ def result_processor(self, dialect, coltype):
345345
)
346346

347347

348-
class AsyncpgFloat(AsyncpgNumeric, sqltypes.Float):
349-
__visit_name__ = "float"
350-
render_bind_cast = True
348+
class AsyncpgNumeric(_AsyncpgNumericCommon, sqltypes.Numeric):
349+
pass
350+
351+
352+
class AsyncpgFloat(_AsyncpgNumericCommon, sqltypes.Float):
353+
pass
351354

352355

353356
class AsyncpgREGCLASS(REGCLASS):

lib/sqlalchemy/dialects/postgresql/pg8000.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class _PGString(sqltypes.String):
122122
render_bind_cast = True
123123

124124

125-
class _PGNumeric(sqltypes.Numeric):
125+
class _PGNumericCommon(sqltypes.NumericCommon):
126126
render_bind_cast = True
127127

128128
def result_processor(self, dialect, coltype):
@@ -150,9 +150,12 @@ def result_processor(self, dialect, coltype):
150150
)
151151

152152

153-
class _PGFloat(_PGNumeric, sqltypes.Float):
154-
__visit_name__ = "float"
155-
render_bind_cast = True
153+
class _PGNumeric(_PGNumericCommon, sqltypes.Numeric):
154+
pass
155+
156+
157+
class _PGFloat(_PGNumericCommon, sqltypes.Float):
158+
pass
156159

157160

158161
class _PGNumericNoBind(_PGNumeric):

0 commit comments

Comments
 (0)