]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- repair the _enable_single_crit method, it was named the same
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 1 Jul 2014 00:25:04 +0000 (20:25 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 1 Jul 2014 00:27:00 +0000 (20:27 -0400)
as the attribute and probably just replaced itself, so that is
now _set_enable_single_crit
- as a side effect of the main issue fixed here, correct the case in
adjust_for_single_inheritance where the same mapper appears more
than once in mapper_adapter_map; run through a set() for uniqueness.
- Fixed bug in subquery eager loading in conjunction with
:func:`.with_polymorphic`, the targeting of entities and columns
in the subquery load has been made more accurate with respect
to this type of entity and others. Fixes #3106

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/strategies.py
test/orm/test_subquery_relations.py

index 6ac46bcd48e054846819c5f2c545020e0c745881..fbf0aa1f73156d56515795fdb654bc7da11f769d 100644 (file)
     :version: 0.9.7
     :released:
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 3106
+        :versions: 1.0.0
+
+        Fixed bug in subquery eager loading in conjunction with
+        :func:`.with_polymorphic`, the targeting of entities and columns
+        in the subquery load has been made more accurate with respect
+        to this type of entity and others.
+
     .. change::
         :tags: bug, orm
         :tickets: 3099
index 5d60c4e29dfdcf494ec07d65b77b8dc87c9c1fe3..728f7787a2b11062f5bbfb0f54eec41ae2a17a62 100644 (file)
@@ -941,7 +941,7 @@ class Query(object):
 
         """
         fromclause = self.with_labels().enable_eagerloads(False).\
-                                    _enable_single_crit(False).\
+                                    _set_enable_single_crit(False).\
                                     statement.correlate(None)
         q = self._from_selectable(fromclause)
         if entities:
@@ -949,7 +949,7 @@ class Query(object):
         return q
 
     @_generative()
-    def _enable_single_crit(self, val):
+    def _set_enable_single_crit(self, val):
         self._enable_single_crit = val
 
     @_generative()
@@ -2908,7 +2908,8 @@ class Query(object):
         subtypes are selected from the total results.
 
         """
-        for (ext_info, adapter) in self._mapper_adapter_map.values():
+
+        for (ext_info, adapter) in set(self._mapper_adapter_map.values()):
             if ext_info in self._join_entities:
                 continue
             single_crit = ext_info.mapper._single_table_criterion
index 0a120cf1e743f1842e3133491b95b005b23646f9..1d06dd7f7c13febef05a1bbac922c170d61967c6 100644 (file)
@@ -713,7 +713,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
             elif subq_path.contains_mapper(self.mapper):
                 return
 
-        subq_mapper, leftmost_mapper, leftmost_attr, leftmost_relationship = \
+        leftmost_mapper, leftmost_attr, leftmost_relationship = \
                 self._get_leftmost(subq_path)
 
         orig_query = context.attributes.get(
@@ -725,7 +725,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
         left_alias = self._generate_from_original_query(
                             orig_query, leftmost_mapper,
                             leftmost_attr, leftmost_relationship,
-                            entity.mapper
+                            entity.entity_zero
         )
 
         # generate another Query that will join the
@@ -738,13 +738,12 @@ class SubqueryLoader(AbstractRelationshipLoader):
             ("orig_query", SubqueryLoader): orig_query,
             ('subquery_path', None): subq_path
         }
-        q = q._enable_single_crit(False)
 
+        q = q._set_enable_single_crit(False)
         to_join, local_attr, parent_alias = \
                     self._prep_for_joins(left_alias, subq_path)
         q = q.order_by(*local_attr)
         q = q.add_columns(*local_attr)
-
         q = self._apply_joins(q, to_join, left_alias,
                             parent_alias, effective_entity)
 
@@ -771,15 +770,17 @@ class SubqueryLoader(AbstractRelationshipLoader):
         leftmost_cols = leftmost_prop.local_columns
 
         leftmost_attr = [
-            leftmost_mapper._columntoproperty[c].class_attribute
+            getattr(subq_path[0].entity,
+                leftmost_mapper._columntoproperty[c].key)
             for c in leftmost_cols
         ]
-        return subq_mapper, leftmost_mapper, leftmost_attr, leftmost_prop
+
+        return leftmost_mapper, leftmost_attr, leftmost_prop
 
     def _generate_from_original_query(self,
             orig_query, leftmost_mapper,
             leftmost_attr, leftmost_relationship,
-            entity_mapper
+            orig_entity
     ):
         # reformat the original query
         # to look only for significant columns
@@ -787,9 +788,8 @@ class SubqueryLoader(AbstractRelationshipLoader):
 
         # set a real "from" if not present, as this is more
         # accurate than just going off of the column expression
-        if not q._from_obj and entity_mapper.isa(leftmost_mapper):
-            q._set_select_from([entity_mapper], False)
-
+        if not q._from_obj and orig_entity.mapper.isa(leftmost_mapper):
+            q._set_select_from([orig_entity], False)
         target_cols = q._adapt_col_list(leftmost_attr)
 
         # select from the identity columns of the outer
index 03498d7ea3711f5a56ffef5bf3f9f2c83c9b3e15..0f8ffb6e728a2e085d26a0ceda622e5ebc4e4a38 100644 (file)
@@ -14,6 +14,8 @@ from sqlalchemy.testing.entities import ComparableEntity
 from test.orm import _fixtures
 import sqlalchemy as sa
 
+from sqlalchemy.orm import with_polymorphic
+
 class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
     run_inserts = 'once'
     run_deletes = None
@@ -1133,6 +1135,158 @@ class BaseRelationFromJoinedSubclassTest(_Polymorphic):
             )
         )
 
+    def test_correct_subquery_with_polymorphic_no_alias(self):
+        # test #3106
+        sess = create_session()
+
+        wp = with_polymorphic(Person, [Engineer])
+        q = sess.query(wp).\
+                options(subqueryload(wp.paperwork)).\
+                order_by(Engineer.primary_language.desc())
+
+        def go():
+            eq_(q.first(),
+                Engineer(
+                    paperwork=[
+                        Paperwork(description="tps report #1"),
+                        Paperwork(description="tps report #2")],
+                    primary_language='java'
+                )
+
+            )
+        self.assert_sql_execution(
+            testing.db,
+            go,
+            CompiledSQL(
+                "SELECT people.person_id AS people_person_id, "
+                "people.name AS people_name, people.type AS people_type, "
+                "engineers.engineer_id AS engineers_engineer_id, "
+                "engineers.primary_language AS engineers_primary_language "
+                "FROM people LEFT OUTER JOIN engineers ON people.person_id = "
+                "engineers.engineer_id ORDER BY engineers.primary_language "
+                "DESC LIMIT :param_1"),
+            CompiledSQL(
+                "SELECT paperwork.paperwork_id AS paperwork_paperwork_id, "
+                "paperwork.description AS paperwork_description, "
+                "paperwork.person_id AS paperwork_person_id, "
+                "anon_1.people_person_id AS anon_1_people_person_id FROM "
+                "(SELECT people.person_id AS people_person_id FROM people "
+                "LEFT OUTER JOIN engineers ON people.person_id = "
+                "engineers.engineer_id ORDER BY engineers.primary_language "
+                "DESC LIMIT :param_1) AS anon_1 JOIN paperwork "
+                "ON anon_1.people_person_id = paperwork.person_id "
+                "ORDER BY anon_1.people_person_id, paperwork.paperwork_id")
+        )
+
+    def test_correct_subquery_with_polymorphic_alias(self):
+        # test #3106
+        sess = create_session()
+
+        wp = with_polymorphic(Person, [Engineer], aliased=True)
+        q = sess.query(wp).\
+                options(subqueryload(wp.paperwork)).\
+                order_by(wp.Engineer.primary_language.desc())
+
+        def go():
+            eq_(q.first(),
+                Engineer(
+                    paperwork=[
+                        Paperwork(description="tps report #1"),
+                        Paperwork(description="tps report #2")],
+                    primary_language='java'
+                )
+
+            )
+        self.assert_sql_execution(
+            testing.db,
+            go,
+            CompiledSQL(
+                "SELECT anon_1.people_person_id AS anon_1_people_person_id, "
+                "anon_1.people_name AS anon_1_people_name, "
+                "anon_1.people_type AS anon_1_people_type, "
+                "anon_1.engineers_engineer_id AS anon_1_engineers_engineer_id, "
+                "anon_1.engineers_primary_language "
+                "AS anon_1_engineers_primary_language FROM "
+                "(SELECT people.person_id AS people_person_id, "
+                "people.name AS people_name, people.type AS people_type, "
+                "engineers.engineer_id AS engineers_engineer_id, "
+                "engineers.primary_language AS engineers_primary_language "
+                "FROM people LEFT OUTER JOIN engineers ON people.person_id = "
+                "engineers.engineer_id) AS anon_1 "
+                "ORDER BY anon_1.engineers_primary_language DESC "
+                "LIMIT :param_1"),
+            CompiledSQL(
+                "SELECT paperwork.paperwork_id AS paperwork_paperwork_id, "
+                "paperwork.description AS paperwork_description, "
+                "paperwork.person_id AS paperwork_person_id, "
+                "anon_1.anon_2_people_person_id AS "
+                "anon_1_anon_2_people_person_id FROM "
+                "(SELECT DISTINCT anon_2.people_person_id AS "
+                "anon_2_people_person_id, "
+                "anon_2.engineers_primary_language AS "
+                "anon_2_engineers_primary_language FROM "
+                "(SELECT people.person_id AS people_person_id, "
+                "people.name AS people_name, people.type AS people_type, "
+                "engineers.engineer_id AS engineers_engineer_id, "
+                "engineers.primary_language AS engineers_primary_language "
+                "FROM people LEFT OUTER JOIN engineers ON people.person_id = "
+                "engineers.engineer_id) AS anon_2 "
+                "ORDER BY anon_2.engineers_primary_language "
+                "DESC LIMIT :param_1) AS anon_1 "
+                "JOIN paperwork "
+                "ON anon_1.anon_2_people_person_id = paperwork.person_id "
+                "ORDER BY anon_1.anon_2_people_person_id, "
+                "paperwork.paperwork_id")
+        )
+
+    def test_correct_subquery_with_polymorphic_flat_alias(self):
+        # test #3106
+        sess = create_session()
+
+        wp = with_polymorphic(Person, [Engineer], aliased=True, flat=True)
+        q = sess.query(wp).\
+                options(subqueryload(wp.paperwork)).\
+                order_by(wp.Engineer.primary_language.desc())
+
+        def go():
+            eq_(q.first(),
+                Engineer(
+                    paperwork=[
+                        Paperwork(description="tps report #1"),
+                        Paperwork(description="tps report #2")],
+                    primary_language='java'
+                )
+
+            )
+        self.assert_sql_execution(
+            testing.db,
+            go,
+            CompiledSQL(
+                "SELECT people_1.person_id AS people_1_person_id, "
+                "people_1.name AS people_1_name, "
+                "people_1.type AS people_1_type, "
+                "engineers_1.engineer_id AS engineers_1_engineer_id, "
+                "engineers_1.primary_language AS engineers_1_primary_language "
+                "FROM people AS people_1 "
+                "LEFT OUTER JOIN engineers AS engineers_1 "
+                "ON people_1.person_id = engineers_1.engineer_id "
+                "ORDER BY engineers_1.primary_language DESC LIMIT :param_1"),
+            CompiledSQL(
+                "SELECT paperwork.paperwork_id AS paperwork_paperwork_id, "
+                "paperwork.description AS paperwork_description, "
+                "paperwork.person_id AS paperwork_person_id, "
+                "anon_1.people_1_person_id AS anon_1_people_1_person_id "
+                "FROM (SELECT people_1.person_id AS people_1_person_id "
+                "FROM people AS people_1 "
+                "LEFT OUTER JOIN engineers AS engineers_1 "
+                "ON people_1.person_id = engineers_1.engineer_id "
+                "ORDER BY engineers_1.primary_language DESC LIMIT :param_1) "
+                "AS anon_1 JOIN paperwork ON anon_1.people_1_person_id = "
+                "paperwork.person_id ORDER BY anon_1.people_1_person_id, "
+                "paperwork.paperwork_id"
+            )
+        )
+
 class SubRelationFromJoinedSubclassMultiLevelTest(_Polymorphic):
     @classmethod
     def define_tables(cls, metadata):