summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-08-29 12:05:00 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-08-29 12:05:00 -0400
commitfbcb056d90f4a54eed7cf270818c9a1fc173f99f (patch)
treee095fb7b11a76ea152595b561f79222896b76b1d
parent08d2c863df04f954f305b460bba45b15a410fc74 (diff)
downloadsqlalchemy-fbcb056d90f4a54eed7cf270818c9a1fc173f99f.tar.gz
- Changed the approach by which the "single inheritance criterion"
is applied, when using :meth:`.Query.from_self`, or its common user :meth:`.Query.count`. The criteria to limit rows to those with a certain type is now indicated on the inside subquery, not the outside one, so that even if the "type" column is not available in the columns clause, we can filter on it on the "inner" query. fixes #3177
-rw-r--r--doc/build/changelog/changelog_10.rst16
-rw-r--r--doc/build/changelog/migration_10.rst57
-rw-r--r--lib/sqlalchemy/orm/query.py2
-rw-r--r--test/orm/inheritance/test_single.py19
4 files changed, 90 insertions, 4 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index 87f32aff6..13af7f953 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -23,6 +23,22 @@
.. change::
+ :tags: bug, orm
+ :tickets: 3177
+
+ Changed the approach by which the "single inheritance criterion"
+ is applied, when using :meth:`.Query.from_self`, or its common
+ user :meth:`.Query.count`. The criteria to limit rows to those
+ with a certain type is now indicated on the inside subquery,
+ not the outside one, so that even if the "type" column is not
+ available in the columns clause, we can filter on it on the "inner"
+ query.
+
+ .. seealso::
+
+ :ref:`migration_3177`
+
+ .. change::
:tags: change, orm
The ``proc()`` callable passed to the ``create_row_processor()``
diff --git a/doc/build/changelog/migration_10.rst b/doc/build/changelog/migration_10.rst
index 3dc2871b0..16a790234 100644
--- a/doc/build/changelog/migration_10.rst
+++ b/doc/build/changelog/migration_10.rst
@@ -417,6 +417,63 @@ as remaining ORM constructs such as :func:`.orm.synonym`.
:ticket:`2963`
+.. _migration_3177:
+
+Change to single-table-inheritance criteria when using from_self(), count()
+---------------------------------------------------------------------------
+
+Given a single-table inheritance mapping, such as::
+
+ class Widget(Base):
+ __table__ = 'widget_table'
+
+ class FooWidget(Widget):
+ pass
+
+Using :meth:`.Query.from_self` or :meth:`.Query.count` against a subclass
+would produce a subquery, but then add the "WHERE" criteria for subtypes
+to the outside::
+
+ sess.query(FooWidget).from_self().all()
+
+rendering::
+
+ SELECT
+ anon_1.widgets_id AS anon_1_widgets_id,
+ anon_1.widgets_type AS anon_1_widgets_type
+ FROM (SELECT widgets.id AS widgets_id, widgets.type AS widgets_type,
+ FROM widgets) AS anon_1
+ WHERE anon_1.widgets_type IN (?)
+
+The issue with this is that if the inner query does not specify all
+columns, then we can't add the WHERE clause on the outside (it actually tries,
+and produces a bad query). This decision
+apparently goes way back to 0.6.5 with the note "may need to make more
+adjustments to this". Well, those adjustments have arrived! So now the
+above query will render::
+
+ SELECT
+ anon_1.widgets_id AS anon_1_widgets_id,
+ anon_1.widgets_type AS anon_1_widgets_type
+ FROM (SELECT widgets.id AS widgets_id, widgets.type AS widgets_type,
+ FROM widgets
+ WHERE widgets.type IN (?)) AS anon_1
+
+So that queries that don't include "type" will still work!::
+
+ sess.query(FooWidget.id).count()
+
+Renders::
+
+ SELECT count(*) AS count_1
+ FROM (SELECT widgets.id AS widgets_id
+ FROM widgets
+ WHERE widgets.type IN (?)) AS anon_1
+
+
+:ticket:`3177`
+
+
Dialect Changes
===============
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index b05646719..61fbd1be8 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -945,9 +945,9 @@ class Query(object):
"""
fromclause = self.with_labels().enable_eagerloads(False).\
- _set_enable_single_crit(False).\
statement.correlate(None)
q = self._from_selectable(fromclause)
+ q._enable_single_crit = False
if entities:
q._set_entities(entities)
return q
diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py
index 434642ca1..23b1c4fd2 100644
--- a/test/orm/inheritance/test_single.py
+++ b/test/orm/inheritance/test_single.py
@@ -149,11 +149,24 @@ class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest):
'employees_manager_data, '
'employees.engineer_info AS '
'employees_engineer_info, employees.type '
- 'AS employees_type FROM employees) AS '
- 'anon_1 WHERE anon_1.employees_type IN '
- '(:type_1, :type_2)',
+ 'AS employees_type FROM employees WHERE '
+ 'employees.type IN (:type_1, :type_2)) AS '
+ 'anon_1',
use_default_dialect=True)
+ def test_from_self_count(self):
+ Engineer = self.classes.Engineer
+
+ sess = create_session()
+ col = func.count(literal_column('*'))
+ self.assert_compile(
+ sess.query(Engineer.employee_id).from_self(col),
+ "SELECT count(*) AS count_1 "
+ "FROM (SELECT employees.employee_id AS employees_employee_id "
+ "FROM employees "
+ "WHERE employees.type IN (?, ?)) AS anon_1"
+ )
+
def test_select_from(self):
Manager, JuniorEngineer, employees, Engineer = (self.classes.Manager,
self.classes.JuniorEngineer,