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

This commit is contained in:
Mariusz Felisiak 2018-12-02 22:17:32 +00:00 committed by Tim Graham
parent ae180fa4b7
commit f9a33e3c3f
4 changed files with 19 additions and 1 deletions

View File

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

View File

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

View File

@ -429,7 +429,17 @@ class SQLCompiler:
*self.query.values_select, *self.query.values_select,
*self.query.annotation_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: except EmptyResultSet:
# Omit the empty queryset with UNION and with DIFFERENCE if the # Omit the empty queryset with UNION and with DIFFERENCE if the
# first queryset is nonempty. # first queryset is nonempty.

View File

@ -214,3 +214,9 @@ class QuerySetSetOperationTests(TestCase):
list(qs1.union(qs2).order_by('num')) list(qs1.union(qs2).order_by('num'))
# switched order, now 'exists' again: # switched order, now 'exists' again:
list(qs2.union(qs1).order_by('num')) 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)