]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Detect (Entity, Entity) vs (Entity, onclause) in legacy join
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Apr 2021 21:37:56 +0000 (17:37 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Apr 2021 23:26:12 +0000 (19:26 -0400)
Fixed regression where a deprecated form of :meth:`_orm.Query.join` were
used, passing a series of entities to join from without any ON clause in a
single :meth:`_orm.Query.join` call, would fail to function correctly.

Fixes: #6203
Change-Id: I5a6ec80de972af5b2ca9054e6f24a0b8af4a3e13

doc/build/changelog/unreleased_14/6203.rst [new file with mode: 0644]
lib/sqlalchemy/orm/query.py
test/orm/test_deprecations.py

diff --git a/doc/build/changelog/unreleased_14/6203.rst b/doc/build/changelog/unreleased_14/6203.rst
new file mode 100644 (file)
index 0000000..39076f1
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: bug, orm, regression
+    :tickets: 6203
+
+    Fixed regression where a deprecated form of :meth:`_orm.Query.join` were
+    used, passing a series of entities to join from without any ON clause in a
+    single :meth:`_orm.Query.join` call, would fail to function correctly.
index 7e2fde74972c4d30d9023fff904d2d8e0e8d718c..90ae1b700c148a6a245cd281f0a933befed3609f 100644 (file)
@@ -22,7 +22,6 @@ import itertools
 import operator
 import types
 
-from sqlalchemy.sql import visitors
 from . import exc as orm_exc
 from . import interfaces
 from . import loading
@@ -48,10 +47,12 @@ from .. import log
 from .. import sql
 from .. import util
 from ..sql import coercions
+from ..sql import elements
 from ..sql import expression
 from ..sql import roles
 from ..sql import Select
 from ..sql import util as sql_util
+from ..sql import visitors
 from ..sql.annotation import SupportsCloneAnnotations
 from ..sql.base import _entity_namespace_key
 from ..sql.base import _generative
@@ -2167,6 +2168,8 @@ class Query(
                 (Item, Item.order_id == Order.id)
             )
 
+            session.query(User).join(Order, Item)
+
             # ... and several more forms actually
 
           **Why it's legacy**: being able to chain multiple ON clauses in one
@@ -2259,9 +2262,26 @@ class Query(
         if not legacy and onclause is None and not isinstance(target, tuple):
             # non legacy argument form
             _props = [(target,)]
-        elif not legacy and isinstance(
-            target,
-            (expression.Selectable, type, AliasedClass, types.FunctionType),
+        elif (
+            not legacy
+            and isinstance(
+                target,
+                (
+                    expression.Selectable,
+                    type,
+                    AliasedClass,
+                    types.FunctionType,
+                ),
+            )
+            and isinstance(
+                onclause,
+                (
+                    elements.ColumnElement,
+                    str,
+                    interfaces.PropComparator,
+                    types.FunctionType,
+                ),
+            )
         ):
             # non legacy argument form
             _props = [(target, onclause)]
index 4c55525ab55bd7a6c0dcf8560c71402ed7892259..c7561e8046d74417ba40fcdada21e0676f7dd201 100644 (file)
@@ -238,6 +238,37 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
             "addresses.user_id JOIN users ON users.id = addresses.user_id",
         )
 
+    def test_multiple_entities(self):
+        User = self.classes.User
+        Address = self.classes.Address
+        Dingaling = self.classes.Dingaling
+
+        s = fixture_session()
+
+        u1 = aliased(User)
+
+        with testing.expect_deprecated_20(join_chain_dep):
+            q1 = s.query(u1).join(Address, User)
+
+        self.assert_compile(
+            q1,
+            "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
+            "FROM users AS users_1 JOIN addresses ON users_1.id = "
+            "addresses.user_id JOIN users ON users.id = addresses.user_id",
+        )
+
+        with testing.expect_deprecated_20(join_chain_dep):
+            q1 = s.query(u1).join(Address, User, Dingaling)
+
+        self.assert_compile(
+            q1,
+            "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
+            "FROM users AS users_1 JOIN addresses "
+            "ON users_1.id = addresses.user_id "
+            "JOIN users ON users.id = addresses.user_id "
+            "JOIN dingalings ON addresses.id = dingalings.address_id",
+        )
+
     def test_str_join_target(self):
         User = self.classes.User