Selaa lähdekoodia

Fixed #29932 -- Fixed combining compound queries with sub-compound queries on SQLite and Oracle.

Mariusz Felisiak 6 vuotta sitten
vanhempi
commit
f9a33e3c3f

+ 1 - 0
django/db/backends/base/features.py

@@ -226,6 +226,7 @@ class BaseDatabaseFeatures:
     supports_select_intersection = True
     supports_select_difference = True
     supports_slicing_ordering_in_compound = False
+    supports_parentheses_in_compound = True
 
     # Does the database support SQL 2003 FILTER (WHERE ...) in aggregate
     # expressions?

+ 1 - 0
django/db/backends/sqlite3/features.py

@@ -37,6 +37,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
     supports_partial_indexes = Database.version_info >= (3, 8, 0)
     # Is "ALTER TABLE ... RENAME COLUMN" supported?
     can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0)
+    supports_parentheses_in_compound = False
 
     @cached_property
     def supports_stddev(self):

+ 11 - 1
django/db/models/sql/compiler.py

@@ -429,7 +429,17 @@ class SQLCompiler:
                         *self.query.values_select,
                         *self.query.annotation_select,
                     ))
-                parts += (compiler.as_sql(),)
+                part_sql, part_args = compiler.as_sql()
+                if compiler.query.combinator:
+                    # Wrap in a subquery if wrapping in parentheses isn't
+                    # supported.
+                    if not features.supports_parentheses_in_compound:
+                        part_sql = 'SELECT * FROM ({})'.format(part_sql)
+                    # Add parentheses when combining with compound query if not
+                    # already added for all compound queries.
+                    elif not features.supports_slicing_ordering_in_compound:
+                        part_sql = '({})'.format(part_sql)
+                parts += ((part_sql, part_args),)
             except EmptyResultSet:
                 # Omit the empty queryset with UNION and with DIFFERENCE if the
                 # first queryset is nonempty.

+ 6 - 0
tests/queries/test_qs_combinators.py

@@ -214,3 +214,9 @@ class QuerySetSetOperationTests(TestCase):
             list(qs1.union(qs2).order_by('num'))
         # switched order, now 'exists' again:
         list(qs2.union(qs1).order_by('num'))
+
+    @skipUnlessDBFeature('supports_select_difference', 'supports_select_intersection')
+    def test_qs_with_subcompound_qs(self):
+        qs1 = Number.objects.all()
+        qs2 = Number.objects.intersection(Number.objects.filter(num__gt=1))
+        self.assertEqual(qs1.difference(qs2).count(), 2)