From: Mike Bayer Date: Fri, 2 Apr 2021 15:06:28 +0000 (-0400) Subject: Ensure Grouping._with_binary_element_type() maintains class X-Git-Tag: rel_1_4_5~2^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a16687b3fc4179e8f42d8099aa6d1e6d18fedf70;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Ensure Grouping._with_binary_element_type() maintains class 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 --- diff --git a/doc/build/changelog/unreleased_14/6181.rst b/doc/build/changelog/unreleased_14/6181.rst new file mode 100644 index 0000000000..5881321b32 --- /dev/null +++ b/doc/build/changelog/unreleased_14/6181.rst @@ -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. + diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 7a27690b89..c483383033 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -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): diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py index f6a13f8ca0..0d7f331e0e 100644 --- a/test/sql/test_operators.py +++ b/test/sql/test_operators.py @@ -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" diff --git a/test/sql/test_roles.py b/test/sql/test_roles.py index d5e8f476c4..e47a7e8897 100644 --- a/test/sql/test_roles.py +++ b/test/sql/test_roles.py @@ -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,