From: Mike Bayer Date: Wed, 1 Jun 2022 16:11:19 +0000 (-0400) Subject: propagate proxy_key from WrapsColumnExpression X-Git-Tag: rel_2_0_0b1~272^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14250f2668151f1c4df86dbf962c771e9788111e;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git propagate proxy_key from WrapsColumnExpression this allows cast() of a label() to propagate the proxy key outwards in the same way that it apparently works at the SQL level. This is stuffing even more rules into naming so basically seeing how far we can go without other cases starting to fail. Fixes: #8084 Change-Id: I20bd97dae798fee6492334c06934e807d0f269ef --- diff --git a/doc/build/changelog/unreleased_14/8084.rst b/doc/build/changelog/unreleased_14/8084.rst new file mode 100644 index 0000000000..43095e8c93 --- /dev/null +++ b/doc/build/changelog/unreleased_14/8084.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, sql + :tickets: 8084 + + Enhanced the mechanism of :class:`.Cast` and other "wrapping" + column constructs to more fully preserve a wrapped :class:`.Label` + construct, including that the label name will be preserved in the + ``.c`` collection of a :class:`.Subquery`. The label was already + able to render in the SQL correctly on the outside of the construct + which it was wrapped inside. diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 61c5379d8a..f9ede8a716 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -1768,6 +1768,14 @@ class WrapsColumnExpression(ColumnElement[_T]): else: return self._dedupe_anon_tq_label_idx(idx) + @property + def _proxy_key(self): + wce = self.wrapped_column_expression + + if not wce._is_text_clause: + return wce._proxy_key + return super()._proxy_key + SelfBindParameter = TypeVar("SelfBindParameter", bound="BindParameter[Any]") diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index 6ad2aa2c14..94c38548f7 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -3298,7 +3298,7 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL): (exprs[1], "hoho", "hoho(mytable.myid)", "hoho_1"), ( exprs[2], - "_no_label", + "name", "CAST(mytable.name AS NUMERIC)", "name", # due to [ticket:4449] ), @@ -3322,6 +3322,7 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL): t = table1 s1 = select(col).select_from(t) + eq_(col._proxy_key, key if key != "_no_label" else None) eq_(list(s1.subquery().c.keys()), [key]) if lbl: diff --git a/test/sql/test_labels.py b/test/sql/test_labels.py index 8c8e9dbeda..d385b9e8d1 100644 --- a/test/sql/test_labels.py +++ b/test/sql/test_labels.py @@ -26,6 +26,7 @@ from sqlalchemy.testing import AssertsCompiledSQL from sqlalchemy.testing import engines from sqlalchemy.testing import eq_ from sqlalchemy.testing import fixtures +from sqlalchemy.testing import is_ from sqlalchemy.testing import mock from sqlalchemy.testing.schema import Column from sqlalchemy.testing.schema import Table @@ -938,6 +939,33 @@ class ColExprLabelTest(fixtures.TestBase, AssertsCompiledSQL): "some_table.name FROM some_table", ) + @testing.combinations("inside", "outside") + def test_wraps_col_expr_label_propagate(self, cast_location): + """test #8084""" + + table1 = self.table1 + + if cast_location == "inside": + expr = cast(table1.c.name, Integer).label("foo") + elif cast_location == "outside": + expr = cast(table1.c.name.label("foo"), Integer) + else: + assert False + + self.assert_compile( + select(expr), + "SELECT CAST(some_table.name AS INTEGER) AS foo FROM some_table", + ) + is_(select(expr).selected_columns.foo, expr) + + subq = select(expr).subquery() + self.assert_compile( + select(subq).where(subq.c.foo == 10), + "SELECT anon_1.foo FROM (SELECT CAST(some_table.name AS INTEGER) " + "AS foo FROM some_table) AS anon_1 WHERE anon_1.foo = :foo_1", + checkparams={"foo_1": 10}, + ) + def test_type_coerce_auto_label_label_style_disambiguate(self): table1 = self.table1