Mike Bayer avatar Mike Bayer committed 1db7766

- Column-entities (i.e. query(Foo.id)) copy their
state more fully when queries are derived from
themselves + a selectable (i.e. from_self(),
union(), etc.), so that join() and such have the
correct state to work from. [ticket:1853]

- Fixed bug where Query.join() would fail if
querying a non-ORM column then joining without
an on clause when a FROM clause is already
present, now raises a checked exception the
same way it does when the clause is not
present. [ticket:1853]

Comments (0)

Files changed (3)

     and a delete of the parent has occurred.
     [ticket:1845]
 
+  - Column-entities (i.e. query(Foo.id)) copy their 
+    state more fully when queries are derived from 
+    themselves + a selectable (i.e. from_self(), 
+    union(), etc.), so that join() and such have the 
+    correct state to work from.  [ticket:1853]
+    
+  - Fixed bug where Query.join() would fail if
+    querying a non-ORM column then joining without
+    an on clause when a FROM clause is already
+    present, now raises a checked exception the 
+    same way it does when the clause is not
+    present.  [ticket:1853]
+
   - Improved the check for an "unmapped class", 
     including the case where the superclass is mapped
     but the subclass is not.  Any attempts to access

lib/sqlalchemy/orm/query.py

         
         join_to_left = not is_aliased_class and not left_is_aliased
 
-        if self._from_obj:
+        if self._from_obj and left_selectable is not None:
             replace_clause_index, clause = sql_util.find_join_source(
                                                     self._from_obj, 
                                                     left_selectable)
         return self.column.type
         
     def adapt_to_selectable(self, query, sel):
-        _ColumnEntity(query, sel.corresponding_column(self.column))
+        c = _ColumnEntity(query, sel.corresponding_column(self.column))
+        c.entity_zero = self.entity_zero
+        c.entities = self.entities
         
     def setup_entity(self, entity, mapper, adapter, from_obj,
                                 is_aliased_class, with_polymorphic):

test/orm/test_query.py

                 (order3, item3),
             ]
         )
+    
+    def test_joins_from_adapted_entities(self):
+
+        # test for #1853
+
+        session = create_session()
+        first = session.query(User)
+        second = session.query(User)
+        unioned = first.union(second)
+        subquery = session.query(User.id).subquery()
+        join = subquery, subquery.c.id == User.id
+        joined = unioned.outerjoin(join)
+        self.assert_compile(joined,
+                            'SELECT anon_1.users_id AS '
+                            'anon_1_users_id, anon_1.users_name AS '
+                            'anon_1_users_name FROM (SELECT users.id '
+                            'AS users_id, users.name AS users_name '
+                            'FROM users UNION SELECT users.id AS '
+                            'users_id, users.name AS users_name FROM '
+                            'users) AS anon_1 LEFT OUTER JOIN (SELECT '
+                            'users.id AS id FROM users) AS anon_2 ON '
+                            'anon_2.id = anon_1.users_id',
+                            use_default_dialect=True)
+
+        first = session.query(User.id)
+        second = session.query(User.id)
+        unioned = first.union(second)
+        subquery = session.query(User.id).subquery()
+        join = subquery, subquery.c.id == User.id
+        joined = unioned.outerjoin(join)
+        self.assert_compile(joined,
+                            'SELECT anon_1.users_id AS anon_1_users_id '
+                            'FROM (SELECT users.id AS users_id FROM '
+                            'users UNION SELECT users.id AS users_id '
+                            'FROM users) AS anon_1 LEFT OUTER JOIN '
+                            '(SELECT users.id AS id FROM users) AS '
+                            'anon_2 ON anon_2.id = anon_1.users_id',
+                            use_default_dialect=True)
         
     def test_reset_joinpoint(self):
         for aliased in (True, False):
             sess.query(User.name).join((addresses, User.id==addresses.c.user_id)).order_by(User.id).all(),
             [(u'jack',), (u'ed',), (u'ed',), (u'ed',), (u'fred',)]
         )
+    
+    def test_no_joinpoint_expr(self):
+        sess = create_session()
+        
+        # these are consistent regardless of
+        # select_from() being present.
+        
+        assert_raises_message(
+            sa_exc.InvalidRequestError,
+            "Could not find a FROM",
+            sess.query(users.c.id).join, User
+        )
+        
+        assert_raises_message(
+            sa_exc.InvalidRequestError,
+            "Could not find a FROM",
+            sess.query(users.c.id).select_from(users).join, User
+        )
         
     def test_from_self_resets_joinpaths(self):
         """test a join from from_self() doesn't confuse joins inside the subquery
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.