From 02ae3cd54d0c47850ae1c894abae256a4717fe2d Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 2 Jun 2013 19:33:19 -0400 Subject: [PATCH] getting things to join without subqueries, but some glitches in the compiler step when we do query.count() are showing --- lib/sqlalchemy/orm/query.py | 16 ++++++------- lib/sqlalchemy/sql/compiler.py | 11 ++++++--- lib/sqlalchemy/sql/expression.py | 8 +++---- lib/sqlalchemy/sql/util.py | 1 + test/orm/inheritance/test_polymorphic_rel.py | 25 +++++++++++++++++++- test/orm/test_of_type.py | 12 ++++++---- 6 files changed, 52 insertions(+), 21 deletions(-) diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index beae7aba0b..25ffb0127e 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1862,19 +1862,20 @@ class Query(object): (right_selectable.description, right_mapper.mapped_table.description)) - if not isinstance(right_selectable, expression.Alias): - right_selectable = right_selectable.alias() + if not isinstance(right_selectable, expression.Join): + if not isinstance(right_selectable, expression.Alias): + right_selectable = right_selectable.alias() + need_adapter = True right = aliased(right_mapper, right_selectable) - need_adapter = True aliased_entity = right_mapper and \ not right_is_aliased and \ ( - right_mapper.with_polymorphic or - isinstance( - right_mapper.mapped_table, - expression.Join) + right_mapper.with_polymorphic + #isinstance( + # right_mapper.mapped_table, + # expression.Join) ) if not need_adapter and (create_aliases or aliased_entity): @@ -1946,7 +1947,6 @@ class Query(object): clause = left_selectable assert clause is not None - try: clause = orm_join(clause, right, onclause, isouter=outerjoin) except sa_exc.ArgumentError as ae: diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 030d6dce93..27e883c861 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -1095,14 +1095,19 @@ class SQLCompiler(engine.Compiled): c._label = c._key_label = c.name elem.right = selectable - adapters.append( - sql_util.ClauseAdapter(selectable, + import pdb + pdb.set_trace() + adapter = sql_util.ClauseAdapter(selectable, traverse_options=traverse_options) - ) + adapter.__traverse_options__.pop('stop_on') + adapters.append(adapter) select = visitors.cloned_traverse(select, traverse_options, {"join": visit_join}) + if adapters: + import pdb + pdb.set_trace() for adap in reversed(adapters): select = adap.traverse(select) return select diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 5820cb1061..edab9e2901 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -4212,10 +4212,10 @@ class FromGrouping(FromClause): @property def foreign_keys(self): - # this could be - # self.element.foreign_keys - # see SelectableTest.test_join_condition - return set() + return self.element.foreign_keys + + def is_derived_from(self, element): + return self.element.is_derived_from(element) @property def _hide_froms(self): diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index ffa07d3df5..6a267752e9 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -833,6 +833,7 @@ class ClauseAdapter(visitors.ReplacingCloningVisitor): return newcol def replace(self, col): + print "COL!", col if isinstance(col, expression.FromClause) and \ self.selectable.is_derived_from(col): return self.selectable diff --git a/test/orm/inheritance/test_polymorphic_rel.py b/test/orm/inheritance/test_polymorphic_rel.py index 8c1f22114f..eecfb036b8 100644 --- a/test/orm/inheritance/test_polymorphic_rel.py +++ b/test/orm/inheritance/test_polymorphic_rel.py @@ -37,6 +37,7 @@ class _PolymorphicTestBase(object): e1, e2, e3, b1, m1 = \ cls.e1, cls.e2, cls.e3, cls.b1, cls.m1 + def test_loads_at_once(self): """ Test that all objects load from the full query, when @@ -672,7 +673,17 @@ class _PolymorphicTestBase(object): expected) self.assert_sql_count(testing.db, go, 1) + def test_subqueryload_on_subclass(self): sess = create_session() + expected = [ + Engineer( + name="dilbert", + engineer_name="dilbert", + primary_language="java", + status="regular engineer", + machines=[ + Machine(name="IBM ThinkPad"), + Machine(name="IPhone")])] def go(): # test load People with subqueryload to engineers + machines eq_(sess.query(Person) @@ -726,6 +737,14 @@ class _PolymorphicTestBase(object): .join(Engineer.machines).all(), [c1, c2]) + def test_join_to_subclass_six_point_five(self): + sess = create_session() + eq_(sess.query(Company) + .join(people.join(engineers), 'employees') + .join(Engineer.machines) + .filter(Engineer.name == 'dilbert').all(), + [c1]) + def test_join_to_subclass_seven(self): sess = create_session() eq_(sess.query(Company) @@ -898,7 +917,8 @@ class _PolymorphicTestBase(object): .filter(Paperwork.description.like('%#%')).all(), [c1, c2]) - def test_explicit_polymorphic_join(self): + + def test_explicit_polymorphic_join_one(self): sess = create_session() # join from Company to Engineer; join condition formulated by @@ -910,6 +930,9 @@ class _PolymorphicTestBase(object): .filter(Engineer.engineer_name == 'vlad').one(), c2) + def test_explicit_polymorphic_join_two(self): + sess = create_session() + # same, using explicit join condition. Query.join() must # adapt the on clause here to match the subquery wrapped around # "people join engineers". diff --git a/test/orm/test_of_type.py b/test/orm/test_of_type.py index 17ffebc3d8..1506735605 100644 --- a/test/orm/test_of_type.py +++ b/test/orm/test_of_type.py @@ -69,12 +69,16 @@ class _PolymorphicTestBase(object): .filter(Engineer.primary_language == 'java').count(), 1) + def test_join_to_subclass_four(self): + sess = Session() # test [ticket:2093] eq_(sess.query(Company.company_id, Engineer) .join(Company.employees.of_type(Engineer)) .filter(Engineer.primary_language == 'java').count(), 1) + def test_join_to_subclass_five(self): + sess = Session() eq_(sess.query(Company) .join(Company.employees.of_type(Engineer)) .filter(Engineer.primary_language == 'java').count(), @@ -185,6 +189,7 @@ class _PolymorphicTestBase(object): ) self.assert_sql_count(testing.db, go, 3) + class PolymorphicPolymorphicTest(_PolymorphicTestBase, _PolymorphicPolymorphic): def _polymorphic_join_target(self, cls): from sqlalchemy.orm import class_mapper @@ -524,11 +529,8 @@ class SubclassRelationshipTest(testing.AssertsCompiledSQL, fixtures.DeclarativeM self.assert_compile(q, "SELECT data_container.id AS data_container_id, " "data_container.name AS data_container_name " - "FROM data_container JOIN (SELECT job.id AS job_id, " - "job.type AS job_type, job.container_id AS job_container_id, " - "subjob.id AS subjob_id, subjob.attr AS subjob_attr " - "FROM job JOIN subjob ON job.id = subjob.id) AS anon_1 " - "ON data_container.id = anon_1.job_container_id" + "FROM data_container JOIN (job JOIN subjob ON job.id = subjob.id) " + "ON data_container.id = job.container_id" ) def test_join_wpoly_innerjoin(self): -- 2.47.3