From: Mike Bayer Date: Sun, 4 Dec 2011 01:57:27 +0000 (-0500) Subject: - [bug] Calls to query.join() to a single-table X-Git-Tag: rel_0_7_4~35 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=252acd50d8ae0fe58d61cf7f053dfe1fe4206de4;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - [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] --- diff --git a/CHANGES b/CHANGES index 87036000a7..15a2e2a016 100644 --- 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] diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 9c7ef7b8eb..60176dfe3b 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -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: diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index 694ba01690..d05551ef4a 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -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,