Commits

Mike Bayer  committed df343fa

- Fixed bug in query.options() whereby a path
applied to a lazyload using string keys could
overlap a same named attribute on the wrong
entity. Note 0.6 has a more conservative fix
to this. [ticket:2098]

  • Participants
  • Parent commits dfc4dea

Comments (0)

Files changed (4)

     where the parent entity is not fully present.  
     [ticket:2069]
 
+  - Fixed bug in query.options() whereby a path 
+    applied to a lazyload using string keys could 
+    overlap a same named attribute on the wrong 
+    entity.  Note 0.6 has a more conservative fix 
+    to this.  [ticket:2098]
+
 - sql
   - Added a fully descriptive error message for the
     case where Column is subclassed and _make_proxy()

File lib/sqlalchemy/orm/interfaces.py

         l = []
         mappers = []
 
-        # _current_path implies we're in a secondary load with an
-        # existing path
-
+        # _current_path implies we're in a 
+        # secondary load with an existing path
         current_path = list(query._current_path)
 
         tokens = deque(self.key)
                 sub_tokens = token.split(".", 1)
                 token = sub_tokens[0]
                 tokens.extendleft(sub_tokens[1:])
+
+                # exhaust current_path before
+                # matching tokens to entities
+                if current_path:
+                    if current_path[1] == token:
+                        current_path = current_path[2:]
+                        continue
+                    else:
+                        return [], []
+
                 if not entity:
-                    if current_path:
-                        if current_path[1] == token:
-                            current_path = current_path[2:]
-                            continue
-                    entity = self._find_entity_basestring(query, 
-                            token, raiseerr)
+                    entity = self._find_entity_basestring(
+                                        query, 
+                                        token, 
+                                        raiseerr)
                     path_element = entity.path_entity
                     mapper = entity.mapper
                 mappers.append(mapper)
                     prop = None
             elif isinstance(token, PropComparator):
                 prop = token.property
+
+                # exhaust current_path before
+                # matching tokens to entities
+                if current_path:
+                    if current_path[0:2] == \
+                            [token.parententity, prop.key]:
+                        current_path = current_path[2:]
+                        continue
+                    else:
+                        return [], []
+
                 if not entity:
-                    if current_path:
-                        if current_path[0:2] == [token.parententity,
-                                prop.key]:
-                            current_path = current_path[2:]
-                            continue
-                    entity = self._find_entity_prop_comparator(query,
-                            prop.key, token.parententity, raiseerr)
+                    entity = self._find_entity_prop_comparator(
+                                            query,
+                                            prop.key, 
+                                            token.parententity, 
+                                            raiseerr)
                     if not entity:
                         return [], []
                     path_element = entity.path_entity
                     mapper = entity.mapper
                 mappers.append(prop.parent)
             else:
-                raise sa_exc.ArgumentError('mapper option expects '
-                        'string key or list of attributes')
+                raise sa_exc.ArgumentError(
+                        "mapper option expects "
+                        "string key or list of attributes")
             if prop is None:
                 return [], []
             path = build_path(path_element, prop.key, path)
                 path_element = path_element
 
         if current_path:
+            # ran out of tokens before 
+            # current_path was exhausted.
+            assert not tokens
             return [], []
+
         return l, mappers
 
 

File test/orm/test_eager_relations.py

             ((joinedload("orders.items"), ), 10),
             ((
                 joinedload(User.orders, ), 
-                joinedload(User.orders, Order.items), 
+               joinedload(User.orders, Order.items), 
                 joinedload(User.orders, Order.items, Item.keywords), 
-            ), 1),
+           ), 1),
             ((
                 joinedload(User.orders, Order.items, Item.keywords), 
             ), 10),
             ((
-                joinedload(User.orders, Order.items), 
-                joinedload(User.orders, Order.items, Item.keywords), 
+               joinedload(User.orders, Order.items), 
+               joinedload(User.orders, Order.items, Item.keywords), 
             ), 5),
         ]:
             sess = create_session()

File test/orm/test_query.py

         opt = self._option_fixture(User.addresses)
         self._assert_path_result(opt, q, [(User, 'addresses')], [User])
 
+    def test_path_on_entity_but_doesnt_match_currentpath(self):
+        # ensure "current path" is fully consumed before
+        # matching against current entities.
+        # see [ticket:2098]
+        sess = Session()
+        q = sess.query(User)
+        opt = self._option_fixture('email_address', 'id')
+        q = sess.query(Address)._with_current_path([class_mapper(User), 'addresses'])
+        self._assert_path_result(opt, q, [], [])
+
     def test_get_path_one_level_with_unrelated(self):
         sess = Session()
         q = sess.query(Order)