From: Mike Bayer Date: Wed, 20 Jul 2011 22:03:22 +0000 (-0400) Subject: - Fixed bug whereby the source clause X-Git-Tag: rel_0_6_9~44 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=78255276181d39ea74d850ad8fad31d11ff98fdf;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug whereby the source clause used by query.join() would be inconsistent if against a column expression that combined multiple entities together. [ticket:2197] --- diff --git a/CHANGES b/CHANGES index f7d5db828a..bfe5ceaafd 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,11 @@ CHANGES 0.6.9 ===== - orm + - Fixed bug whereby the source clause + used by query.join() would be inconsistent + if against a column expression that combined + multiple entities together. [ticket:2197] + - Fixed bug whereby if a mapped class redefined __hash__() or __eq__() to something non-standard, which is a supported use case diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 956136941c..020eb36972 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -2796,7 +2796,8 @@ class _ColumnEntity(_QueryEntity): def setup_entity(self, entity, mapper, adapter, from_obj, is_aliased_class, with_polymorphic): - self.selectable = from_obj + if 'selectable' not in self.__dict__: + self.selectable = from_obj self.froms.add(from_obj) def corresponds_to(self, entity): diff --git a/test/orm/test_query.py b/test/orm/test_query.py index d8c2c19233..9c89b837b9 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -3040,6 +3040,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL): class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): + __dialect__ = 'default' def test_values(self): sess = create_session() @@ -3411,7 +3412,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): "orders.description AS orders_description, " "'q' AS foo FROM orders WHERE " "orders.description = :description_1) AS " - "anon_1", use_default_dialect=True) + "anon_1") eq_( q.all(), [(3, u'order 3', 'q')] @@ -3501,8 +3502,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): ' AS addresses_email_address FROM users, ' 'addresses WHERE users.id = :id_1 AND ' 'addresses.user_id = users.id ORDER BY ' - 'users.name', - use_default_dialect=True) + 'users.name') def test_multi_columns(self): @@ -3608,6 +3608,61 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): assert q.all() == expected sess.expunge_all() + def test_expression_selectable_matches_mzero(self): + ua = aliased(User) + aa = aliased(Address) + s = create_session() + for crit, j, exp in [ + (User.id + Address.id, User.addresses, + "SELECT users.id + addresses.id AS anon_1 " + "FROM users JOIN addresses ON users.id = " + "addresses.user_id" + ), + (User.id + Address.id, Address.user, + "SELECT users.id + addresses.id AS anon_1 " + "FROM addresses JOIN users ON users.id = " + "addresses.user_id" + ), + (Address.id + User.id, User.addresses, + "SELECT addresses.id + users.id AS anon_1 " + "FROM users JOIN addresses ON users.id = " + "addresses.user_id" + ), + (User.id + aa.id, (aa, User.addresses), + "SELECT users.id + addresses_1.id AS anon_1 " + "FROM users JOIN addresses AS addresses_1 " + "ON users.id = addresses_1.user_id" + ), + ]: + q = s.query(crit) + mzero = q._mapper_zero() + assert mzero.mapped_table is q._entity_zero().selectable + q = q.join(j) + self.assert_compile(q, exp) + + for crit, j, exp in [ + (ua.id + Address.id, ua.addresses, + "SELECT users_1.id + addresses.id AS anon_1 " + "FROM users AS users_1 JOIN addresses " + "ON users_1.id = addresses.user_id"), + (ua.id + aa.id, (aa, ua.addresses), + "SELECT users_1.id + addresses_1.id AS anon_1 " + "FROM users AS users_1 JOIN addresses AS " + "addresses_1 ON users_1.id = addresses_1.user_id"), + (ua.id + aa.id, (ua, aa.user), + "SELECT users_1.id + addresses_1.id AS anon_1 " + "FROM addresses AS addresses_1 JOIN " + "users AS users_1 " + "ON users_1.id = addresses_1.user_id") + ]: + q = s.query(crit) + mzero = q._mapper_zero() + assert mzero._AliasedClass__alias is q._entity_zero().selectable + q = q.join(j) + self.assert_compile(q, exp) + + + class ImmediateTest(_fixtures.FixtureTest): run_inserts = 'once' run_deletes = None