]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- fixed bug where Query would not apply a subquery to the SQL when LIMIT
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 29 Nov 2007 15:36:13 +0000 (15:36 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 29 Nov 2007 15:36:13 +0000 (15:36 +0000)
was used in conjunction with an inheriting mapper where the eager
loader was only in the parent mapper.

CHANGES
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/query.py
test/orm/eager_relations.py
test/orm/inheritance/polymorph2.py

diff --git a/CHANGES b/CHANGES
index d6607e935df14afeebd634a615626421831a96b7..94368253cd6a7fee4ec9b80ec0474b1d43d9f33b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -32,6 +32,10 @@ CHANGES
    - fixed endless loop issue when using lazy="dynamic" on both 
      sides of a bi-directional relationship [ticket:872]
 
+   - fixed bug where Query would not apply a subquery to the SQL when LIMIT
+     was used in conjunction with an inheriting mapper where the eager 
+     loader was only in the parent mapper.
+     
    - clarified the error message which occurs when you try to update()
      an instance with the same identity key as an instance already present
      in the session.
index fef1aaf09fb05ef6667044789f8172fc021ef78c..570251c69cc891c6311e628a296e619a769ce1bb 100644 (file)
@@ -849,13 +849,6 @@ class Mapper(object):
 
         raise exceptions.InvalidRequestError("No contextual Session is established.  Use a MapperExtension that implements get_session or use 'import sqlalchemy.mods.threadlocal' to establish a default thread-local contextual session.")
             
-    def has_eager(self):
-        """Return True if one of the properties attached to this
-        Mapper is eager loading.
-        """
-
-        return len(self._eager_loaders) > 0
-
     def instances(self, cursor, session, *mappers, **kwargs):
         """Return a list of mapped instances corresponding to the rows
         in a given ResultProxy.
index 3daf11ed0c5186ac09f59aa04a60073e9aacd480..e6dc0a91190b7fc3574e01fa0f0e09b0b8684f09 100644 (file)
@@ -6,10 +6,10 @@
 
 from sqlalchemy import sql, util, exceptions, logging
 from sqlalchemy.sql import util as sql_util
-from sqlalchemy.sql import expression, visitors
+from sqlalchemy.sql import expression, visitors, operators
 from sqlalchemy.orm import mapper, object_mapper
 from sqlalchemy.orm import util as mapperutil
-import operator
+from itertools import chain
 
 __all__ = ['Query', 'QueryContext']
 
@@ -46,7 +46,7 @@ class Query(object):
         self._populate_existing = False
         self._version_check = False
         self._autoflush = True
-        self._eager_loaders = util.Set([x for x in self.mapper._eager_loaders])
+        self._eager_loaders = util.Set(chain(*[mapper._eager_loaders for mapper in [m for m in self.mapper.iterate_to_root()]]))
         self._attributes = {}
         self._current_path = ()
         self._primary_adapter=None
@@ -135,7 +135,7 @@ class Query(object):
         mapper = object_mapper(instance)
         prop = mapper.get_property(property, resolve_synonyms=True)
         target = prop.mapper
-        criterion = prop.compare(operator.eq, instance, value_is_parent=True)
+        criterion = prop.compare(operators.eq, instance, value_is_parent=True)
         return Query(target, **kwargs).filter(criterion)
     query_from_parent = classmethod(query_from_parent)
     
@@ -185,7 +185,7 @@ class Query(object):
                 raise exceptions.InvalidRequestError("Could not locate a property which relates instances of class '%s' to instances of class '%s'" % (self.mapper.class_.__name__, instance.__class__.__name__))
         else:
             prop = mapper.get_property(property, resolve_synonyms=True)
-        return self.filter(prop.compare(operator.eq, instance, value_is_parent=True))
+        return self.filter(prop.compare(operators.eq, instance, value_is_parent=True))
 
     def add_entity(self, entity, alias=None, id=None):
         """add a mapped entity to the list of result columns to be returned.
@@ -319,7 +319,7 @@ class Query(object):
     def filter_by(self, **kwargs):
         """apply the given filtering criterion to the query and return the newly resulting ``Query``."""
 
-        clauses = [self._joinpoint.get_property(key, resolve_synonyms=True).compare(operator.eq, value)
+        clauses = [self._joinpoint.get_property(key, resolve_synonyms=True).compare(operators.eq, value)
             for key, value in kwargs.iteritems()]
         
         return self.filter(sql.and_(*clauses))
@@ -1154,9 +1154,9 @@ class Query(object):
         for key, value in params.iteritems():
             (keys, prop) = self._locate_prop(key, start=start)
             if isinstance(prop, properties.PropertyLoader):
-                c = prop.compare(operator.eq, value) & self.join_via(keys[:-1])
+                c = prop.compare(operators.eq, value) & self.join_via(keys[:-1])
             else:
-                c = prop.compare(operator.eq, value) & self.join_via(keys)
+                c = prop.compare(operators.eq, value) & self.join_via(keys)
             if clause is None:
                 clause =  c
             else:
index 068f09d021f4f84d2aec031eb5e2d0c03574fc0f..076f44005abaa1148cdcb185f9187ff8611a382b 100644 (file)
@@ -772,6 +772,5 @@ class CyclicalInheritingEagerTest(ORMTest):
         # testing a particular endless loop condition in eager join setup
         create_session().query(SubT).all()
 
-
 if __name__ == '__main__':
     testbase.main()
index 117bf7031f43690d6dab625da1cdb125dbb51ccd..6454ab860606fff7c5716fde3d66223ee74653c2 100644 (file)
@@ -3,7 +3,7 @@ from sqlalchemy import *
 from sqlalchemy import exceptions, util
 from sqlalchemy.orm import *
 from testlib import *
-
+from testlib import fixtures
 
 class AttrSettable(object):
     def __init__(self, **kwargs):
@@ -351,16 +351,6 @@ class RelationTest4(ORMTest):
         print class_mapper(Person).primary_key
         print person_mapper.get_select_mapper().primary_key
         
-        # so the primaryjoin is "people.c.person_id==cars.c.owner".  the "lazy" clause will be
-        # "people.c.person_id=?".  the employee_join is two selects union'ed together, one of which 
-        # will contain employee.c.person_id the other contains manager.c.person_id.  people.c.person_id is not explicitly in 
-        # either column clause in this case.  we can modify polymorphic_union to always put the "base" column in which would fix this,
-        # but im not sure if that really fixes the issue in all cases and its too far from the problem.
-        # instead, when the primaryjoin is adapted to point to the polymorphic union and is targeting employee_join.c.person_id, 
-        # it has to use not just straight column correspondence but also "keys_ok=True", meaning it will link up to any column 
-        # with the name "person_id", as opposed to columns that descend directly from people.c.person_id.  polymorphic unions
-        # require the cols all match up on column name which then determine the top selectable names, so matching by name is OK.
-
         session = create_session()
 
         # creating 5 managers named from M1 to E5
@@ -949,6 +939,68 @@ class CustomPKTest(ORMTest):
         ot1 = sess.query(T1).get(ot1.id)
         ot1.data = 'hi'
         sess.flush()
+
+class InheritingEagerTest(ORMTest):
+    def define_tables(self, metadata):
+        global people, employees, tags, peopleTags
+        
+        people = Table('people', metadata,
+                           Column('id', Integer, primary_key=True),
+                           Column('_type', String(30), nullable=False),
+                          )
+
+
+        employees = Table('employees', metadata,
+                         Column('id', Integer, ForeignKey('people.id'),primary_key=True),
+                        )
+
+        tags = Table('tags', metadata,
+                           Column('id', Integer, primary_key=True),
+                           Column('label', String, nullable=False),
+                       )
+
+        peopleTags = Table('peopleTags', metadata,
+                               Column('person_id', Integer,ForeignKey('people.id')),
+                               Column('tag_id', Integer,ForeignKey('tags.id')),
+                         )
+                         
+    def test_basic(self):
+        """test that Query uses the full set of mapper._eager_loaders when generating SQL"""
+        
+        class Person(fixtures.Base):
+            pass
+            
+        class Employee(Person):
+           def __init__(self, name='bob'):
+               self.name = name
+
+        class Tag(fixtures.Base):
+           def __init__(self, label):
+               self.label = label
+
+        mapper(Person, people, polymorphic_on=people.c._type,polymorphic_identity='person', properties={
+            'tags': relation(Tag, secondary=peopleTags,backref='people', lazy=False)
+        })
+        mapper(Employee, employees, inherits=Person,polymorphic_identity='employee')
+        mapper(Tag, tags)
+
+        session = create_session()
+
+        bob = Employee()
+        session.save(bob)
+        
+        tag = Tag('crazy')
+        bob.tags.append(tag)
+
+        tag = Tag('funny')
+        bob.tags.append(tag)
+        session.flush()
+
+        session.clear()
+        # query from Employee with limit, query needs to apply eager limiting subquery
+        instance = session.query(Employee).filter_by(id=1).limit(1).first()
+        assert len(instance.tags) == 2
+       
         
 if __name__ == "__main__":    
     testbase.main()