From: Mike Bayer Date: Mon, 5 Apr 2021 21:37:56 +0000 (-0400) Subject: Detect (Entity, Entity) vs (Entity, onclause) in legacy join X-Git-Tag: rel_1_4_6~10^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=03122c8d060ddc8d8194017dc2cd7e4a1c09c1e1;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Detect (Entity, Entity) vs (Entity, onclause) in legacy join 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 --- diff --git a/doc/build/changelog/unreleased_14/6203.rst b/doc/build/changelog/unreleased_14/6203.rst new file mode 100644 index 0000000000..39076f1890 --- /dev/null +++ b/doc/build/changelog/unreleased_14/6203.rst @@ -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. diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 7e2fde7497..90ae1b700c 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -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)] diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py index 4c55525ab5..c7561e8046 100644 --- a/test/orm/test_deprecations.py +++ b/test/orm/test_deprecations.py @@ -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