]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fit literal compile of empty in on a tuple
authorFederico Caselli <cfederico87@gmail.com>
Fri, 16 Apr 2021 20:07:34 +0000 (22:07 +0200)
committerFederico Caselli <cfederico87@gmail.com>
Fri, 16 Apr 2021 20:28:16 +0000 (22:28 +0200)
Fixed regression where an empty in statement on a tuple would result
in an error when compiled with the option ``literal_binds=True``.

Fixes: #6290
Change-Id: Ic0dff8f4a874cccdb201b6d9dcd3c2e7b7884cbb

doc/build/changelog/unreleased_14/6290.rst [new file with mode: 0644]
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/elements.py
test/sql/test_operators.py

diff --git a/doc/build/changelog/unreleased_14/6290.rst b/doc/build/changelog/unreleased_14/6290.rst
new file mode 100644 (file)
index 0000000..64e2317
--- /dev/null
@@ -0,0 +1,6 @@
+.. change::
+    :tags: bug, compiler, regression
+    :tickets: 6290
+
+    Fixed regression where an empty in statement on a tuple would result
+    in an error when compiled with the option ``literal_binds=True``.
index 0c701cb52336195b5dcae41f041915426e933a86..a466216e3cc4f04244ef2e58aaedcb6b8a7cbf60 100644 (file)
@@ -1914,10 +1914,14 @@ class SQLCompiler(Compiled):
     ):
 
         if not values:
-            assert not parameter.type._is_tuple_type
-            replacement_expression = self.visit_empty_set_expr(
-                [parameter.type]
-            )
+            if parameter.type._is_tuple_type:
+                replacement_expression = (
+                    "VALUES " if self.dialect.tuple_in_values else ""
+                ) + self.visit_empty_set_expr(parameter.type.types)
+            else:
+                replacement_expression = self.visit_empty_set_expr(
+                    [parameter.type]
+                )
 
         elif isinstance(values[0], (tuple, list)):
             assert parameter.type._is_tuple_type
index e97ed252ebab77d270253610806b96ec5c744f7d..699a874fb831220bcde468944eb8fb14bf6a507a 100644 (file)
@@ -1407,8 +1407,8 @@ class BindParameter(roles.InElementRole, ColumnElement):
                 self.type = type_api._resolve_value_to_type(check_value)
         elif isinstance(type_, type):
             self.type = type_()
-        elif type_._is_tuple_type:
-            if expanding and value:
+        elif type_._is_tuple_type and value:
+            if expanding:
                 check_value = value[0]
             else:
                 check_value = value
index 878360b9d8da98f85289745dd2f93b9f1f422f14..8fe802bf3db54de853127c6db49ff212f1e2eb79 100644 (file)
@@ -1933,18 +1933,51 @@ class InTest(fixtures.TestBase, testing.AssertsCompiledSQL):
             self.table1.c.myid.in_([None]), "mytable.myid IN (NULL)"
         )
 
-    def test_in_29(self):
+    @testing.combinations(True, False)
+    def test_in_29(self, is_in):
         a, b, c = (
             column("a", Integer),
             column("b", String),
             column("c", LargeBinary),
         )
         t1 = tuple_(a, b, c)
-        expr = t1.in_([(3, "hi", "there"), (4, "Q", "P")])
+        expr = t1.in_([(3, "hi", b"there"), (4, "Q", b"P")])
+        if not is_in:
+            expr = ~expr
+        self.assert_compile(
+            expr,
+            "(a, b, c) %s ([POSTCOMPILE_param_1])"
+            % ("IN" if is_in else "NOT IN"),
+            checkparams={"param_1": [(3, "hi", b"there"), (4, "Q", b"P")]},
+        )
         self.assert_compile(
             expr,
-            "(a, b, c) IN ([POSTCOMPILE_param_1])",
-            checkparams={"param_1": [(3, "hi", "there"), (4, "Q", "P")]},
+            "(a, b, c) %s ((3, 'hi', 'there'), (4, 'Q', 'P'))"
+            % ("IN" if is_in else "NOT IN"),
+            literal_binds=True,
+        )
+
+    @testing.combinations(True, False)
+    def test_in_empty_tuple(self, is_in):
+        a, b, c = (
+            column("a", Integer),
+            column("b", String),
+            column("c", LargeBinary),
+        )
+        t1 = tuple_(a, b, c)
+        expr = t1.in_([]) if is_in else t1.not_in([])
+        self.assert_compile(
+            expr,
+            "(a, b, c) %s ([POSTCOMPILE_param_1])"
+            % ("IN" if is_in else "NOT IN"),
+            checkparams={"param_1": []},
+        )
+        self.assert_compile(
+            expr,
+            "(a, b, c) %s (SELECT 1 WHERE 1!=1)"
+            % ("IN" if is_in else "NOT IN"),
+            literal_binds=True,
+            dialect="default_enhanced",
         )
 
     def test_in_set(self):