]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
use coercions for label element, ensure propagate_attrs
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 18 Oct 2021 19:43:27 +0000 (15:43 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 18 Oct 2021 19:55:12 +0000 (15:55 -0400)
Fixed bug where the ORM "plugin", necessary for features such as
:func:`_orm.with_loader_criteria` to work correctly, would not be applied
to a :func:`_sql.select` which queried from an ORM column expression if it
made use of the :meth:`_sql.ColumnElement.label` modifier.

Fixes: #7205
Change-Id: I72b84442e14df8b5ece33916f3c51ca3f358864b

doc/build/changelog/unreleased_14/7205.rst [new file with mode: 0644]
lib/sqlalchemy/sql/elements.py
test/orm/test_relationship_criteria.py

diff --git a/doc/build/changelog/unreleased_14/7205.rst b/doc/build/changelog/unreleased_14/7205.rst
new file mode 100644 (file)
index 0000000..cdc3799
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 7205
+
+    Fixed bug where the ORM "plugin", necessary for features such as
+    :func:`_orm.with_loader_criteria` to work correctly, would not be applied
+    to a :func:`_sql.select` which queried from an ORM column expression if it
+    made use of the :meth:`_sql.ColumnElement.label` modifier.
+
+
index e49665019a36940ca8af0fe1432df0e78753c359..3699f872bb4d9618726387030cc511277d508016 100644 (file)
@@ -4521,19 +4521,31 @@ class Label(roles.LabeledColumnExprRole, ColumnElement):
 
         """
 
-        if isinstance(element, Label):
-            self._resolve_label = element._label
-
+        orig_element = element
+        element = coercions.expect(
+            roles.ExpressionElementRole,
+            element,
+            apply_propagate_attrs=self,
+        )
         while isinstance(element, Label):
+            # TODO: this is only covered in test_text.py, but nothing
+            # fails if it's removed.  determine rationale
             element = element.element
 
         if name:
             self.name = name
+
+            # TODO: nothing fails if this is removed.  this is related
+            # to the order_by() string feature tested in test_text.py.
             self._resolve_label = self.name
         else:
             self.name = _anonymous_label.safe_construct(
                 id(self), getattr(element, "name", "anon")
             )
+            if isinstance(orig_element, Label):
+                # TODO: no coverage for this block, again would be in
+                # test_text.py where the resolve_label concept is important
+                self._resolve_label = orig_element._label
 
         self.key = self._tq_label = self._tq_key_label = self.name
         self._element = element
index cccf91f7b6aadee93ea3e19829271e43cd2d747c..86f7e9fc91983ddc6a962bfc1ea37e8e6ffe2cbd 100644 (file)
@@ -7,6 +7,7 @@ from sqlalchemy import event
 from sqlalchemy import ForeignKey
 from sqlalchemy import func
 from sqlalchemy import Integer
+from sqlalchemy import literal_column
 from sqlalchemy import orm
 from sqlalchemy import select
 from sqlalchemy import sql
@@ -26,6 +27,7 @@ from sqlalchemy.orm.decl_api import declared_attr
 from sqlalchemy.testing import eq_
 from sqlalchemy.testing.assertsql import CompiledSQL
 from sqlalchemy.testing.fixtures import fixture_session
+from sqlalchemy.testing.util import resolve_lambda
 from test.orm import _fixtures
 
 
@@ -450,6 +452,40 @@ class LoaderCriteriaTest(_Fixtures, testing.AssertsCompiledSQL):
             "FROM users AS users_1 WHERE users_1.name != :name_1",
         )
 
+    @testing.combinations(
+        (lambda User: [User.id], "users.id"),
+        (lambda User: [User.id.label("foo")], "users.id AS foo"),
+        (lambda User: [User.name + "bar"], "users.name || :name_1 AS anon_1"),
+        (
+            lambda User: [(User.name + "bar").label("foo")],
+            "users.name || :name_1 AS foo",
+        ),
+        (lambda User: [func.count(User.id)], "count(users.id) AS count_1"),
+        (
+            lambda User: [func.count(User.id).label("foo")],
+            "count(users.id) AS foo",
+        ),
+        argnames="case, expected",
+    )
+    def test_select_expr_with_criteria(
+        self, case, expected, user_address_fixture
+    ):
+        """test #7205"""
+        User, Address = user_address_fixture
+
+        stmt = select(*resolve_lambda(case, User=User)).options(
+            # use non-bound value so that we dont have to accommodate for
+            # the "anon" counter
+            with_loader_criteria(
+                User, User.name != literal_column("some_crit")
+            )
+        )
+
+        self.assert_compile(
+            stmt,
+            "SELECT %s FROM users WHERE users.name != some_crit" % (expected,),
+        )
+
     def test_select_from_aliased_inclaliased_criteria(
         self, user_address_fixture
     ):