]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
call setinputsizes for cx_Oracle.DATETIME
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 3 Oct 2019 18:09:54 +0000 (14:09 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 3 Oct 2019 19:03:35 +0000 (15:03 -0400)
Restored adding cx_Oracle.DATETIME to the setinputsizes() call when a
SQLAlchemy :class:`.Date`, :class:`.DateTime` or :class:`.Time` datatype is
used, so that in the case where a bound parameter is passed as NULL
in some complex queries (in particular this happens with some lazy load
situations), the type is still present.  This was removed
in the 1.2 series for arbitrary reasons.

Also adds a suite test for this generic situation.

What's not clear is that do we really need setinputsizes() for all
datatypes if we are supporting NULL in bound parameters.

Fixes: #4886
Change-Id: If99215c31861f9ea6f60a30d47f2f320adc4797f
(cherry picked from commit e21afbcd8c78e4e3f9400da1a2565472682de825)

doc/build/changelog/unreleased_13/4886.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/oracle/cx_oracle.py
lib/sqlalchemy/testing/requirements.py
lib/sqlalchemy/testing/suite/test_types.py

diff --git a/doc/build/changelog/unreleased_13/4886.rst b/doc/build/changelog/unreleased_13/4886.rst
new file mode 100644 (file)
index 0000000..285bbdd
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, oracle
+    :tickets: 4886
+
+    Restored adding cx_Oracle.DATETIME to the setinputsizes() call when a
+    SQLAlchemy :class:`.Date`, :class:`.DateTime` or :class:`.Time` datatype is
+    used, as some complex queries require this to be present.  This was removed
+    in the 1.2 series for arbitrary reasons.
index 90d049aa0864b16c9ddac8d85cfe8fc2a8d49b46..6fb716df23de1563e1d683e64310212cc0e7b140 100644 (file)
@@ -782,6 +782,7 @@ class OracleDialect_cx_oracle(OracleDialect):
                 )
 
             self._include_setinputsizes = {
+                cx_Oracle.DATETIME,
                 cx_Oracle.NCLOB,
                 cx_Oracle.CLOB,
                 cx_Oracle.LOB,
index 11f5d249f9416f10c0814b86b5f8c9a02d1fafda..266aa332727367f4bd9b2c14db04e1be6b0d0a62 100644 (file)
@@ -172,6 +172,14 @@ class SuiteRequirements(Requirements):
         """
         return exclusions.closed()
 
+    @property
+    def standalone_null_binds_whereclause(self):
+        """target database/driver supports bound parameters with NULL in the
+        WHERE clause, in situations where it has to be typed.
+
+        """
+        return exclusions.open()
+
     @property
     def intersect(self):
         """Target database must support INTERSECT or equivalent."""
index 37428c54544d03d18282f56e709d95af578f4d5b..435ab4689de8f347a29020af6c2bcdb2228b9422 100644 (file)
@@ -14,7 +14,9 @@ from ..schema import Column
 from ..schema import Table
 from ... import and_
 from ... import BigInteger
+from ... import bindparam
 from ... import Boolean
+from ... import case
 from ... import cast
 from ... import Date
 from ... import DateTime
@@ -291,6 +293,33 @@ class _DateFixture(_LiteralRoundTripFixture):
         compare = self.compare or self.data
         self._literal_round_trip(self.datatype, [self.data], [compare])
 
+    @testing.requires.standalone_null_binds_whereclause
+    def test_null_bound_comparison(self):
+        # this test is based on an Oracle issue observed in #4886.
+        # passing NULL for an expression that needs to be interpreted as
+        # a certain type, does the DBAPI have the info it needs to do this.
+        date_table = self.tables.date_table
+        with config.db.connect() as conn:
+            result = conn.execute(
+                date_table.insert(), {"date_data": self.data}
+            )
+            id_ = result.inserted_primary_key[0]
+            stmt = select([date_table.c.id]).where(
+                case(
+                    [
+                        (
+                            bindparam("foo", type_=self.datatype) != None,
+                            bindparam("foo", type_=self.datatype),
+                        )
+                    ],
+                    else_=date_table.c.date_data,
+                )
+                == date_table.c.date_data
+            )
+
+            row = conn.execute(stmt, {"foo": None}).first()
+            eq_(row[0], id_)
+
 
 class DateTimeTest(_DateFixture, fixtures.TablesTest):
     __requires__ = ("datetime",)