From: Mike Bayer Date: Wed, 1 Jun 2022 16:11:19 +0000 (-0400) Subject: propagate proxy_key from WrapsColumnExpression X-Git-Tag: rel_1_4_38~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1b82a9b4f3157ada1b6077679ec393ebe173120;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 (cherry picked from commit 14250f2668151f1c4df86dbf962c771e9788111e) --- 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 da9c5f6b56..0462b26482 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -1201,6 +1201,14 @@ class WrapsColumnExpression(object): 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(WrapsColumnExpression, self)._proxy_key + class BindParameter(roles.InElementRole, ColumnElement): r"""Represent a "bound expression". diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index 250e8b30cf..4db5f3df9d 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -3208,7 +3208,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] ), @@ -3232,6 +3232,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