From: Mike Bayer Date: Mon, 16 Jan 2017 18:06:44 +0000 (-0500) Subject: Consult _select_from_entity in _adjust_for_single_inheritance X-Git-Tag: rel_1_2_0b1~150^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b6a6ffa4c0e0911bb6c5e66b67da9789989c7699;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Consult _select_from_entity in _adjust_for_single_inheritance Fixed bug in single-table inheritance where the select_from() argument would not be taken into account when limiting rows to a subclass. Previously, only expressions in the columns requested would be taken into account. Change-Id: Id353c45eade52b264d8f6685a58ba53975669eea Fixes: #3891 --- diff --git a/doc/build/changelog/changelog_12.rst b/doc/build/changelog/changelog_12.rst index 7e6c406da9..a158ec1a3b 100644 --- a/doc/build/changelog/changelog_12.rst +++ b/doc/build/changelog/changelog_12.rst @@ -22,6 +22,19 @@ of SQL expressions) could not be reliably serialized. Also bumped the default pickle level for the serializer to "HIGHEST_PROTOCOL". + .. change:: 3891 + :tags: bug, orm + :tickets: 3891 + + Fixed bug in single-table inheritance where the select_from() + argument would not be taken into account when limiting rows + to a subclass. Previously, only expressions in the + columns requested would be taken into account. + + .. seealso:: + + :ref:`change_3891` + .. change:: 3932 :tags: bug, oracle :tickets: 3932 diff --git a/doc/build/changelog/migration_12.rst b/doc/build/changelog/migration_12.rst index 4ef08bca6c..d64200d120 100644 --- a/doc/build/changelog/migration_12.rst +++ b/doc/build/changelog/migration_12.rst @@ -34,6 +34,7 @@ SQLAlchemy is currnetly tested on versions 3.5 and 3.6. New Features and Improvements - ORM =================================== + New Features and Improvements - Core ==================================== @@ -103,6 +104,41 @@ able to load within the scope of the event. :ticket:`3934` +.. _change_3891: + +Fixed issue involving single-table inheritance with ``select_from()`` +--------------------------------------------------------------------- + +The :meth:`.Query.select_from` method now honors the single-table inheritance +column discriminator when generating SQL; previously, only the expressions +in the query column list would be taken into account. + +Supposing ``Manager`` is a subclass of ``Employee``. A query like the following:: + + sess.query(Manager.id) + +Would generate SQL as:: + + SELECT employee.id FROM employee WHERE employee.type IN ('manager') + +However, if ``Manager`` were only specified by :meth:`.Query.select_from` +and not in the columns list, the discriminator would not be added:: + + sess.query(func.count(1)).select_from(Manager) + +would generate:: + + SELECT count(1) FROM employee + +With the fix, :meth:`.Query.select_from` now works correctly and we get:: + + SELECT count(1) FROM employee WHERE employee.type IN ('manager') + +Applications that may have been working around this by supplying the +WHERE clause manually may need to be adjusted. + +:ticket:`3891` + Key Behavioral Changes - Core ============================= diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 2fae36272b..f6459de2c0 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -3475,12 +3475,22 @@ class Query(object): """Apply single-table-inheritance filtering. For all distinct single-table-inheritance mappers represented in - the columns clause of this query, add criterion to the WHERE + the columns clause of this query, as well as the "select from entity", + add criterion to the WHERE clause of the given QueryContext such that only the appropriate subtypes are selected from the total results. """ - for (ext_info, adapter) in set(self._mapper_adapter_map.values()): + + search = set(self._mapper_adapter_map.values()) + if self._select_from_entity: + # based on the behavior in _set_select_from, + # when we have self._select_from_entity, we don't + # have _from_obj_alias. + # assert self._from_obj_alias is None + search = search.union([(self._select_from_entity, None)]) + + for (ext_info, adapter) in search: if ext_info in self._join_entities: continue single_crit = ext_info.mapper._single_table_criterion diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index 26cf9fa01a..0c7fc6f8d3 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -310,11 +310,26 @@ class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest): use_default_dialect=True ) - def test_select_from(self): - Manager = self.classes.Manager - JuniorEngineer = self.classes.JuniorEngineer - employees = self.tables.employees - Engineer = self.classes.Engineer + def test_select_from_count(self): + Manager, Engineer = (self.classes.Manager, self.classes.Engineer) + + sess = create_session() + m1 = Manager(name='Tom', manager_data='data1') + e1 = Engineer(name='Kurt', engineer_info='knows how to hack') + sess.add_all([m1, e1]) + sess.flush() + + eq_( + sess.query(func.count(1)).select_from(Manager).all(), + [(1, )] + ) + + def test_select_from_subquery(self): + Manager, JuniorEngineer, employees, Engineer = ( + self.classes.Manager, + self.classes.JuniorEngineer, + self.tables.employees, + self.classes.Engineer) sess = create_session() m1 = Manager(name='Tom', manager_data='data1')