mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #32691 -- Made Exact lookup on BooleanFields compare directly to a boolean value on MySQL.
Performance regression in 37e6c5b79b
.
Thanks Todor Velichkov for the report.
Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
parent
aaf9b55858
commit
407fe95cb1
@ -2,6 +2,7 @@ import uuid
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.backends.base.operations import BaseDatabaseOperations
|
from django.db.backends.base.operations import BaseDatabaseOperations
|
||||||
|
from django.db.models import Exists, ExpressionWrapper, Lookup
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
|
|
||||||
@ -378,3 +379,14 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||||||
):
|
):
|
||||||
lookup = 'JSON_UNQUOTE(%s)'
|
lookup = 'JSON_UNQUOTE(%s)'
|
||||||
return lookup
|
return lookup
|
||||||
|
|
||||||
|
def conditional_expression_supported_in_where_clause(self, expression):
|
||||||
|
# MySQL ignores indexes with boolean fields unless they're compared
|
||||||
|
# directly to a boolean value.
|
||||||
|
if isinstance(expression, (Exists, Lookup)):
|
||||||
|
return True
|
||||||
|
if isinstance(expression, ExpressionWrapper) and expression.conditional:
|
||||||
|
return self.conditional_expression_supported_in_where_clause(expression.expression)
|
||||||
|
if getattr(expression, 'conditional', False):
|
||||||
|
return False
|
||||||
|
return super().conditional_expression_supported_in_where_clause(expression)
|
||||||
|
@ -2,6 +2,7 @@ import collections.abc
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from unittest import skipUnless
|
||||||
|
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.db import connection, models
|
from django.db import connection, models
|
||||||
@ -927,6 +928,44 @@ class LookupTests(TestCase):
|
|||||||
with self.assertRaisesMessage(ValueError, msg):
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
list(Article.objects.filter(author=Author.objects.all()[1:]))
|
list(Article.objects.filter(author=Author.objects.all()[1:]))
|
||||||
|
|
||||||
|
@skipUnless(connection.vendor == 'mysql', 'MySQL-specific workaround.')
|
||||||
|
def test_exact_booleanfield(self):
|
||||||
|
# MySQL ignores indexes with boolean fields unless they're compared
|
||||||
|
# directly to a boolean value.
|
||||||
|
product = Product.objects.create(name='Paper', qty_target=5000)
|
||||||
|
Stock.objects.create(product=product, short=False, qty_available=5100)
|
||||||
|
stock_1 = Stock.objects.create(product=product, short=True, qty_available=180)
|
||||||
|
qs = Stock.objects.filter(short=True)
|
||||||
|
self.assertSequenceEqual(qs, [stock_1])
|
||||||
|
self.assertIn(
|
||||||
|
'%s = True' % connection.ops.quote_name('short'),
|
||||||
|
str(qs.query),
|
||||||
|
)
|
||||||
|
|
||||||
|
@skipUnless(connection.vendor == 'mysql', 'MySQL-specific workaround.')
|
||||||
|
def test_exact_booleanfield_annotation(self):
|
||||||
|
# MySQL ignores indexes with boolean fields unless they're compared
|
||||||
|
# directly to a boolean value.
|
||||||
|
qs = Author.objects.annotate(case=Case(
|
||||||
|
When(alias='a1', then=True),
|
||||||
|
default=False,
|
||||||
|
output_field=BooleanField(),
|
||||||
|
)).filter(case=True)
|
||||||
|
self.assertSequenceEqual(qs, [self.au1])
|
||||||
|
self.assertIn(' = True', str(qs.query))
|
||||||
|
|
||||||
|
qs = Author.objects.annotate(
|
||||||
|
wrapped=ExpressionWrapper(Q(alias='a1'), output_field=BooleanField()),
|
||||||
|
).filter(wrapped=True)
|
||||||
|
self.assertSequenceEqual(qs, [self.au1])
|
||||||
|
self.assertIn(' = True', str(qs.query))
|
||||||
|
# EXISTS(...) shouldn't be compared to a boolean value.
|
||||||
|
qs = Author.objects.annotate(
|
||||||
|
exists=Exists(Author.objects.filter(alias='a1', pk=OuterRef('pk'))),
|
||||||
|
).filter(exists=True)
|
||||||
|
self.assertSequenceEqual(qs, [self.au1])
|
||||||
|
self.assertNotIn(' = True', str(qs.query))
|
||||||
|
|
||||||
def test_custom_field_none_rhs(self):
|
def test_custom_field_none_rhs(self):
|
||||||
"""
|
"""
|
||||||
__exact=value is transformed to __isnull=True if Field.get_prep_value()
|
__exact=value is transformed to __isnull=True if Field.get_prep_value()
|
||||||
|
Loading…
Reference in New Issue
Block a user