]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [bug] Calls to query.join() to a single-table
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 4 Dec 2011 01:57:27 +0000 (20:57 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 4 Dec 2011 01:57:27 +0000 (20:57 -0500)
inheritance subclass are now tracked, and
are used to eliminate the additional WHERE..
IN criterion normally tacked on with single
table inheritance, since the join should
accommodate it.  This allows OUTER JOIN
to a single table subclass to produce
the correct results, and overall will produce
fewer WHERE criterion when dealing with
single table inheritance joins.
[ticket:2328]

CHANGES
lib/sqlalchemy/orm/query.py
test/orm/inheritance/test_single.py

diff --git a/CHANGES b/CHANGES
index 87036000a74c62e431b72ebe1c6bd6dc6f0990c2..15a2e2a0167b5fbf9a94b2d2264ac6a61364b91d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -84,6 +84,18 @@ CHANGES
      a tuple is inadvertently passed to session.query()
      [ticket:2297].  Also in 0.6.9.
 
+   - [bug] Calls to query.join() to a single-table
+     inheritance subclass are now tracked, and
+     are used to eliminate the additional WHERE..
+     IN criterion normally tacked on with single
+     table inheritance, since the join should
+     accommodate it.  This allows OUTER JOIN
+     to a single table subclass to produce
+     the correct results, and overall will produce
+     fewer WHERE criterion when dealing with 
+     single table inheritance joins.  
+     [ticket:2328]
+
 - sql
    - [bug] related to [ticket:2316], made some 
      adjustments to the change from [ticket:2261]
index 9c7ef7b8eb3f593b68a3c7e16db2706c2cd68342..60176dfe3b5e45e6f916395012fc7c0d5c7baa45 100644 (file)
@@ -89,6 +89,7 @@ class Query(object):
     _only_load_props = None
     _refresh_state = None
     _from_obj = ()
+    _join_entities = ()
     _select_from_entity = None
     _filter_aliases = None
     _from_obj_alias = None
@@ -1641,6 +1642,9 @@ class Query(object):
         left_mapper, left_selectable, left_is_aliased = _entity_info(left)
         right_mapper, right_selectable, right_is_aliased = _entity_info(right)
 
+        if right_mapper:
+            self._join_entities += (right, )
+
         if right_mapper and prop and \
                 not right_mapper.common_parent(prop.mapper):
             raise sa_exc.InvalidRequestError(
@@ -2818,9 +2822,10 @@ class Query(object):
         selected from the total results.
 
         """
-
         for entity, (mapper, adapter, s, i, w) in \
                             self._mapper_adapter_map.iteritems():
+            if entity in self._join_entities:
+                continue
             single_crit = mapper._single_table_criterion
             if single_crit is not None:
                 if adapter:
index 694ba01690e598bfd8e036d54bd8e626615ec390..d05551ef4a93c977fd65edfe1b4dc7217a9fd1a2 100644 (file)
@@ -295,7 +295,9 @@ class RelationshipFromSingleTest(testing.AssertsCompiledSQL, fixtures.MappedTest
                             use_default_dialect=True
                             )
 
-class RelationshipToSingleTest(fixtures.MappedTest):
+class RelationshipToSingleTest(testing.AssertsCompiledSQL, fixtures.MappedTest):
+    __dialect__ = 'default'
+
     @classmethod
     def define_tables(cls, metadata):
         Table('employees', metadata,
@@ -327,7 +329,8 @@ class RelationshipToSingleTest(fixtures.MappedTest):
             pass
 
     def test_of_type(self):
-        JuniorEngineer, Company, companies, Manager, Employee, employees, Engineer = (self.classes.JuniorEngineer,
+        JuniorEngineer, Company, companies, Manager,\
+                Employee, employees, Engineer = (self.classes.JuniorEngineer,
                                 self.classes.Company,
                                 self.tables.companies,
                                 self.classes.Manager,
@@ -368,6 +371,50 @@ class RelationshipToSingleTest(fixtures.MappedTest):
             ]
         )
 
+    def test_outer_join(self):
+        Company, Employee, Engineer = self.classes.Company,\
+                                self.classes.Employee,\
+                                self.classes.Engineer
+        companies, employees = self.tables.companies, self.tables.employees
+
+        mapper(Company, companies, properties={
+            'engineers':relationship(Engineer)
+        })
+        mapper(Employee, employees, polymorphic_on=employees.c.type)
+        mapper(Engineer, inherits=Employee, polymorphic_identity='engineer')
+
+        sess = create_session()
+        self.assert_compile(
+            sess.query(Company, Engineer.name).outerjoin("engineers"),
+            "SELECT companies.company_id AS companies_company_id, "
+            "companies.name AS companies_name, employees.name AS employees_name "
+            "FROM companies LEFT OUTER JOIN employees ON companies.company_id "
+            "= employees.company_id AND employees.type IN (:type_1)" 
+        )
+
+    def test_outer_join_alias(self):
+        Company, Employee, Engineer = self.classes.Company,\
+                                self.classes.Employee,\
+                                self.classes.Engineer
+        companies, employees = self.tables.companies, self.tables.employees
+
+        mapper(Company, companies, properties={
+            'engineers':relationship(Engineer)
+        })
+        mapper(Employee, employees, polymorphic_on=employees.c.type)
+        mapper(Engineer, inherits=Employee, polymorphic_identity='engineer')
+
+        eng_alias = aliased(Engineer)
+        sess = create_session()
+        self.assert_compile(
+            sess.query(Company, eng_alias.name).outerjoin(eng_alias, Company.engineers),
+            "SELECT companies.company_id AS companies_company_id, "
+            "companies.name AS companies_name, employees_1.name AS "
+            "employees_1_name FROM companies LEFT OUTER "
+            "JOIN employees AS employees_1 ON companies.company_id "
+            "= employees_1.company_id AND employees_1.type IN (:type_1)"
+        )
+
 
     def test_relationship_to_subclass(self):
         JuniorEngineer, Company, companies, Manager, Employee, employees, Engineer = (self.classes.JuniorEngineer,