]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Ensure Grouping._with_binary_element_type() maintains class
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Apr 2021 15:06:28 +0000 (11:06 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Apr 2021 15:08:10 +0000 (11:08 -0400)
Fixed regression where use of the :meth:`.Operators.in_` method with a
:class:`_sql.Select` object against a non-table-bound column would produce
an ``AttributeError``, or more generally using a :class:`_sql.ScalarSelect`
that has no datatype in a binary expression would produce invalid state.

Fixes: #6181
Change-Id: I1ddea433b3603fdab8f489bff571b512a6ffc66b

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

diff --git a/doc/build/changelog/unreleased_14/6181.rst b/doc/build/changelog/unreleased_14/6181.rst
new file mode 100644 (file)
index 0000000..5881321
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, regression, sql
+    :tickets: 6181
+
+    Fixed regression where use of the :meth:`.Operators.in_` method with a
+    :class:`_sql.Select` object against a non-table-bound column would produce
+    an ``AttributeError``, or more generally using a :class:`_sql.ScalarSelect`
+    that has no datatype in a binary expression would produce invalid state.
+
index 7a27690b8969697324629c06f120feff6f9790c5..c4833830337c352e36bf2a138cd859a77f193204 100644 (file)
@@ -3764,7 +3764,7 @@ class Grouping(GroupedElement, ColumnElement):
         self.type = getattr(element, "type", type_api.NULLTYPE)
 
     def _with_binary_element_type(self, type_):
-        return Grouping(self.element._with_binary_element_type(type_))
+        return self.__class__(self.element._with_binary_element_type(type_))
 
     @util.memoized_property
     def _is_implicitly_boolean(self):
index f6a13f8ca0f010e64c93034da2f37bb9f2dfca94..0d7f331e0e2df9caa6100919128533ce455222a9 100644 (file)
@@ -1973,6 +1973,22 @@ class InTest(fixtures.TestBase, testing.AssertsCompiledSQL):
             checkparams={"myid_1": [1, 2, 3]},
         )
 
+    def test_scalar_subquery_wo_type(self):
+        """ test for :ticket:`6181` """
+
+        m = MetaData()
+        t = Table("t", m, Column("a", Integer))
+
+        # the scalar subquery of this will have no type; coercions will
+        # want to call _with_binary_element_type(); that has to return
+        # a scalar select
+        req = select(column("scan"))
+
+        self.assert_compile(
+            select(t.c.a).where(t.c.a.in_(req)),
+            "SELECT t.a FROM t WHERE t.a IN (SELECT scan)",
+        )
+
 
 class MathOperatorTest(fixtures.TestBase, testing.AssertsCompiledSQL):
     __dialect__ = "default"
index d5e8f476c498e6e156ff50cf95c3d8647d73ffd2..e47a7e88973c45fa91cbcdb8f109db50b95ae1e5 100644 (file)
@@ -26,6 +26,7 @@ from sqlalchemy.sql.coercions import expect
 from sqlalchemy.sql.elements import _truncated_label
 from sqlalchemy.sql.elements import Null
 from sqlalchemy.sql.selectable import FromGrouping
+from sqlalchemy.sql.selectable import ScalarSelect
 from sqlalchemy.sql.selectable import SelectStatementGrouping
 from sqlalchemy.testing import assert_raises
 from sqlalchemy.testing import assert_raises_message
@@ -145,6 +146,19 @@ class RoleTest(fixtures.TestBase):
             )
         )
 
+    def test_untyped_scalar_subquery(self):
+        """test for :ticket:`6181` """
+
+        c = column("q")
+        subq = select(c).scalar_subquery()
+
+        assert isinstance(
+            subq._with_binary_element_type(Integer()), ScalarSelect
+        )
+
+        expr = column("a", Integer) == subq
+        assert isinstance(expr.right, ScalarSelect)
+
     def test_no_clauseelement_in_bind(self):
         with testing.expect_raises_message(
             exc.ArgumentError,