]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Support local timespamp support on Oracle
authorFederico Caselli <cfederico87@gmail.com>
Wed, 11 Jan 2023 19:44:07 +0000 (20:44 +0100)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 12 Jan 2023 15:53:53 +0000 (10:53 -0500)
Added support for the Oracle SQL type ``TIMESTAMP WITH LOCAL TIME ZONE``,
using a newly added Oracle-specific :class:`_oracle.TIMESTAMP` datatype.

Fixes: #9086
Change-Id: Ib14119503a8aaf20e1aa4e36be80ccca37383e90

doc/build/changelog/unreleased_20/9086.rst [new file with mode: 0644]
doc/build/dialects/oracle.rst
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/dialects/oracle/types.py
test/dialect/oracle/test_types.py

diff --git a/doc/build/changelog/unreleased_20/9086.rst b/doc/build/changelog/unreleased_20/9086.rst
new file mode 100644 (file)
index 0000000..55031d9
--- /dev/null
@@ -0,0 +1,6 @@
+.. change::
+    :tags: oracle, usecase
+    :tickets: 9086
+
+    Added support for the Oracle SQL type ``TIMESTAMP WITH LOCAL TIME ZONE``,
+    using a newly added Oracle-specific :class:`_oracle.TIMESTAMP` datatype.
index d676f633b993311ded4ecbae6d03c84c30831fe4..02f512214162ccdfc978ffd94158af400ae46110 100644 (file)
@@ -65,6 +65,9 @@ construction arguments, are as follows:
 .. autoclass:: RAW
   :members: __init__
 
+.. autoclass:: TIMESTAMP
+  :members: __init__
+
 .. _cx_oracle:
 
 cx_Oracle
index d24df7ee0063d7d0a2e7de9874e390f11ea58691..65377be286d3f850d2b3aa99aa86393faf8ebbf7 100644 (file)
@@ -564,6 +564,7 @@ from .types import NVARCHAR2  # noqa
 from .types import OracleRaw  # noqa
 from .types import RAW
 from .types import ROWID  # noqa
+from .types import TIMESTAMP
 from .types import VARCHAR2  # noqa
 from ... import Computed
 from ... import exc
@@ -595,7 +596,6 @@ from ...types import INTEGER
 from ...types import NCHAR
 from ...types import NVARCHAR
 from ...types import REAL
-from ...types import TIMESTAMP
 from ...types import VARCHAR
 
 RESERVED_WORDS = set(
@@ -635,6 +635,7 @@ ischema_names = {
     "NCLOB": NCLOB,
     "TIMESTAMP": TIMESTAMP,
     "TIMESTAMP WITH TIME ZONE": TIMESTAMP,
+    "TIMESTAMP WITH LOCAL TIME ZONE": TIMESTAMP,
     "INTERVAL DAY TO SECOND": INTERVAL,
     "RAW": RAW,
     "FLOAT": FLOAT,
@@ -681,7 +682,9 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
         return "LONG"
 
     def visit_TIMESTAMP(self, type_, **kw):
-        if type_.timezone:
+        if getattr(type_, "local_timezone", False):
+            return "TIMESTAMP WITH LOCAL TIME ZONE"
+        elif type_.timezone:
             return "TIMESTAMP WITH TIME ZONE"
         else:
             return "TIMESTAMP"
@@ -2330,6 +2333,8 @@ class OracleDialect(default.DefaultDialect):
                 coltype = self.ischema_names.get(coltype)(char_length)
             elif "WITH TIME ZONE" in coltype:
                 coltype = TIMESTAMP(timezone=True)
+            elif "WITH LOCAL TIME ZONE" in coltype:
+                coltype = TIMESTAMP(local_timezone=True)
             else:
                 coltype = re.sub(r"\(\d+\)", "", coltype)
                 try:
index af66d2eb48bb19ffdb690b1c287f3c86523f41f3..db3d57228ae15fabadd62190c896a7a398f9a640 100644 (file)
@@ -5,6 +5,7 @@
 # the MIT License: https://www.opensource.org/licenses/mit-license.php
 # mypy: ignore-errors
 
+from ... import exc
 from ...sql import sqltypes
 from ...types import NVARCHAR
 from ...types import VARCHAR
@@ -216,6 +217,33 @@ class INTERVAL(sqltypes.NativeForEmulated, sqltypes._AbstractInterval):
         )
 
 
+class TIMESTAMP(sqltypes.TIMESTAMP):
+    """Oracle implementation of ``TIMESTAMP``, which supports additional
+    Oracle-specific modes
+
+    .. versionadded:: 2.0
+
+    """
+
+    def __init__(self, timezone: bool = False, local_timezone: bool = False):
+        """Construct a new :class:`_oracle.TIMESTAMP`.
+
+        :param timezone: boolean.  Indicates that the TIMESTAMP type should
+         use Oracle's ``TIMESTAMP WITH TIME ZONE`` datatype.
+
+        :param local_timezone: boolean.  Indicates that the TIMESTAMP type
+         should use Oracle's ``TIMESTAMP WITH LOCAL TIME ZONE`` datatype.
+
+
+        """
+        if timezone and local_timezone:
+            raise exc.ArgumentError(
+                "timezone and local_timezone are mutually exclusive"
+            )
+        super().__init__(timezone=timezone)
+        self.local_timezone = local_timezone
+
+
 class ROWID(sqltypes.TypeEngine):
     """Oracle ROWID type.
 
index 2ba42f58406b47c78b1964793c9c41f3ab44cdf1..e77bde19e9677852f202267058c088b1ec9f9bef 100644 (file)
@@ -752,6 +752,7 @@ class TypesTest(fixtures.TestBase):
             Column("d3", TIMESTAMP),
             Column("d4", TIMESTAMP(timezone=True)),
             Column("d5", oracle.INTERVAL(second_precision=5)),
+            Column("d6", oracle.TIMESTAMP(local_timezone=True)),
         )
         metadata.create_all(connection)
         m = MetaData()
@@ -760,10 +761,12 @@ class TypesTest(fixtures.TestBase):
         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 isinstance(t1.c.d3.type, oracle.TIMESTAMP)
         assert not t1.c.d3.type.timezone
-        assert isinstance(t1.c.d4.type, TIMESTAMP)
+        assert isinstance(t1.c.d4.type, oracle.TIMESTAMP)
         assert t1.c.d4.type.timezone
+        assert isinstance(t1.c.d6.type, oracle.TIMESTAMP)
+        assert t1.c.d6.type.local_timezone
         assert isinstance(t1.c.d5.type, oracle.INTERVAL)
 
     def _dont_test_reflect_all_types_schema(self):