.. changelog::
:version: 1.1.0b1
+ .. change::
+ :tags: bug, orm
+ :tickets: 3582
+
+ Fixed bug where the "single table inheritance" criteria would be
+ added onto the end of a query in some inappropriate situations, such
+ as when querying from an exists() of a single-inheritance subclass.
+
+ .. seealso::
+
+ :ref:`change_3582`
+
.. change::
:tags: enhancement, schema
:pullreq: github:204
some issues may be moved to later milestones in order to allow
for a timely release.
- Document last updated: October 7, 2015
+ Document last updated: November 11, 2015
Introduction
============
:ticket:`3250`
+
+.. _change_3582:
+
+Further Fixes to single-table inheritance querying
+--------------------------------------------------
+
+Continuing from 1.0's :ref:`migration_3177`, the :class:`.Query` should
+no longer inappropriately add the "single inheritance" criteria when the
+query is against a subquery expression such as an exists::
+
+ class Widget(Base):
+ __tablename__ = 'widget'
+ id = Column(Integer, primary_key=True)
+ type = Column(String)
+ data = Column(String)
+ __mapper_args__ = {'polymorphic_on': type}
+
+
+ class FooWidget(Widget):
+ __mapper_args__ = {'polymorphic_identity': 'foo'}
+
+ q = session.query(FooWidget).filter(FooWidget.data == 'bar').exists()
+
+ session.query(q).all()
+
+Produces::
+
+ SELECT EXISTS (SELECT 1
+ FROM widget
+ WHERE widget.data = :data_1 AND widget.type IN (:type_1)) AS anon_1
+
+The IN clause on the inside is appropriate, in order to limit to FooWidget
+objects, however previously the IN clause would also be generated a second
+time on the outside of the subquery.
+
+:ticket:`3582`
+
+
New Features and Improvements - Core
====================================
self._from_entities = set(self.entities)
else:
all_elements = [
- elem for elem in visitors.iterate(column, {})
+ elem for elem in sql_util.surface_column_elements(column)
if 'parententity' in elem._annotations
]
stack.append(elem.element)
+def surface_column_elements(clause):
+ """traverse and yield only outer-exposed column elements, such as would
+ be addressable in the WHERE clause of a SELECT if this element were
+ in the columns clause."""
+
+ stack = deque([clause])
+ while stack:
+ elem = stack.popleft()
+ yield elem
+ for sub in elem.get_children():
+ if isinstance(elem, FromGrouping):
+ continue
+ stack.append(sub)
+
+
def selectables_overlap(left, right):
"""Return True if left/right have some overlapping selectable"""
class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest):
+ __dialect__ = 'default'
+
@classmethod
def define_tables(cls, metadata):
Table('employees', metadata,
eq_(sess.query(Manager).filter(Manager.name.like('%m%')).count(), 2)
eq_(sess.query(Employee).filter(Employee.name.like('%m%')).count(), 3)
+ def test_exists_standalone(self):
+ Engineer = self.classes.Engineer
+
+ sess = create_session()
+
+ self.assert_compile(
+ sess.query(
+ sess.query(Engineer).filter(Engineer.name == 'foo').exists()),
+ "SELECT EXISTS (SELECT 1 FROM employees WHERE "
+ "employees.name = :name_1 AND employees.type "
+ "IN (:type_1, :type_2)) AS anon_1"
+ )
+
def test_type_filtering(self):
Employee, Manager, reports, Engineer = (self.classes.Employee,
self.classes.Manager,