From: Mike Bayer Date: Wed, 11 Nov 2015 17:57:32 +0000 (-0500) Subject: - Fixed bug where the "single table inheritance" criteria would be X-Git-Tag: rel_1_1_0b1~84^2~70^2~59 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=33c378f768c699f3590f168f6c3c86448239268c;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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. fixes #3582 --- diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst index 688818a2a3..9ce3975c2f 100644 --- a/doc/build/changelog/changelog_11.rst +++ b/doc/build/changelog/changelog_11.rst @@ -21,6 +21,18 @@ .. 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 diff --git a/doc/build/changelog/migration_11.rst b/doc/build/changelog/migration_11.rst index 5b7c8321a0..f43cfa87c0 100644 --- a/doc/build/changelog/migration_11.rst +++ b/doc/build/changelog/migration_11.rst @@ -16,7 +16,7 @@ What's New in SQLAlchemy 1.1? 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 ============ @@ -253,6 +253,44 @@ configuration of the existing object-level technique of assigning :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 ==================================== diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 3b51b80ba0..84fb04d807 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -3676,7 +3676,7 @@ class _ColumnEntity(_QueryEntity): 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 ] diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index cbd74faacc..5def704442 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -203,6 +203,21 @@ def surface_selectables(clause): 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""" diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index 9f5d21a430..0d102c0656 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -9,6 +9,8 @@ from sqlalchemy.testing.schema import Table, Column class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest): + __dialect__ = 'default' + @classmethod def define_tables(cls, metadata): Table('employees', metadata, @@ -208,6 +210,19 @@ class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest): 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,