]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
propagate proxy_key from WrapsColumnExpression
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 1 Jun 2022 16:11:19 +0000 (12:11 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 1 Jun 2022 16:11:19 +0000 (12:11 -0400)
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

doc/build/changelog/unreleased_14/8084.rst [new file with mode: 0644]
lib/sqlalchemy/sql/elements.py
test/sql/test_compiler.py
test/sql/test_labels.py

diff --git a/doc/build/changelog/unreleased_14/8084.rst b/doc/build/changelog/unreleased_14/8084.rst
new file mode 100644 (file)
index 0000000..43095e8
--- /dev/null
@@ -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.
index 61c5379d8acafea5cad81fd3dd4a2beb73821a7e..f9ede8a7160328d7230d988c15987424a8410c79 100644 (file)
@@ -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]")
 
index 6ad2aa2c14f63783637d60d9fc6424d433a887c8..94c38548f7a69798ce35ea065fedc98c442f74f2 100644 (file)
@@ -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:
index 8c8e9dbeda358b8ecefb590e4658b85feb178970..d385b9e8d14993245c4978a0ed926b0251269daf 100644 (file)
@@ -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