]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
remove None exception in IN
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 5 Feb 2025 13:37:04 +0000 (08:37 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 5 Feb 2025 19:08:51 +0000 (14:08 -0500)
Fixed SQL composition bug which impacted caching where using a ``None``
value inside of an ``in_()`` expression would bypass the usual "expanded
bind parameter" logic used by the IN construct, which allows proper caching
to take place.

Fixes: #12314
References: #12312
Change-Id: I0d2fc4e15c73407379ba368dd4ee32660fc66259

doc/build/changelog/unreleased_20/12314.rst [new file with mode: 0644]
lib/sqlalchemy/sql/coercions.py
test/dialect/mssql/test_compiler.py
test/sql/test_compare.py
test/sql/test_operators.py

diff --git a/doc/build/changelog/unreleased_20/12314.rst b/doc/build/changelog/unreleased_20/12314.rst
new file mode 100644 (file)
index 0000000..6d5e83a
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, sql
+    :tickets: 12314
+
+    Fixed SQL rendering bug which impacted caching where using a ``None`` value
+    inside of an ``in_()`` expression would bypass the usual "expanded bind
+    parameter" logic used by the IN construct, which allows proper caching to
+    take place.
+
index 7119ae1c1f56fd4f7f3dcb9256293b0be53a9c4f..39655e56d940ca774e5062009664a34d9d45cc2a 100644 (file)
@@ -859,8 +859,6 @@ class InElementImpl(RoleImpl):
 
                     else:
                         non_literal_expressions[o] = o
-                elif o is None:
-                    non_literal_expressions[o] = elements.Null()
 
             if non_literal_expressions:
                 return elements.ClauseList(
index 59b13b91e0b5be53616b2cf4f449ec366d16bd02..eb4dba0a079683ea06ed3f7a20fc7e395adf2aff 100644 (file)
@@ -393,7 +393,11 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
                 "check_post_param": {},
             },
         ),
-        (lambda t: t.c.foo.in_([None]), "sometable.foo IN (NULL)", {}),
+        (
+            lambda t: t.c.foo.in_([None]),
+            "sometable.foo IN (__[POSTCOMPILE_foo_1])",
+            {},
+        ),
     )
     def test_strict_binds(self, expr, compiled, kw):
         """test the 'strict' compiler binds."""
index f9c435f839b49b14d43cf95061fdef5361beb6cd..5c7c5053e963e086d90d7c0bbd40a7bb0c0f4503 100644 (file)
@@ -1274,6 +1274,23 @@ class CacheKeyTest(fixtures.CacheKeyFixture, CoreFixtures, fixtures.TestBase):
         is_true(c1._generate_cache_key() != c3._generate_cache_key())
         is_false(c1._generate_cache_key() == c3._generate_cache_key())
 
+    def test_in_with_none(self):
+        """test #12314"""
+
+        def fixture():
+            elements = list(
+                random_choices([1, 2, None, 3, 4], k=random.randint(1, 7))
+            )
+
+            # slight issue.  if the first element is None and not an int,
+            # the type of the BindParameter goes from Integer to Nulltype.
+            # but if we set the left side to be Integer then it comes from
+            # that side, and the vast majority of in_() use cases come from
+            # a typed column expression, so this is fine
+            return (column("x", Integer).in_(elements),)
+
+        self._run_cache_key_fixture(fixture, False)
+
     def test_cache_key(self):
         for fixtures_, compare_values in [
             (self.fixtures, True),
index fbe9ba3900d1359a30f9d5507d020a53521100ef..6ed2c76d75050bdc24a5553924fba91ded711098 100644 (file)
@@ -2321,8 +2321,23 @@ class InTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         )
 
     def test_in_28(self):
+        """revised to test #12314"""
         self.assert_compile(
-            self.table1.c.myid.in_([None]), "mytable.myid IN (NULL)"
+            self.table1.c.myid.in_([None]),
+            "mytable.myid IN (__[POSTCOMPILE_myid_1])",
+        )
+
+    @testing.combinations(
+        [1, 2, None, 3],
+        [None, None, None],
+        [None, 2, 3, 3],
+    )
+    def test_in_null_combinations(self, expr):
+        """test #12314"""
+
+        self.assert_compile(
+            self.table1.c.myid.in_(expr),
+            "mytable.myid IN (__[POSTCOMPILE_myid_1])",
         )
 
     @testing.combinations(True, False)