mirror of
https://github.com/django/django.git
synced 2024-11-29 22:56:46 +01:00
Fixed #30494 -- Disabled __year lookup optimization for indirect values.
The previous heuristics were naively enabling the BETWEEN optimization on successful cast of the first rhs SQL params to an integer while it was not appropriate for a lot of database resolved expressions. Thanks Alexey Chernov for the report.
This commit is contained in:
parent
1d0bab0bfd
commit
4d1420947e
@ -508,21 +508,16 @@ class YearExact(YearLookup, Exact):
|
|||||||
lookup_name = 'exact'
|
lookup_name = 'exact'
|
||||||
|
|
||||||
def as_sql(self, compiler, connection):
|
def as_sql(self, compiler, connection):
|
||||||
# We will need to skip the extract part and instead go
|
# Avoid the extract operation if the rhs is a direct value to allow
|
||||||
# directly with the originating field, that is self.lhs.lhs.
|
# indexes to be used.
|
||||||
lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
|
if self.rhs_is_direct_value():
|
||||||
rhs_sql, rhs_params = self.process_rhs(compiler, connection)
|
# Skip the extract part by directly using the originating field,
|
||||||
try:
|
# that is self.lhs.lhs.
|
||||||
# Check that rhs_params[0] exists (IndexError),
|
lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
|
||||||
# it isn't None (TypeError), and is a number (ValueError)
|
bounds = self.year_lookup_bounds(connection, self.rhs)
|
||||||
int(rhs_params[0])
|
params.extend(bounds)
|
||||||
except (IndexError, TypeError, ValueError):
|
return '%s BETWEEN %%s AND %%s' % lhs_sql, params
|
||||||
# Can't determine the bounds before executing the query, so skip
|
return super().as_sql(compiler, connection)
|
||||||
# optimizations by falling back to a standard exact comparison.
|
|
||||||
return super().as_sql(compiler, connection)
|
|
||||||
bounds = self.year_lookup_bounds(connection, rhs_params[0])
|
|
||||||
params.extend(bounds)
|
|
||||||
return '%s BETWEEN %%s AND %%s' % lhs_sql, params
|
|
||||||
|
|
||||||
|
|
||||||
class YearGt(YearComparisonLookup):
|
class YearGt(YearComparisonLookup):
|
||||||
|
@ -4,7 +4,8 @@ import pytz
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import (
|
from django.db.models import (
|
||||||
DateField, DateTimeField, IntegerField, Max, OuterRef, Subquery, TimeField,
|
DateField, DateTimeField, F, IntegerField, Max, OuterRef, Subquery,
|
||||||
|
TimeField,
|
||||||
)
|
)
|
||||||
from django.db.models.functions import (
|
from django.db.models.functions import (
|
||||||
Extract, ExtractDay, ExtractHour, ExtractIsoYear, ExtractMinute,
|
Extract, ExtractDay, ExtractHour, ExtractIsoYear, ExtractMinute,
|
||||||
@ -108,6 +109,14 @@ class DateFunctionTests(TestCase):
|
|||||||
query_string = str(qs.query).lower()
|
query_string = str(qs.query).lower()
|
||||||
self.assertEqual(query_string.count(' between '), 1)
|
self.assertEqual(query_string.count(' between '), 1)
|
||||||
self.assertEqual(query_string.count('extract'), 0)
|
self.assertEqual(query_string.count('extract'), 0)
|
||||||
|
# an expression rhs cannot use the between optimization.
|
||||||
|
qs = DTModel.objects.annotate(
|
||||||
|
start_year=ExtractYear('start_datetime'),
|
||||||
|
).filter(end_datetime__year=F('start_year') + 1)
|
||||||
|
self.assertEqual(qs.count(), 1)
|
||||||
|
query_string = str(qs.query).lower()
|
||||||
|
self.assertEqual(query_string.count(' between '), 0)
|
||||||
|
self.assertEqual(query_string.count('extract'), 3)
|
||||||
|
|
||||||
def test_extract_year_greaterthan_lookup(self):
|
def test_extract_year_greaterthan_lookup(self):
|
||||||
start_datetime = datetime(2015, 6, 15, 14, 10)
|
start_datetime = datetime(2015, 6, 15, 14, 10)
|
||||||
|
Loading…
Reference in New Issue
Block a user