summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/dialects
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2022-02-25 17:47:44 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2022-02-25 17:47:44 +0000
commit2d97c388eae4345840f745337ec033045651b36d (patch)
tree50ffde647a0506e86de955af8ea3685c65f20b9d /lib/sqlalchemy/dialects
parent51e6a62ab371897d646a5986b9139838402c57df (diff)
parentb9d231869d7e39decabdec12478e359c4dcb95ee (diff)
downloadsqlalchemy-2d97c388eae4345840f745337ec033045651b36d.tar.gz
Merge "Implement generic Double and related fixed types" into main
Diffstat (limited to 'lib/sqlalchemy/dialects')
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py1
-rw-r--r--lib/sqlalchemy/dialects/mysql/types.py2
-rw-r--r--lib/sqlalchemy/dialects/oracle/__init__.py1
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py104
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py9
-rw-r--r--lib/sqlalchemy/dialects/sqlite/base.py2
6 files changed, 101 insertions, 18 deletions
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py
index 7ec2b3dc2..5c2de0911 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 855fac1b3..2a4b4ad5f 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/__init__.py b/lib/sqlalchemy/dialects/oracle/__init__.py
index c83e0573d..8994b4a10 100644
--- a/lib/sqlalchemy/dialects/oracle/__init__.py
+++ b/lib/sqlalchemy/dialects/oracle/__init__.py
@@ -24,6 +24,7 @@ from .base import NUMBER
from .base import NVARCHAR
from .base import NVARCHAR2
from .base import RAW
+from .base import REAL
from .base import ROWID
from .base import TIMESTAMP
from .base import VARCHAR
diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py
index 578450d24..1ae58b8f4 100644
--- a/lib/sqlalchemy/dialects/oracle/base.py
+++ b/lib/sqlalchemy/dialects/oracle/base.py
@@ -558,10 +558,12 @@ from ...sql import visitors
from ...types import BLOB
from ...types import CHAR
from ...types import CLOB
+from ...types import DOUBLE_PRECISION
from ...types import FLOAT
from ...types import INTEGER
from ...types import NCHAR
from ...types import NVARCHAR
+from ...types import REAL
from ...types import TIMESTAMP
from ...types import VARCHAR
@@ -625,8 +627,49 @@ class NUMBER(sqltypes.Numeric, sqltypes.Integer):
return sqltypes.Integer
-class DOUBLE_PRECISION(sqltypes.Float):
- __visit_name__ = "DOUBLE_PRECISION"
+class FLOAT(sqltypes.FLOAT):
+ """Oracle FLOAT.
+
+ This is the same as :class:`_sqltypes.FLOAT` except that
+ an Oracle-specific :paramref:`_oracle.FLOAT.binary_precision`
+ parameter is accepted, and
+ the :paramref:`_sqltypes.Float.precision` parameter is not accepted.
+
+ Oracle FLOAT types indicate precision in terms of "binary precision", which
+ defaults to 126. For a REAL type, the value is 63. This parameter does not
+ cleanly map to a specific number of decimal places but is roughly
+ equivalent to the desired number of decimal places divided by 0.3103.
+
+ .. versionadded:: 2.0
+
+ """
+
+ __visit_name__ = "FLOAT"
+
+ def __init__(
+ self,
+ binary_precision=None,
+ asdecimal=False,
+ decimal_return_scale=None,
+ ):
+ r"""
+ Construct a FLOAT
+
+ :param binary_precision: Oracle binary precision value to be rendered
+ in DDL. This may be approximated to the number of decimal characters
+ using the formula "decimal precision = 0.30103 * binary precision".
+ The default value used by Oracle for FLOAT / DOUBLE PRECISION is 126.
+
+ :param asdecimal: See :paramref:`_sqltypes.Float.asdecimal`
+
+ :param decimal_return_scale: See
+ :paramref:`_sqltypes.Float.decimal_return_scale`
+
+ """
+ super().__init__(
+ asdecimal=asdecimal, decimal_return_scale=decimal_return_scale
+ )
+ self.binary_precision = binary_precision
class BINARY_DOUBLE(sqltypes.Float):
@@ -742,6 +785,7 @@ ischema_names = {
"RAW": RAW,
"FLOAT": FLOAT,
"DOUBLE PRECISION": DOUBLE_PRECISION,
+ "REAL": REAL,
"LONG": LONG,
"BINARY_DOUBLE": BINARY_DOUBLE,
"BINARY_FLOAT": BINARY_FLOAT,
@@ -760,6 +804,9 @@ 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)
@@ -795,24 +842,50 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
return self._generate_numeric(type_, "BINARY_FLOAT", **kw)
def visit_FLOAT(self, type_, **kw):
- # don't support conversion between decimal/binary
- # precision yet
- kw["no_precision"] = True
+ kw["_requires_binary_precision"] = True
return self._generate_numeric(type_, "FLOAT", **kw)
def visit_NUMBER(self, type_, **kw):
return self._generate_numeric(type_, "NUMBER", **kw)
def _generate_numeric(
- self, type_, name, precision=None, scale=None, no_precision=False, **kw
+ self,
+ type_,
+ name,
+ precision=None,
+ scale=None,
+ _requires_binary_precision=False,
+ **kw,
):
if precision is None:
- precision = type_.precision
+
+ precision = getattr(type_, "precision", None)
+
+ if _requires_binary_precision:
+ binary_precision = getattr(type_, "binary_precision", None)
+
+ if precision and binary_precision is None:
+ # https://www.oracletutorial.com/oracle-basics/oracle-float/
+ estimated_binary_precision = int(precision / 0.30103)
+ raise exc.ArgumentError(
+ "Oracle FLOAT types use 'binary precision', which does "
+ "not convert cleanly from decimal 'precision'. Please "
+ "specify "
+ f"this type with a separate Oracle variant, such as "
+ f"{type_.__class__.__name__}(precision={precision})."
+ f"with_variant(oracle.FLOAT"
+ f"(binary_precision="
+ f"{estimated_binary_precision}), 'oracle'), so that the "
+ "Oracle specific 'binary_precision' may be specified "
+ "accurately."
+ )
+ else:
+ precision = binary_precision
if scale is None:
scale = getattr(type_, "scale", None)
- if no_precision or precision is None:
+ if precision is None:
return name
elif scale is None:
n = "%(name)s(%(precision)s)"
@@ -1964,8 +2037,19 @@ class OracleDialect(default.DefaultDialect):
else:
coltype = NUMBER(precision, scale)
elif coltype == "FLOAT":
- # TODO: support "precision" here as "binary_precision"
- coltype = FLOAT()
+ # https://docs.oracle.com/cd/B14117_01/server.101/b10758/sqlqr06.htm
+ if precision == 126:
+ # The DOUBLE PRECISION datatype is a floating-point
+ # number with binary precision 126.
+ coltype = DOUBLE_PRECISION()
+ elif precision == 63:
+ # The REAL datatype is a floating-point number with a
+ # binary precision of 63, or 18 decimal.
+ coltype = REAL()
+ else:
+ # non standard precision
+ coltype = FLOAT(binary_precision=precision)
+
elif coltype in ("VARCHAR2", "NVARCHAR2", "CHAR", "NCHAR"):
coltype = self.ischema_names.get(coltype)(length)
elif "WITH TIME ZONE" in coltype:
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index 698ea277f..e265cd0f7 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -1449,6 +1449,7 @@ from ...types import BIGINT
from ...types import BOOLEAN
from ...types import CHAR
from ...types import DATE
+from ...types import DOUBLE_PRECISION
from ...types import FLOAT
from ...types import INTEGER
from ...types import NUMERIC
@@ -1575,10 +1576,6 @@ class BYTEA(sqltypes.LargeBinary):
__visit_name__ = "BYTEA"
-class DOUBLE_PRECISION(sqltypes.Float):
- __visit_name__ = "DOUBLE_PRECISION"
-
-
class INET(sqltypes.TypeEngine):
__visit_name__ = "INET"
@@ -2896,8 +2893,8 @@ class PGTypeCompiler(compiler.GenericTypeCompiler):
else:
return "FLOAT(%(precision)s)" % {"precision": type_.precision}
- def visit_DOUBLE_PRECISION(self, type_, **kw):
- return "DOUBLE PRECISION"
+ def visit_double(self, type_, **kw):
+ return self.visit_DOUBLE_PRECISION(type, **kw)
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 385de373e..79068c75f 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,