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
New Features and Improvements - ORM
===================================
+
New Features and Improvements - Core
====================================
: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
=============================
"""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
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')