summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/models/lookups.py25
-rw-r--r--tests/db_functions/datetime/test_extract_trunc.py11
2 files changed, 20 insertions, 16 deletions
diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py
index 70cd525f30..d4edf2ce0f 100644
--- a/django/db/models/lookups.py
+++ b/django/db/models/lookups.py
@@ -508,21 +508,16 @@ class YearExact(YearLookup, Exact):
lookup_name = 'exact'
def as_sql(self, compiler, connection):
- # We will need to skip the extract part and instead go
- # directly with the originating field, that is self.lhs.lhs.
- lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
- rhs_sql, rhs_params = self.process_rhs(compiler, connection)
- try:
- # Check that rhs_params[0] exists (IndexError),
- # it isn't None (TypeError), and is a number (ValueError)
- int(rhs_params[0])
- except (IndexError, TypeError, ValueError):
- # Can't determine the bounds before executing the query, so skip
- # 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
+ # Avoid the extract operation if the rhs is a direct value to allow
+ # indexes to be used.
+ if self.rhs_is_direct_value():
+ # Skip the extract part by directly using the originating field,
+ # that is self.lhs.lhs.
+ lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
+ bounds = self.year_lookup_bounds(connection, self.rhs)
+ params.extend(bounds)
+ return '%s BETWEEN %%s AND %%s' % lhs_sql, params
+ return super().as_sql(compiler, connection)
class YearGt(YearComparisonLookup):
diff --git a/tests/db_functions/datetime/test_extract_trunc.py b/tests/db_functions/datetime/test_extract_trunc.py
index 2088d09d06..f62bd0f0b2 100644
--- a/tests/db_functions/datetime/test_extract_trunc.py
+++ b/tests/db_functions/datetime/test_extract_trunc.py
@@ -4,7 +4,8 @@ import pytz
from django.conf import settings
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 (
Extract, ExtractDay, ExtractHour, ExtractIsoYear, ExtractMinute,
@@ -108,6 +109,14 @@ class DateFunctionTests(TestCase):
query_string = str(qs.query).lower()
self.assertEqual(query_string.count(' between '), 1)
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):
start_datetime = datetime(2015, 6, 15, 14, 10)