From: Mike Bayer Date: Wed, 24 Mar 2010 16:46:58 +0000 (-0400) Subject: this version works with *all* the polymorphic scenarios by putting the subquery into X-Git-Tag: rel_0_6beta3~12^2~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a794aa5481b3d6e6ab3bae6d12c39d8cb228da8;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git this version works with *all* the polymorphic scenarios by putting the subquery into an aliased(), so that it can be controlled. self ref breaks now. will move the joining out to use orm.join(). --- diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index d42a8f8632..c56804847a 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1069,8 +1069,7 @@ class Query(object): left = self._joinpoint_zero() if left is right and \ - not create_aliases and \ - not self._entity_zero()._subq_aliasing: # <-- TODO: hack + not create_aliases: raise sa_exc.InvalidRequestError( "Can't construct a join from %s to %s, they are the same entity" % (left, right)) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 0e5e2efdfc..92b560cd9c 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -699,18 +699,27 @@ class SubqueryLoader(AbstractRelationshipLoader): # for the significant columns, not order # by anything. q = orig_query._clone() - q._attributes = {} - q._attributes[("orig_query", SubqueryLoader)] = orig_query +# q._attributes = {} +# q._attributes[("orig_query", SubqueryLoader)] = orig_query q._set_entities(q._adapt_col_list(leftmost_attr)) if q._limit is None and q._offset is None: q._order_by = None - - q = q.from_self(self.mapper) + + embed_q = q.with_labels().subquery() + + q = q.session.query(self.mapper) + q._attributes = {} + q._attributes[("orig_query", SubqueryLoader)] = orig_query + + left_alias = mapperutil.AliasedClass(leftmost_mapper, embed_q) + q = q.select_from(left_alias) + +# q = q.from_self(self.mapper) # TODO: this is currently a magic hardcody # flag on _MapperEntity. we should find # a way to turn it into public functionality. - q._entities[0]._subq_aliasing = True +# q._entities[0]._subq_aliasing = True q._attributes[('subquery_path', None)] = subq_path @@ -720,8 +729,12 @@ class SubqueryLoader(AbstractRelationshipLoader): ] if len(to_join) < 2: +# local_attr = [ +# self.parent._get_col_to_prop(c).class_attribute +# for c in local_cols +# ] local_attr = [ - self.parent._get_col_to_prop(c).class_attribute + getattr(left_alias, self.parent._get_col_to_prop(c).key) for c in local_cols ] else: @@ -747,6 +760,7 @@ class SubqueryLoader(AbstractRelationshipLoader): else: q = q.join(prop.class_attribute, aliased=alias_join) + # propagate loader options etc. to the new query q = q._with_current_path(subq_path) q = q._conditional_options(*orig_query._with_options) diff --git a/test/orm/inheritance/test_query.py b/test/orm/inheritance/test_query.py index 5e6375eab7..7be6de6e38 100644 --- a/test/orm/inheritance/test_query.py +++ b/test/orm/inheritance/test_query.py @@ -185,6 +185,13 @@ def _produce_test(select_type): eq_(sess.query(Person).all(), all_employees) self.assert_sql_count(testing.db, go, {'':14, 'Polymorphic':9}.get(select_type, 10)) + def test_foo(self): + sess = create_session() + + def go(): + eq_(sess.query(Person).options(subqueryload(Engineer.machines)).all(), all_employees) + self.assert_sql_count(testing.db, go, {'':14, 'Unions':8, 'Polymorphic':7}.get(select_type, 8)) + def test_primary_eager_aliasing(self): sess = create_session() @@ -195,14 +202,11 @@ def _produce_test(select_type): eq_(sess.query(Person).options(eagerload(Engineer.machines))[1:3], all_employees[1:3]) self.assert_sql_count(testing.db, go, {'':6, 'Polymorphic':3}.get(select_type, 4)) - # additionally, subqueryload() can't handle from_self() on the union. - # I'm not too concerned about that. sess = create_session() - @testing.fails_if(lambda:select_type == 'Unions') def go(): eq_(sess.query(Person).options(subqueryload(Engineer.machines)).all(), all_employees) - self.assert_sql_count(testing.db, go, {'':14, 'Unions':3, 'Polymorphic':7}.get(select_type, 8)) + self.assert_sql_count(testing.db, go, {'':14, 'Unions':8, 'Polymorphic':7}.get(select_type, 8)) sess = create_session() @@ -515,7 +519,6 @@ def _produce_test(select_type): self.assert_sql_count(testing.db, go, {'':7, 'Polymorphic':1}.get(select_type, 2)) sess = create_session() - @testing.fails_if(lambda: select_type=='Unions') def go(): eq_( sess.query(Company).options( @@ -523,7 +526,7 @@ def _produce_test(select_type): )).all(), assert_result) - self.assert_sql_count(testing.db, go, {'':9, 'Joins':6,'Unions':3,'Polymorphic':5,'AliasedJoins':6}[select_type]) + self.assert_sql_count(testing.db, go, {'':9, 'Joins':6,'Unions':6,'Polymorphic':5,'AliasedJoins':6}[select_type]) def test_eagerload_on_subclass(self): sess = create_session()