]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Don't include SelectBase when searching for surface column elements
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 5 Oct 2017 15:25:10 +0000 (11:25 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 5 Oct 2017 15:30:01 +0000 (11:30 -0400)
Fixed bug where correlated select used against single-table inheritance
entity would fail to render correctly in the outer query, due to adjustment
for single inheritance discriminator criteria inappropriately re-applying
the criteria to the outer query.

Change-Id: I38df21f1392af1843e10119682fa0635d346e2a8
Fixes: #4103
(cherry picked from commit 1281e6e6c41ad3d7240fe50f4fecab4083b79975)

doc/build/changelog/unreleased_11/4103.rst [new file with mode: 0644]
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/sql/util.py
test/orm/inheritance/test_single.py

diff --git a/doc/build/changelog/unreleased_11/4103.rst b/doc/build/changelog/unreleased_11/4103.rst
new file mode 100644 (file)
index 0000000..b667dd1
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 4103
+    :versions: 1.2.0b3
+
+    Fixed bug where correlated select used against single-table inheritance
+    entity would fail to render correctly in the outer query, due to adjustment
+    for single inheritance discriminator criteria inappropriately re-applying
+    the criteria to the outer query.
\ No newline at end of file
index e8bd7177237233f07a78ba571fdbeaab196e992f..b98a21e79b3f2c42c4b41cf9c2f4e932aea10b5c 100644 (file)
@@ -4010,7 +4010,8 @@ class _ColumnEntity(_QueryEntity):
             self._from_entities = set(self.entities)
         else:
             all_elements = [
-                elem for elem in sql_util.surface_column_elements(column)
+                elem for elem in sql_util.surface_column_elements(
+                    column, include_scalar_selects=False)
                 if 'parententity' in elem._annotations
             ]
 
index 281d5f6a32d21d90f93b7f4b2a0bff9f42ef33bc..0c122949baddac2fdf2ebef77e5ad18dcf06e242 100644 (file)
@@ -18,7 +18,7 @@ from collections import deque
 from .elements import BindParameter, ColumnClause, ColumnElement, \
     Null, UnaryExpression, literal_column, Label, _label_reference, \
     _textual_label_reference
-from .selectable import ScalarSelect, Join, FromClause, FromGrouping
+from .selectable import SelectBase, ScalarSelect, Join, FromClause, FromGrouping
 from .schema import Column
 
 join_condition = util.langhelpers.public_factory(
@@ -235,17 +235,21 @@ def surface_selectables(clause):
             stack.append(elem.element)
 
 
-def surface_column_elements(clause):
+def surface_column_elements(clause, include_scalar_selects=True):
     """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."""
 
+    filter_ = (FromGrouping, )
+    if not include_scalar_selects:
+        filter_ += (SelectBase, )
+
     stack = deque([clause])
     while stack:
         elem = stack.popleft()
         yield elem
         for sub in elem.get_children():
-            if isinstance(sub, FromGrouping):
+            if isinstance(sub, filter_):
                 continue
             stack.append(sub)
 
index 26cf9fa01a0ebbbb66508179c47a497ed3356cb4..aa9f62f6ff4b0fe138ce1eacbd52e4f8d2cfe2a0 100644 (file)
@@ -754,6 +754,38 @@ class RelationshipToSingleTest(
             "AND employees_1.type IN (:type_1)"
         )
 
+    def test_correlated_column_select(self):
+        Company, Employee, Engineer = (self.classes.Company,
+                                       self.classes.Employee,
+                                       self.classes.Engineer)
+        companies, employees = self.tables.companies, self.tables.employees
+
+        mapper(Company, companies)
+        mapper(
+            Employee, employees,
+            polymorphic_on=employees.c.type,
+            properties={
+                'company': relationship(Company)
+            }
+        )
+        mapper(Engineer, inherits=Employee, polymorphic_identity='engineer')
+
+        sess = create_session()
+        engineer_count = sess.query(func.count(Engineer.employee_id)) \
+            .select_from(Engineer) \
+            .filter(Engineer.company_id == Company.company_id) \
+            .correlate(Company) \
+            .as_scalar()
+
+        self.assert_compile(
+            sess.query(Company.company_id, engineer_count),
+            "SELECT companies.company_id AS companies_company_id, "
+            "(SELECT count(employees.employee_id) AS count_1 "
+            "FROM employees WHERE employees.company_id = "
+            "companies.company_id AND employees.type IN (:type_1)) AS anon_1 "
+            "FROM companies"
+        )
+
     def test_no_aliasing_from_overlap(self):
         # test [ticket:3233]