From b9d231869d7e39decabdec12478e359c4dcb95ee Mon Sep 17 00:00:00 2001 From: zeeeeeb <5767468+zeeeeeb@users.noreply.github.com> Date: Sat, 12 Feb 2022 14:00:02 -0500 Subject: Implement generic Double and related fixed types Added :class:`.Double`, :class:`.DOUBLE`, :class:`.DOUBLE_PRECISION` datatypes to the base ``sqlalchemy.`` module namespace, for explicit use of double/double precision as well as generic "double" datatypes. Use :class:`.Double` for generic support that will resolve to DOUBLE/DOUBLE PRECISION/FLOAT as needed for different backends. Implemented DDL and reflection support for ``FLOAT`` datatypes which include an explicit "binary_precision" value. Using the Oracle-specific :class:`_oracle.FLOAT` datatype, the new parameter :paramref:`_oracle.FLOAT.binary_precision` may be specified which will render Oracle's precision for floating point types directly. This value is interpreted during reflection. Upon reflecting back a ``FLOAT`` datatype, the datatype returned is one of :class:`_types.DOUBLE_PRECISION` for a ``FLOAT`` for a precision of 126 (this is also Oracle's default precision for ``FLOAT``), :class:`_types.REAL` for a precision of 63, and :class:`_oracle.FLOAT` for a custom precision, as per Oracle documentation. As part of this change, the generic :paramref:`_sqltypes.Float.precision` value is explicitly rejected when generating DDL for Oracle, as this precision cannot be accurately converted to "binary precision"; instead, an error message encourages the use of :meth:`_sqltypes.TypeEngine.with_variant` so that Oracle's specific form of precision may be chosen exactly. This is a backwards-incompatible change in behavior, as the previous "precision" value was silently ignored for Oracle. Fixes: #5465 Closes: #7674 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/7674 Pull-request-sha: 5c68419e5aee2e27bf21a8ac9eb5950d196c77e5 Change-Id: I831f4af3ee3b23fde02e8f6393c83e23dd7cd34d --- lib/sqlalchemy/sql/compiler.py | 9 +++ lib/sqlalchemy/sql/sqltypes.py | 125 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 125 insertions(+), 9 deletions(-) (limited to 'lib/sqlalchemy/sql') diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index b140f9297..131281a16 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -4888,6 +4888,12 @@ class GenericTypeCompiler(TypeCompiler): def visit_FLOAT(self, type_, **kw): return "FLOAT" + def visit_DOUBLE(self, type_, **kw): + return "DOUBLE" + + def visit_DOUBLE_PRECISION(self, type_, **kw): + return "DOUBLE PRECISION" + def visit_REAL(self, type_, **kw): return "REAL" @@ -5006,6 +5012,9 @@ class GenericTypeCompiler(TypeCompiler): def visit_float(self, type_, **kw): return self.visit_FLOAT(type_, **kw) + def visit_double(self, type_, **kw): + return self.visit_DOUBLE(type_, **kw) + def visit_numeric(self, type_, **kw): return self.visit_NUMERIC(type_, **kw) diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 819f1dc9a..d022a24ca 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -578,7 +578,30 @@ class Float(Numeric): Construct a Float. :param precision: the numeric precision for use in DDL ``CREATE - TABLE``. + TABLE``. Backends **should** attempt to ensure this precision + indicates a number of digits for the generic + :class:`_sqltypes.Float` datatype. + + .. note:: For the Oracle backend, the + :paramref:`_sqltypes.Float.precision` parameter is not accepted + when rendering DDL, as Oracle does not support float precision + specified as a number of decimal places. Instead, use the + Oracle-specific :class:`_oracle.FLOAT` datatype and specify the + :paramref:`_oracle.FLOAT.binary_precision` parameter. This is new + in version 2.0 of SQLAlchemy. + + To create a database agnostic :class:`_types.Float` that + separately specifies binary precision for Oracle, use + :meth:`_types.TypeEngine.with_variant` as follows:: + + from sqlalchemy import Column + from sqlalchemy import Float + from sqlalchemy.dialects import oracle + + Column( + "float_data", + Float(5).with_variant(oracle.FLOAT(binary_precision=16), "oracle") + ) :param asdecimal: the same flag as that of :class:`.Numeric`, but defaults to ``False``. Note that setting this flag to ``True`` @@ -595,7 +618,7 @@ class Float(Numeric): .. versionadded:: 0.9.0 - """ + """ # noqa: E501 self.precision = precision self.asdecimal = asdecimal self.decimal_return_scale = decimal_return_scale @@ -611,6 +634,20 @@ class Float(Numeric): return None +class Double(Float): + """A type for double ``FLOAT`` floating point types. + + Typically generates a ``DOUBLE`` or ``DOUBLE_PRECISION`` in DDL, + and otherwise acts like a normal :class:`.Float` on the Python + side. + + .. versionadded:: 2.0 + + """ + + __visit_name__ = "double" + + class DateTime(_LookupExpressionAdapter, TypeEngine[dt.datetime]): """A type for ``datetime.datetime()`` objects. @@ -2769,35 +2806,93 @@ class TupleType(TypeEngine[Tuple[Any]]): class REAL(Float): - """The SQL REAL type.""" + """The SQL REAL type. + + .. seealso:: + + :class:`_types.Float` - documentation for the base type. + + """ __visit_name__ = "REAL" class FLOAT(Float): - """The SQL FLOAT type.""" + """The SQL FLOAT type. + + .. seealso:: + + :class:`_types.Float` - documentation for the base type. + + """ __visit_name__ = "FLOAT" +class DOUBLE(Double): + """The SQL DOUBLE type. + + .. versionadded:: 2.0 + + .. seealso:: + + :class:`_types.Double` - documentation for the base type. + + """ + + __visit_name__ = "DOUBLE" + + +class DOUBLE_PRECISION(Double): + """The SQL DOUBLE PRECISION type. + + .. versionadded:: 2.0 + + .. seealso:: + + :class:`_types.Double` - documentation for the base type. + + """ + + __visit_name__ = "DOUBLE_PRECISION" + + class NUMERIC(Numeric): - """The SQL NUMERIC type.""" + """The SQL NUMERIC type. + + .. seealso:: + + :class:`_types.Numeric` - documentation for the base type. + + """ __visit_name__ = "NUMERIC" class DECIMAL(Numeric): - """The SQL DECIMAL type.""" + """The SQL DECIMAL type. + + .. seealso:: + + :class:`_types.Numeric` - documentation for the base type. + + """ __visit_name__ = "DECIMAL" class INTEGER(Integer): - """The SQL INT or INTEGER type.""" + """The SQL INT or INTEGER type. + + .. seealso:: + + :class:`_types.Integer` - documentation for the base type. + + """ __visit_name__ = "INTEGER" @@ -2807,14 +2902,26 @@ INT = INTEGER class SMALLINT(SmallInteger): - """The SQL SMALLINT type.""" + """The SQL SMALLINT type. + + .. seealso:: + + :class:`_types.SmallInteger` - documentation for the base type. + + """ __visit_name__ = "SMALLINT" class BIGINT(BigInteger): - """The SQL BIGINT type.""" + """The SQL BIGINT type. + + .. seealso:: + + :class:`_types.BigInteger` - documentation for the base type. + + """ __visit_name__ = "BIGINT" -- cgit v1.2.1