]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
de-memoize _proxy_key when new annotations are added
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 12 Aug 2024 23:50:05 +0000 (19:50 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 13 Aug 2024 14:02:27 +0000 (10:02 -0400)
Fixed regression from 1.3 where the column key used for a hybrid property
might be populated with that of the underlying column that it returns, for
a property that returns an ORM mapped column directly, rather than the key
used by the hybrid property itself.

Fixes: #11728
Change-Id: Ifb298e46a20f90f6b6a717674f142a87cbceb468
(cherry picked from commit ffc7e8d73b30ea45fb03e0727b9fe96b6b8d4cfa)
(cherry picked from commit 5b1758a9bb8952adca91a95483ec1d11a66ad1e2)

doc/build/changelog/unreleased_14/11728.rst [new file with mode: 0644]
lib/sqlalchemy/sql/elements.py
test/ext/test_hybrid.py

diff --git a/doc/build/changelog/unreleased_14/11728.rst b/doc/build/changelog/unreleased_14/11728.rst
new file mode 100644 (file)
index 0000000..b27aa33
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, regression, orm
+    :tickets: 11728
+    :versions: 2.0.33
+
+    Fixed regression from 1.3 where the column key used for a hybrid property
+    might be populated with that of the underlying column that it returns, for
+    a property that returns an ORM mapped column directly, rather than the key
+    used by the hybrid property itself.
index 7671e75d48749e2e236d35d743f0998358eee5bf..96f2936fe783a880cf3e53187ee3fa5f9fc089bc 100644 (file)
@@ -5283,7 +5283,14 @@ class AnnotatedColumnElement(Annotated):
 
     def _with_annotations(self, values):
         clone = super(AnnotatedColumnElement, self)._with_annotations(values)
-        clone.__dict__.pop("comparator", None)
+        for attr in (
+            "comparator",
+            "_proxy_key",
+            "_tq_key_label",
+            "_tq_label",
+            "_non_anon_label",
+        ):
+            clone.__dict__.pop(attr, None)
         return clone
 
     @util.memoized_property
index be42fdb6d0e26740805ac6ac94f0f04cd051bcd8..caee584a0e7e4571b552d42c6c9c37525fb0f02c 100644 (file)
@@ -5,6 +5,7 @@ from sqlalchemy import ForeignKey
 from sqlalchemy import func
 from sqlalchemy import inspect
 from sqlalchemy import Integer
+from sqlalchemy import LABEL_STYLE_DISAMBIGUATE_ONLY
 from sqlalchemy import LABEL_STYLE_TABLENAME_PLUS_COL
 from sqlalchemy import literal_column
 from sqlalchemy import Numeric
@@ -322,6 +323,21 @@ class PropertyExpressionTest(fixtures.TestBase, AssertsCompiledSQL):
 
         return A
 
+    @testing.fixture
+    def _unnamed_expr_matches_col_fixture(self):
+        Base = declarative_base()
+
+        class A(Base):
+            __tablename__ = "a"
+            id = Column(Integer, primary_key=True)
+            foo = Column(String)
+
+            @hybrid.hybrid_property
+            def bar(self):
+                return self.foo
+
+        return A
+
     def test_labeling_for_unnamed(self, _unnamed_expr_fixture):
         A = _unnamed_expr_fixture
 
@@ -341,6 +357,39 @@ class PropertyExpressionTest(fixtures.TestBase, AssertsCompiledSQL):
             "a.lastname AS name FROM a) AS anon_1",
         )
 
+    @testing.variation("pre_populate_col_proxy", [True, False])
+    def test_labeling_for_unnamed_matches_col(
+        self, _unnamed_expr_matches_col_fixture, pre_populate_col_proxy
+    ):
+        """test #11728"""
+
+        A = _unnamed_expr_matches_col_fixture
+
+        if pre_populate_col_proxy:
+            pre_stmt = select(A.id, A.foo)
+            pre_stmt.subquery().c
+
+        stmt = select(A.id, A.bar)
+        self.assert_compile(
+            stmt,
+            "SELECT a.id, a.foo FROM a",
+        )
+
+        compile_state = stmt._compile_state_factory(stmt, None)
+        eq_(
+            compile_state._column_naming_convention(
+                LABEL_STYLE_DISAMBIGUATE_ONLY, legacy=False
+            )(list(stmt.inner_columns)[1]),
+            "bar",
+        )
+        eq_(stmt.subquery().c.keys(), ["id", "bar"])
+
+        self.assert_compile(
+            select(stmt.subquery()),
+            "SELECT anon_1.id, anon_1.foo FROM "
+            "(SELECT a.id AS id, a.foo AS foo FROM a) AS anon_1",
+        )
+
     def test_labeling_for_unnamed_tablename_plus_col(
         self, _unnamed_expr_fixture
     ):