[3.0.x] Fixed #31133 -- Fixed crash when subtracting against a subquery annotation.
The subtract_temporals() database operation was not handling expressions returning SQL params in mixed database types. Regression in 35431298226165986ad07e91f9d3aca721ff38ec. Thanks Reupen Shah for the report. Backport of 9bcbcd599abac91ea853b2fe10b784ba32df043e from master
This commit is contained in:
parent
d975415852
commit
02cda09b13
@ -625,7 +625,7 @@ class BaseDatabaseOperations:
|
||||
if self.connection.features.supports_temporal_subtraction:
|
||||
lhs_sql, lhs_params = lhs
|
||||
rhs_sql, rhs_params = rhs
|
||||
return "(%s - %s)" % (lhs_sql, rhs_sql), lhs_params + rhs_params
|
||||
return '(%s - %s)' % (lhs_sql, rhs_sql), (*lhs_params, *rhs_params)
|
||||
raise NotSupportedError("This backend does not support %s subtraction." % internal_type)
|
||||
|
||||
def window_frame_start(self, start):
|
||||
|
@ -282,13 +282,13 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||
# a decimal. MySQL returns an integer without microseconds.
|
||||
return 'CAST((TIME_TO_SEC(%(lhs)s) - TIME_TO_SEC(%(rhs)s)) * 1000000 AS SIGNED)' % {
|
||||
'lhs': lhs_sql, 'rhs': rhs_sql
|
||||
}, lhs_params + rhs_params
|
||||
}, (*lhs_params, *rhs_params)
|
||||
return (
|
||||
"((TIME_TO_SEC(%(lhs)s) * 1000000 + MICROSECOND(%(lhs)s)) -"
|
||||
" (TIME_TO_SEC(%(rhs)s) * 1000000 + MICROSECOND(%(rhs)s)))"
|
||||
) % {'lhs': lhs_sql, 'rhs': rhs_sql}, lhs_params * 2 + rhs_params * 2
|
||||
else:
|
||||
return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), rhs_params + lhs_params
|
||||
) % {'lhs': lhs_sql, 'rhs': rhs_sql}, tuple(lhs_params) * 2 + tuple(rhs_params) * 2
|
||||
params = (*lhs_params, *rhs_params)
|
||||
return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), params
|
||||
|
||||
def explain_query_prefix(self, format=None, **options):
|
||||
# Alias MySQL's TRADITIONAL to TEXT for consistency with other backends.
|
||||
|
@ -607,7 +607,8 @@ END;
|
||||
if internal_type == 'DateField':
|
||||
lhs_sql, lhs_params = lhs
|
||||
rhs_sql, rhs_params = rhs
|
||||
return "NUMTODSINTERVAL(TO_NUMBER(%s - %s), 'DAY')" % (lhs_sql, rhs_sql), lhs_params + rhs_params
|
||||
params = (*lhs_params, *rhs_params)
|
||||
return "NUMTODSINTERVAL(TO_NUMBER(%s - %s), 'DAY')" % (lhs_sql, rhs_sql), params
|
||||
return super().subtract_temporals(internal_type, lhs, rhs)
|
||||
|
||||
def bulk_batch_size(self, fields, objs):
|
||||
|
@ -269,7 +269,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||
if internal_type == 'DateField':
|
||||
lhs_sql, lhs_params = lhs
|
||||
rhs_sql, rhs_params = rhs
|
||||
return "(interval '1 day' * (%s - %s))" % (lhs_sql, rhs_sql), lhs_params + rhs_params
|
||||
params = (*lhs_params, *rhs_params)
|
||||
return "(interval '1 day' * (%s - %s))" % (lhs_sql, rhs_sql), params
|
||||
return super().subtract_temporals(internal_type, lhs, rhs)
|
||||
|
||||
def window_frame_range_start_end(self, start=None, end=None):
|
||||
|
@ -326,9 +326,10 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||
def subtract_temporals(self, internal_type, lhs, rhs):
|
||||
lhs_sql, lhs_params = lhs
|
||||
rhs_sql, rhs_params = rhs
|
||||
params = (*lhs_params, *rhs_params)
|
||||
if internal_type == 'TimeField':
|
||||
return "django_time_diff(%s, %s)" % (lhs_sql, rhs_sql), lhs_params + rhs_params
|
||||
return "django_timestamp_diff(%s, %s)" % (lhs_sql, rhs_sql), lhs_params + rhs_params
|
||||
return 'django_time_diff(%s, %s)' % (lhs_sql, rhs_sql), params
|
||||
return 'django_timestamp_diff(%s, %s)' % (lhs_sql, rhs_sql), params
|
||||
|
||||
def insert_statement(self, ignore_conflicts=False):
|
||||
return 'INSERT OR IGNORE INTO' if ignore_conflicts else super().insert_statement(ignore_conflicts)
|
||||
|
@ -9,4 +9,6 @@ Django 3.0.3 fixes several bugs in 3.0.2.
|
||||
Bugfixes
|
||||
========
|
||||
|
||||
* ...
|
||||
* Fixed a regression in Django 3.0 that caused a crash when subtracting
|
||||
``DateField``, ``DateTimeField``, or ``TimeField`` from a ``Subquery()``
|
||||
annotation (:ticket:`31133`).
|
||||
|
@ -1424,6 +1424,16 @@ class FTimeDeltaTests(TestCase):
|
||||
))
|
||||
self.assertIsNone(queryset.first().shifted)
|
||||
|
||||
@skipUnlessDBFeature('supports_temporal_subtraction')
|
||||
def test_date_subquery_subtraction(self):
|
||||
subquery = Experiment.objects.filter(pk=OuterRef('pk')).values('completed')
|
||||
queryset = Experiment.objects.annotate(
|
||||
difference=ExpressionWrapper(
|
||||
subquery - F('completed'), output_field=models.DurationField(),
|
||||
),
|
||||
).filter(difference=datetime.timedelta())
|
||||
self.assertTrue(queryset.exists())
|
||||
|
||||
@skipUnlessDBFeature('supports_temporal_subtraction')
|
||||
def test_time_subtraction(self):
|
||||
Time.objects.create(time=datetime.time(12, 30, 15, 2345))
|
||||
@ -1450,6 +1460,17 @@ class FTimeDeltaTests(TestCase):
|
||||
))
|
||||
self.assertIsNone(queryset.first().shifted)
|
||||
|
||||
@skipUnlessDBFeature('supports_temporal_subtraction')
|
||||
def test_time_subquery_subtraction(self):
|
||||
Time.objects.create(time=datetime.time(12, 30, 15, 2345))
|
||||
subquery = Time.objects.filter(pk=OuterRef('pk')).values('time')
|
||||
queryset = Time.objects.annotate(
|
||||
difference=ExpressionWrapper(
|
||||
subquery - F('time'), output_field=models.DurationField(),
|
||||
),
|
||||
).filter(difference=datetime.timedelta())
|
||||
self.assertTrue(queryset.exists())
|
||||
|
||||
@skipUnlessDBFeature('supports_temporal_subtraction')
|
||||
def test_datetime_subtraction(self):
|
||||
under_estimate = [
|
||||
@ -1474,6 +1495,16 @@ class FTimeDeltaTests(TestCase):
|
||||
))
|
||||
self.assertIsNone(queryset.first().shifted)
|
||||
|
||||
@skipUnlessDBFeature('supports_temporal_subtraction')
|
||||
def test_datetime_subquery_subtraction(self):
|
||||
subquery = Experiment.objects.filter(pk=OuterRef('pk')).values('start')
|
||||
queryset = Experiment.objects.annotate(
|
||||
difference=ExpressionWrapper(
|
||||
subquery - F('start'), output_field=models.DurationField(),
|
||||
),
|
||||
).filter(difference=datetime.timedelta())
|
||||
self.assertTrue(queryset.exists())
|
||||
|
||||
@skipUnlessDBFeature('supports_temporal_subtraction')
|
||||
def test_datetime_subtraction_microseconds(self):
|
||||
delta = datetime.timedelta(microseconds=8999999999999999)
|
||||
|
Loading…
x
Reference in New Issue
Block a user