]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added new datatype :class:`.oracle.DATE`, which is a subclass of
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 22 Mar 2014 22:22:17 +0000 (18:22 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 22 Mar 2014 22:22:17 +0000 (18:22 -0400)
: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

doc/build/changelog/changelog_09.rst
doc/build/dialects/oracle.rst
lib/sqlalchemy/dialects/oracle/__init__.py
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/dialects/oracle/cx_oracle.py
test/dialect/test_oracle.py
test/engine/test_reflection.py

index 25ee9b9ede72333a6248437c45947be8235fdae4..2e4e5f0983096c0a0fa8dbcab6615f956168ebdf 100644 (file)
 .. changelog::
     :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
index 32a5448775103e96907415ae3389386cfd5be7de..4b4de3d4c60321bb7a3c935077fd235c91c63833 100644 (file)
@@ -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
 ----------
index 070e387d04bbbdca1fcc00d1f664fb7db1db57ca..b75762ab25fc9d306cdae8c7c436ee77ef854c17 100644 (file)
@@ -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'
index d883d74f76428818fa8e48c098dd81aa7ecb5fca..8bacb885f8ace5c92eca8f438da01ede350c4d93 100644 (file)
@@ -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 = {
index e875c466711dd7b652063194059f9d1dd2952089..b8ee90b53ef97ccf61fa8533d5c1a4c3929d79f9 100644 (file)
@@ -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,
index 7e8ddffcd9718d5f0f386ef763e342c4c00c853c..0418758799de04843b3288571791ca4e0dba3484 100644 (file)
@@ -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),
index 446bb8a360d3a7047116e2723b51777faa604f4d..a53893549d22b2f74a53e4589b81ca770b11f991 100644 (file)
@@ -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: