From 7e6366ee1c79ae7192f3a0d2ec86a880487f530d Mon Sep 17 00:00:00 2001 From: zeeeeeb <5767468+zeeeeeb@users.noreply.github.com> Date: Mon, 7 Feb 2022 21:37:45 -0800 Subject: [PATCH] fixes: 5465 --- doc/build/changelog/unreleased_14/5465.rst | 6 ++++++ doc/build/core/type_basics.rst | 3 +++ lib/sqlalchemy/__init__.py | 2 ++ lib/sqlalchemy/dialects/mssql/base.py | 3 +++ lib/sqlalchemy/dialects/mysql/base.py | 1 + lib/sqlalchemy/dialects/mysql/types.py | 2 +- lib/sqlalchemy/dialects/oracle/base.py | 8 +++++++- lib/sqlalchemy/dialects/postgresql/base.py | 8 +++++++- lib/sqlalchemy/dialects/sqlite/base.py | 2 +- lib/sqlalchemy/sql/compiler.py | 6 ++++++ lib/sqlalchemy/sql/sqltypes.py | 18 ++++++++++++++++++ lib/sqlalchemy/types.py | 4 ++++ test/dialect/mssql/test_types.py | 2 ++ test/dialect/mysql/test_compiler.py | 6 ++++++ test/dialect/oracle/test_compiler.py | 16 ++++++++++++++++ test/dialect/oracle/test_reflection.py | 3 ++- test/dialect/oracle/test_types.py | 6 +++++- test/dialect/postgresql/test_compiler.py | 15 +++++++++++++++ test/dialect/postgresql/test_types.py | 3 +++ test/sql/test_types.py | 5 +++++ 20 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 doc/build/changelog/unreleased_14/5465.rst diff --git a/doc/build/changelog/unreleased_14/5465.rst b/doc/build/changelog/unreleased_14/5465.rst new file mode 100644 index 0000000000..af839dfa6d --- /dev/null +++ b/doc/build/changelog/unreleased_14/5465.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: datatypes, use case + :tickets: 5465 + + Implements generic Double, DOUBLE types with compiler mapping to + respective DOUBLE/DOUBLE PRECISION/FLOAT types in Dialects. diff --git a/doc/build/core/type_basics.rst b/doc/build/core/type_basics.rst index 3ec50cc003..a3d8cb8a57 100644 --- a/doc/build/core/type_basics.rst +++ b/doc/build/core/type_basics.rst @@ -52,6 +52,9 @@ type is emitted in ``CREATE TABLE``, such as ``VARCHAR`` see .. autoclass:: Enum :members: __init__, create, drop +.. autoclass:: Double + :members: + .. autoclass:: Float :members: diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py index 7d402d44ec..ab67d09a11 100644 --- a/lib/sqlalchemy/__init__.py +++ b/lib/sqlalchemy/__init__.py @@ -98,6 +98,8 @@ from .types import Date as Date from .types import DATETIME as DATETIME from .types import DateTime as DateTime from .types import DECIMAL as DECIMAL +from .types import DOUBLE +from .types import Double from .types import Enum as Enum from .types import FLOAT as FLOAT from .types import Float as Float diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index 2aff7d8b6d..6b01960d45 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -1488,6 +1488,9 @@ class MSTypeCompiler(compiler.GenericTypeCompiler): else: return "FLOAT(%(precision)s)" % {"precision": precision} + def visit_DOUBLE(self, type_, **kw): + return "FLOAT" + def visit_TINYINT(self, type_, **kw): return "TINYINT" diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index 7ec2b3dc26..5c2de09115 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1082,6 +1082,7 @@ colspecs = { _FloatType: _FloatType, sqltypes.Numeric: NUMERIC, sqltypes.Float: FLOAT, + sqltypes.Double: DOUBLE, sqltypes.Time: TIME, sqltypes.Enum: ENUM, sqltypes.MatchType: _MatchType, diff --git a/lib/sqlalchemy/dialects/mysql/types.py b/lib/sqlalchemy/dialects/mysql/types.py index 855fac1b36..2a4b4ad5fb 100644 --- a/lib/sqlalchemy/dialects/mysql/types.py +++ b/lib/sqlalchemy/dialects/mysql/types.py @@ -152,7 +152,7 @@ class DECIMAL(_NumericType, sqltypes.DECIMAL): ) -class DOUBLE(_FloatType): +class DOUBLE(_FloatType, sqltypes.DOUBLE): """MySQL DOUBLE type.""" __visit_name__ = "DOUBLE" diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index 578450d248..c10f83cfac 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -625,7 +625,7 @@ class NUMBER(sqltypes.Numeric, sqltypes.Integer): return sqltypes.Integer -class DOUBLE_PRECISION(sqltypes.Float): +class DOUBLE_PRECISION(sqltypes.Double): __visit_name__ = "DOUBLE_PRECISION" @@ -760,12 +760,18 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler): def visit_float(self, type_, **kw): return self.visit_FLOAT(type_, **kw) + def visit_double(self, type_, **kw): + return self.visit_DOUBLE_PRECISION(type_, **kw) + def visit_unicode(self, type_, **kw): if self.dialect._use_nchar_for_unicode: return self.visit_NVARCHAR2(type_, **kw) else: return self.visit_VARCHAR2(type_, **kw) + def visit_DOUBLE(self, type_, **kw): + return self.visit_DOUBLE_PRECISION(type_, **kw) + def visit_INTERVAL(self, type_, **kw): return "INTERVAL DAY%s TO SECOND%s" % ( type_.day_precision is not None diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 698ea277f5..2d44932c0f 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -1575,7 +1575,7 @@ class BYTEA(sqltypes.LargeBinary): __visit_name__ = "BYTEA" -class DOUBLE_PRECISION(sqltypes.Float): +class DOUBLE_PRECISION(sqltypes.Double): __visit_name__ = "DOUBLE_PRECISION" @@ -2899,6 +2899,12 @@ class PGTypeCompiler(compiler.GenericTypeCompiler): def visit_DOUBLE_PRECISION(self, type_, **kw): return "DOUBLE PRECISION" + def visit_DOUBLE(self, type_, **kw): + return "DOUBLE PRECISION" + + def visit_double(self, type_, **kw): + return "DOUBLE PRECISION" + def visit_BIGINT(self, type_, **kw): return "BIGINT" diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 236f6eaeff..246602155a 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -1179,7 +1179,7 @@ ischema_names = { "DATE_CHAR": sqltypes.DATE, "DATETIME": sqltypes.DATETIME, "DATETIME_CHAR": sqltypes.DATETIME, - "DOUBLE": sqltypes.FLOAT, + "DOUBLE": sqltypes.DOUBLE, "DECIMAL": sqltypes.DECIMAL, "FLOAT": sqltypes.FLOAT, "INT": sqltypes.INTEGER, diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 8a3f264255..1b0bc69c60 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -4848,6 +4848,9 @@ class GenericTypeCompiler(TypeCompiler): def visit_FLOAT(self, type_, **kw): return "FLOAT" + def visit_DOUBLE(self, type_, **kw): + return "DOUBLE" + def visit_REAL(self, type_, **kw): return "REAL" @@ -4966,6 +4969,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 42fad5e044..1b430bed8b 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -600,6 +600,17 @@ 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. + """ + + __visit_name__ = "double" + + class DateTime(_LookupExpressionAdapter, TypeEngine[dt.datetime]): """A type for ``datetime.datetime()`` objects. @@ -2766,6 +2777,13 @@ class FLOAT(Float): __visit_name__ = "FLOAT" +class DOUBLE(Double): + + """The SQL DOUBLE type.""" + + __visit_name__ = "DOUBLE" + + class NUMERIC(Numeric): """The SQL NUMERIC type.""" diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 07263c5b9e..a0e0118bdd 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -22,6 +22,7 @@ __all__ = [ "TEXT", "Text", "FLOAT", + "DOUBLE", "NUMERIC", "REAL", "DECIMAL", @@ -44,6 +45,7 @@ __all__ = [ "BigInteger", "Numeric", "Float", + "Double", "DateTime", "Date", "Time", @@ -76,6 +78,8 @@ from .sql.sqltypes import Date from .sql.sqltypes import DATETIME from .sql.sqltypes import DateTime from .sql.sqltypes import DECIMAL +from .sql.sqltypes import DOUBLE +from .sql.sqltypes import Double from .sql.sqltypes import Enum from .sql.sqltypes import FLOAT from .sql.sqltypes import Float diff --git a/test/dialect/mssql/test_types.py b/test/dialect/mssql/test_types.py index 2ef8b76dae..ba74a7970a 100644 --- a/test/dialect/mssql/test_types.py +++ b/test/dialect/mssql/test_types.py @@ -295,6 +295,8 @@ class TypeDDLTest(fixtures.TestBase): (types.Float, [], {}, "FLOAT"), (types.Float, [None], {}, "FLOAT"), (types.Float, [12], {}, "FLOAT(12)"), + (types.Double, [], {}, "FLOAT"), + (types.DOUBLE, [], {}, "FLOAT"), (mssql.MSReal, [], {}, "REAL"), (types.Integer, [], {}, "INTEGER"), (types.BigInteger, [], {}, "BIGINT"), diff --git a/test/dialect/mysql/test_compiler.py b/test/dialect/mysql/test_compiler.py index ba162b4902..fa8f37c1f8 100644 --- a/test/dialect/mysql/test_compiler.py +++ b/test/dialect/mysql/test_compiler.py @@ -15,6 +15,8 @@ from sqlalchemy import Date from sqlalchemy import DATETIME from sqlalchemy import DateTime from sqlalchemy import DECIMAL +from sqlalchemy import DOUBLE +from sqlalchemy import Double from sqlalchemy import exc from sqlalchemy import extract from sqlalchemy import FLOAT @@ -776,6 +778,8 @@ class SQLTest(fixtures.TestBase, AssertsCompiledSQL): (Float, "t.col"), (m.MSFloat, "t.col"), (m.MSDouble, "t.col"), + (DOUBLE, "t.col"), + (Double, "t.col"), (m.MSReal, "t.col"), (m.MSYear, "t.col"), (m.MSYear(2), "t.col"), @@ -798,6 +802,8 @@ class SQLTest(fixtures.TestBase, AssertsCompiledSQL): (m.FLOAT, "CAST(t.col AS FLOAT)"), (Float, "CAST(t.col AS FLOAT)"), (FLOAT, "CAST(t.col AS FLOAT)"), + (Double, "CAST(t.col AS DOUBLE)"), + (DOUBLE, "CAST(t.col AS DOUBLE)"), (m.DOUBLE, "CAST(t.col AS DOUBLE)"), (m.FLOAT, "CAST(t.col AS FLOAT)"), argnames="type_,expected", diff --git a/test/dialect/oracle/test_compiler.py b/test/dialect/oracle/test_compiler.py index 22ffc888ab..4cb6a8bb84 100644 --- a/test/dialect/oracle/test_compiler.py +++ b/test/dialect/oracle/test_compiler.py @@ -1,6 +1,7 @@ # coding: utf-8 from sqlalchemy import and_ from sqlalchemy import bindparam +from sqlalchemy import cast from sqlalchemy import Computed from sqlalchemy import exc from sqlalchemy import except_ @@ -23,6 +24,7 @@ from sqlalchemy import testing from sqlalchemy import text from sqlalchemy import type_coerce from sqlalchemy import TypeDecorator +from sqlalchemy import types as sqltypes from sqlalchemy import union from sqlalchemy.dialects.oracle import base as oracle from sqlalchemy.dialects.oracle import cx_oracle @@ -1380,6 +1382,20 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): dialect=dd, ) + def test_double_to_oracle_double(self): + """test #5465: + test sqlalchemy Double/DOUBLE to PostgreSQL DOUBLE PRECISION + """ + d1 = sqltypes.Double + d2 = sqltypes.DOUBLE + + self.assert_compile( + cast(column("foo"), d1), "CAST(foo AS DOUBLE PRECISION)" + ) + self.assert_compile( + cast(column("bar"), d2), "CAST(bar AS DOUBLE PRECISION)" + ) + class SequenceTest(fixtures.TestBase, AssertsCompiledSQL): def test_basic(self): diff --git a/test/dialect/oracle/test_reflection.py b/test/dialect/oracle/test_reflection.py index df8dff30f8..4c1a617610 100644 --- a/test/dialect/oracle/test_reflection.py +++ b/test/dialect/oracle/test_reflection.py @@ -1,6 +1,7 @@ # coding: utf-8 +from sqlalchemy import DOUBLE from sqlalchemy import exc from sqlalchemy import FLOAT from sqlalchemy import ForeignKey @@ -803,7 +804,7 @@ class TypeReflectionTest(fixtures.TestBase): connection, ): specs = [ - (DOUBLE_PRECISION(), FLOAT()), + (DOUBLE_PRECISION(), DOUBLE()), # when binary_precision is supported # (DOUBLE_PRECISION(), oracle.FLOAT(binary_precision=126)), (BINARY_DOUBLE(), BINARY_DOUBLE()), diff --git a/test/dialect/oracle/test_types.py b/test/dialect/oracle/test_types.py index 715a90000c..8f478feb54 100644 --- a/test/dialect/oracle/test_types.py +++ b/test/dialect/oracle/test_types.py @@ -12,6 +12,8 @@ from sqlalchemy import CHAR from sqlalchemy import DATE from sqlalchemy import Date from sqlalchemy import DateTime +from sqlalchemy import DOUBLE +from sqlalchemy import Double from sqlalchemy import event from sqlalchemy import FLOAT from sqlalchemy import Float @@ -309,7 +311,9 @@ class TypesTest(fixtures.TestBase): Column("numericcol", Numeric(precision=9, scale=2)), Column("floatcol1", Float()), Column("floatcol2", FLOAT()), - Column("doubleprec", oracle.DOUBLE_PRECISION), + Column("doubleprec1", oracle.DOUBLE_PRECISION), + Column("doubleprec2", Double()), + Column("doubleprec3", DOUBLE()), Column("numbercol1", oracle.NUMBER(9)), Column("numbercol2", oracle.NUMBER(9, 3)), Column("numbercol3", oracle.NUMBER), diff --git a/test/dialect/postgresql/test_compiler.py b/test/dialect/postgresql/test_compiler.py index b98d0fac6a..400080e78d 100644 --- a/test/dialect/postgresql/test_compiler.py +++ b/test/dialect/postgresql/test_compiler.py @@ -243,6 +243,21 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): "CAST(bar AS someschema.somename) AS bar", ) + def test_cast_double_pg_double(self): + """test #5465: + + test sqlalchemy Double/DOUBLE to PostgreSQL DOUBLE PRECISION + """ + d1 = sqltypes.Double + d2 = sqltypes.DOUBLE + + stmt = select(cast(column("foo"), d1), cast(column("bar"), d2)) + self.assert_compile( + stmt, + "SELECT CAST(foo AS DOUBLE PRECISION) AS foo, " + "CAST(bar AS DOUBLE PRECISION) AS bar", + ) + def test_cast_enum_schema_translate(self): """test #6739""" e1 = Enum("x", "y", "z", name="somename") diff --git a/test/dialect/postgresql/test_types.py b/test/dialect/postgresql/test_types.py index 0c00c76333..96d1f1f3e7 100644 --- a/test/dialect/postgresql/test_types.py +++ b/test/dialect/postgresql/test_types.py @@ -12,6 +12,7 @@ from sqlalchemy import cast from sqlalchemy import Column from sqlalchemy import column from sqlalchemy import DateTime +from sqlalchemy import Double from sqlalchemy import Enum from sqlalchemy import exc from sqlalchemy import Float @@ -127,6 +128,7 @@ class FloatCoercionTest(fixtures.TablesTest, AssertsExecutionResults): Column("x", postgresql.ARRAY(Float)), Column("y", postgresql.ARRAY(REAL)), Column("z", postgresql.ARRAY(postgresql.DOUBLE_PRECISION)), + Column("w", postgresql.ARRAY(Double)), Column("q", postgresql.ARRAY(Numeric)), ) metadata.create_all(connection) @@ -143,6 +145,7 @@ class FloatCoercionTest(fixtures.TablesTest, AssertsExecutionResults): Column("x", sqltypes.ARRAY(Float)), Column("y", sqltypes.ARRAY(REAL)), Column("z", sqltypes.ARRAY(postgresql.DOUBLE_PRECISION)), + Column("w", sqltypes.ARRAY(Double)), Column("q", sqltypes.ARRAY(Numeric)), ) metadata.create_all(connection) diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 79b77581d0..e5891b2bb3 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -23,6 +23,7 @@ from sqlalchemy import DateTime from sqlalchemy import DECIMAL from sqlalchemy import dialects from sqlalchemy import distinct +from sqlalchemy import Double from sqlalchemy import Enum from sqlalchemy import exc from sqlalchemy import FLOAT @@ -277,6 +278,7 @@ class AdaptTest(fixtures.TestBase): eq_(types.Numeric(asdecimal=False).python_type, float) eq_(types.LargeBinary().python_type, bytes) eq_(types.Float().python_type, float) + eq_(types.Double().python_type, float) eq_(types.Interval().python_type, datetime.timedelta) eq_(types.Date().python_type, datetime.date) eq_(types.DateTime().python_type, datetime.datetime) @@ -3455,6 +3457,9 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): dialects.postgresql.FLOAT(), "FLOAT", allow_dialect_select=True ) + def test_default_compile_double(self): + self.assert_compile(Double(), "DOUBLE") + def test_default_compile_mysql_integer(self): self.assert_compile( dialects.mysql.INTEGER(display_width=5), -- 2.47.3