summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-03-22 18:22:17 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-03-22 18:22:17 -0400
commitbe3c185fd48c2abcc5d9f54dd0c415e15c33184f (patch)
tree243b721f864930e9a20874c7cbacf64b1e184bff
parent56ef17e0f7b4a58fff6f35f15b1e8a5437191bcc (diff)
downloadsqlalchemy-be3c185fd48c2abcc5d9f54dd0c415e15c33184f.tar.gz
- Added new datatype :class:`.oracle.DATE`, which is a subclass of
:class:`.DateTime`. As Oracle has no "datetime" type per se, it instead has only ``DATE``, it is appropriate here that the ``DATE`` type as present in the Oracle dialect be an instance of :class:`.DateTime`. This issue doesn't change anything as far as the behavior of the type, as data conversion is handled by the DBAPI in any case, however the improved subclass layout will help the use cases of inspecting types for cross-database compatibility. Also removed uppercase ``DATETIME`` from the Oracle dialect as this type isn't functional in that context. fixes #2987
-rw-r--r--doc/build/changelog/changelog_09.rst15
-rw-r--r--doc/build/dialects/oracle.rst18
-rw-r--r--lib/sqlalchemy/dialects/oracle/__init__.py4
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py37
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py1
-rw-r--r--test/dialect/test_oracle.py25
-rw-r--r--test/engine/test_reflection.py4
7 files changed, 80 insertions, 24 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index 25ee9b9ed..2e4e5f098 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -15,6 +15,21 @@
:version: 0.9.4
.. change::
+ :tags: bug, oracle
+ :tickets: 2987
+
+ Added new datatype :class:`.oracle.DATE`, which is a subclass of
+ :class:`.DateTime`. As Oracle has no "datetime" type per se,
+ it instead has only ``DATE``, it is appropriate here that the
+ ``DATE`` type as present in the Oracle dialect be an instance of
+ :class:`.DateTime`. This issue doesn't change anything as far as
+ the behavior of the type, as data conversion is handled by the
+ DBAPI in any case, however the improved subclass layout will help
+ the use cases of inspecting types for cross-database compatibility.
+ Also removed uppercase ``DATETIME`` from the Oracle dialect as this
+ type isn't functional in that context.
+
+ .. change::
:tags: bug, sql
:tickets: 2988
:pullreq: github:78
diff --git a/doc/build/dialects/oracle.rst b/doc/build/dialects/oracle.rst
index 32a544877..4b4de3d4c 100644
--- a/doc/build/dialects/oracle.rst
+++ b/doc/build/dialects/oracle.rst
@@ -13,7 +13,7 @@ valid with Oracle are importable from the top level dialect, whether
they originate from :mod:`sqlalchemy.types` or from the local dialect::
from sqlalchemy.dialects.oracle import \
- BFILE, BLOB, CHAR, CLOB, DATE, DATETIME, \
+ BFILE, BLOB, CHAR, CLOB, DATE, \
DOUBLE_PRECISION, FLOAT, INTERVAL, LONG, NCLOB, \
NUMBER, NVARCHAR, NVARCHAR2, RAW, TIMESTAMP, VARCHAR, \
VARCHAR2
@@ -25,31 +25,33 @@ construction arguments, are as follows:
.. autoclass:: BFILE
:members: __init__
-
+
+.. autoclass:: DATE
+ :members: __init__
.. autoclass:: DOUBLE_PRECISION
:members: __init__
-
+
.. autoclass:: INTERVAL
:members: __init__
-
+
.. autoclass:: NCLOB
:members: __init__
-
+
.. autoclass:: NUMBER
:members: __init__
-
+
.. autoclass:: LONG
:members: __init__
-
+
.. autoclass:: RAW
:members: __init__
-
+
cx_Oracle
----------
diff --git a/lib/sqlalchemy/dialects/oracle/__init__.py b/lib/sqlalchemy/dialects/oracle/__init__.py
index 070e387d0..b75762ab2 100644
--- a/lib/sqlalchemy/dialects/oracle/__init__.py
+++ b/lib/sqlalchemy/dialects/oracle/__init__.py
@@ -9,14 +9,14 @@ from sqlalchemy.dialects.oracle import base, cx_oracle, zxjdbc
base.dialect = cx_oracle.dialect
from sqlalchemy.dialects.oracle.base import \
- VARCHAR, NVARCHAR, CHAR, DATE, DATETIME, NUMBER,\
+ VARCHAR, NVARCHAR, CHAR, DATE, NUMBER,\
BLOB, BFILE, CLOB, NCLOB, TIMESTAMP, RAW,\
FLOAT, DOUBLE_PRECISION, LONG, dialect, INTERVAL,\
VARCHAR2, NVARCHAR2, ROWID, dialect
__all__ = (
-'VARCHAR', 'NVARCHAR', 'CHAR', 'DATE', 'DATETIME', 'NUMBER',
+'VARCHAR', 'NVARCHAR', 'CHAR', 'DATE', 'NUMBER',
'BLOB', 'BFILE', 'CLOB', 'NCLOB', 'TIMESTAMP', 'RAW',
'FLOAT', 'DOUBLE_PRECISION', 'LONG', 'dialect', 'INTERVAL',
'VARCHAR2', 'NVARCHAR2', 'ROWID'
diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py
index d883d74f7..8bacb885f 100644
--- a/lib/sqlalchemy/dialects/oracle/base.py
+++ b/lib/sqlalchemy/dialects/oracle/base.py
@@ -176,6 +176,24 @@ accepted, including methods such as :meth:`.MetaData.reflect` and
If synonyms are not in use, this flag should be left disabled.
+DateTime Compatibility
+----------------------
+
+Oracle has no datatype known as ``DATETIME``, it instead has only ``DATE``,
+which can actually store a date and time value. For this reason, the Oracle
+dialect provides a type :class:`.oracle.DATE` which is a subclass of
+:class:`.DateTime`. This type has no special behavior, and is only
+present as a "marker" for this type; additionally, when a database column
+is reflected and the type is reported as ``DATE``, the time-supporting
+:class:`.oracle.DATE` type is used.
+
+.. versionchanged:: 0.9.4 Added :class:`.oracle.DATE` to subclass
+ :class:`.DateTime`. This is a change as previous versions
+ would reflect a ``DATE`` column as :class:`.types.DATE`, which subclasses
+ :class:`.Date`. The only significance here is for schemes that are
+ examining the type of column for use in special Python translations or
+ for migrating schemas to other database backends.
+
"""
import re
@@ -185,7 +203,7 @@ from sqlalchemy.engine import default, base, reflection
from sqlalchemy.sql import compiler, visitors, expression
from sqlalchemy.sql import operators as sql_operators, functions as sql_functions
from sqlalchemy import types as sqltypes, schema as sa_schema
-from sqlalchemy.types import VARCHAR, NVARCHAR, CHAR, DATE, DATETIME, \
+from sqlalchemy.types import VARCHAR, NVARCHAR, CHAR, \
BLOB, CLOB, TIMESTAMP, FLOAT
RESERVED_WORDS = \
@@ -258,6 +276,22 @@ class BFILE(sqltypes.LargeBinary):
class LONG(sqltypes.Text):
__visit_name__ = 'LONG'
+class DATE(sqltypes.DateTime):
+ """Provide the oracle DATE type.
+
+ This type has no special Python behavior, except that it subclasses
+ :class:`.types.DateTime`; this is to suit the fact that the Oracle
+ ``DATE`` type supports a time value.
+
+ .. versionadded:: 0.9.4
+
+ """
+ __visit_name__ = 'DATE'
+
+
+ def _compare_type_affinity(self, other):
+ return other._type_affinity in (sqltypes.DateTime, sqltypes.Date)
+
class INTERVAL(sqltypes.TypeEngine):
__visit_name__ = 'INTERVAL'
@@ -306,6 +340,7 @@ class _OracleBoolean(sqltypes.Boolean):
colspecs = {
sqltypes.Boolean: _OracleBoolean,
sqltypes.Interval: INTERVAL,
+ sqltypes.DateTime: DATE
}
ischema_names = {
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index e875c4667..b8ee90b53 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -610,7 +610,6 @@ class OracleDialect_cx_oracle(OracleDialect):
colspecs = colspecs = {
sqltypes.Numeric: _OracleNumeric,
sqltypes.Date: _OracleDate, # generic type, assume datetime.date is desired
- oracle.DATE: oracle.DATE, # non generic type - passthru
sqltypes.LargeBinary: _OracleBinary,
sqltypes.Boolean: oracle._OracleBoolean,
sqltypes.Interval: _OracleInterval,
diff --git a/test/dialect/test_oracle.py b/test/dialect/test_oracle.py
index 7e8ddffcd..041875879 100644
--- a/test/dialect/test_oracle.py
+++ b/test/dialect/test_oracle.py
@@ -1022,7 +1022,8 @@ class DialectTypesTest(fixtures.TestBase, AssertsCompiledSQL):
(oracle.OracleRaw(), cx_oracle._OracleRaw),
(String(), String),
(VARCHAR(), cx_oracle._OracleString),
- (DATE(), DATE),
+ (DATE(), cx_oracle._OracleDate),
+ (oracle.DATE(), oracle.DATE),
(String(50), cx_oracle._OracleString),
(Unicode(), cx_oracle._OracleNVarChar),
(Text(), cx_oracle._OracleText),
@@ -1403,22 +1404,26 @@ class TypesTest(fixtures.TestBase):
metadata = self.metadata
Table(
"date_types", metadata,
- Column('d1', DATE),
- Column('d2', TIMESTAMP),
- Column('d3', TIMESTAMP(timezone=True)),
- Column('d4', oracle.INTERVAL(second_precision=5)),
+ Column('d1', sqltypes.DATE),
+ Column('d2', oracle.DATE),
+ Column('d3', TIMESTAMP),
+ Column('d4', TIMESTAMP(timezone=True)),
+ Column('d5', oracle.INTERVAL(second_precision=5)),
)
metadata.create_all()
m = MetaData(testing.db)
t1 = Table(
"date_types", m,
autoload=True)
- assert isinstance(t1.c.d1.type, DATE)
- assert isinstance(t1.c.d2.type, TIMESTAMP)
- assert not t1.c.d2.type.timezone
+ assert isinstance(t1.c.d1.type, oracle.DATE)
+ assert isinstance(t1.c.d1.type, DateTime)
+ assert isinstance(t1.c.d2.type, oracle.DATE)
+ assert isinstance(t1.c.d2.type, DateTime)
assert isinstance(t1.c.d3.type, TIMESTAMP)
- assert t1.c.d3.type.timezone
- assert isinstance(t1.c.d4.type, oracle.INTERVAL)
+ assert not t1.c.d3.type.timezone
+ assert isinstance(t1.c.d4.type, TIMESTAMP)
+ assert t1.c.d4.type.timezone
+ assert isinstance(t1.c.d5.type, oracle.INTERVAL)
def test_reflect_all_types_schema(self):
types_table = Table('all_types', MetaData(testing.db),
diff --git a/test/engine/test_reflection.py b/test/engine/test_reflection.py
index 446bb8a36..a53893549 100644
--- a/test/engine/test_reflection.py
+++ b/test/engine/test_reflection.py
@@ -979,11 +979,11 @@ class ReflectionTest(fixtures.TestBase, ComparesTables):
users_v = Table("users_v", m2, autoload=True)
addresses_v = Table("email_addresses_v", m2, autoload=True)
- for c1, c2 in zip(users.c, users_v.c):
+ for c1, c2 in zip(users_v.c, users.c):
eq_(c1.name, c2.name)
self.assert_types_base(c1, c2)
- for c1, c2 in zip(addresses.c, addresses_v.c):
+ for c1, c2 in zip(addresses_v.c, addresses.c):
eq_(c1.name, c2.name)
self.assert_types_base(c1, c2)
finally: