From 41ebe60728a15aa273f4d70de92f5246a89c3d4e Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 27 Feb 2020 00:34:37 -0500 Subject: [PATCH] Fixed #31312 -- Properly ordered temporal subtraction params on MySQL. Regression in 9bcbcd599abac91ea853b2fe10b784ba32df043e. Thanks rick2ricks for the report. --- django/db/backends/mysql/operations.py | 2 +- docs/releases/3.0.4.txt | 4 ++++ tests/expressions/tests.py | 23 ++++++++++++++++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 53429577672..6aaf5c62954 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -291,7 +291,7 @@ class DatabaseOperations(BaseDatabaseOperations): "((TIME_TO_SEC(%(lhs)s) * 1000000 + MICROSECOND(%(lhs)s)) -" " (TIME_TO_SEC(%(rhs)s) * 1000000 + MICROSECOND(%(rhs)s)))" ) % {'lhs': lhs_sql, 'rhs': rhs_sql}, tuple(lhs_params) * 2 + tuple(rhs_params) * 2 - params = (*lhs_params, *rhs_params) + params = (*rhs_params, *lhs_params) return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), params def explain_query_prefix(self, format=None, **options): diff --git a/docs/releases/3.0.4.txt b/docs/releases/3.0.4.txt index 216bc29b0e2..1da9a225520 100644 --- a/docs/releases/3.0.4.txt +++ b/docs/releases/3.0.4.txt @@ -23,3 +23,7 @@ Bugfixes * Fixed a regression in Django 3.0 that caused misplacing parameters in logged SQL queries on Oracle (:ticket:`31271`). + +* Fixed a regression in Django 3.0.3 that caused misplacing parameters of SQL + queries when subtracting ``DateField`` or ``DateTimeField`` expressions on + MySQL (:ticket:`31312`). diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index 6f1a9723e8d..5e60c379a96 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -1221,13 +1221,13 @@ class FTimeDeltaTests(TestCase): # e0: started same day as assigned, zero duration end = stime + delta0 - e0 = Experiment.objects.create( + cls.e0 = Experiment.objects.create( name='e0', assigned=sday, start=stime, end=end, completed=end.date(), estimated_time=delta0, ) cls.deltas.append(delta0) - cls.delays.append(e0.start - datetime.datetime.combine(e0.assigned, midnight)) - cls.days_long.append(e0.completed - e0.assigned) + cls.delays.append(cls.e0.start - datetime.datetime.combine(cls.e0.assigned, midnight)) + cls.days_long.append(cls.e0.completed - cls.e0.assigned) # e1: started one day after assigned, tiny duration, data # set so that end time has no fractional seconds, which @@ -1434,6 +1434,23 @@ class FTimeDeltaTests(TestCase): ).filter(difference=datetime.timedelta()) self.assertTrue(queryset.exists()) + @skipUnlessDBFeature('supports_temporal_subtraction') + def test_date_case_subtraction(self): + queryset = Experiment.objects.annotate( + date_case=Case( + When(Q(name='e0'), then=F('completed')), + output_field=DateField(), + ), + completed_value=Value( + self.e0.completed, + output_field=DateField(), + ), + difference=ExpressionWrapper( + F('date_case') - F('completed_value'), output_field=DurationField(), + ), + ).filter(difference=datetime.timedelta()) + self.assertEqual(queryset.get(), self.e0) + @skipUnlessDBFeature('supports_temporal_subtraction') def test_time_subtraction(self): Time.objects.create(time=datetime.time(12, 30, 15, 2345))