From 45c035c823bfbd642dc1490f1c555316af403c4c Mon Sep 17 00:00:00 2001 From: Tom Forbes Date: Mon, 9 Jul 2018 19:59:42 +0100 Subject: [PATCH] Refs #29548 -- Fixed non-GIS test failures on MariaDB. --- django/db/backends/mysql/features.py | 8 +++++--- django/db/backends/mysql/operations.py | 13 +++++++++++++ tests/backends/mysql/test_features.py | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index 56370bfcd98..ec80c6e54e6 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -80,18 +80,20 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def supports_over_clause(self): + if self.connection.mysql_is_mariadb: + return self.connection.mysql_version >= (10, 2) return self.connection.mysql_version >= (8, 0, 2) @cached_property def has_select_for_update_skip_locked(self): - return self.connection.mysql_version >= (8, 0, 1) + return not self.connection.mysql_is_mariadb and self.connection.mysql_version >= (8, 0, 1) has_select_for_update_nowait = has_select_for_update_skip_locked @cached_property def needs_explain_extended(self): - # EXTENDED is deprecated (and not required) in 5.7 and removed in 8.0. - return self.connection.mysql_version < (5, 7) + # EXTENDED is deprecated (and not required) in MySQL 5.7. + return not self.connection.mysql_is_mariadb and self.connection.mysql_version < (5, 7) @cached_property def supports_transactions(self): diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 31f92ac5cdc..ddeb128d324 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -1,3 +1,4 @@ +import decimal import uuid from django.conf import settings @@ -260,10 +261,22 @@ class DatabaseOperations(BaseDatabaseOperations): def binary_placeholder_sql(self, value): return '_binary %s' if value is not None and not hasattr(value, 'as_sql') else '%s' + def convert_durationfield_value(self, value, expression, connection): + # DurationFields can return a Decimal in MariaDB. + if isinstance(value, decimal.Decimal): + value = float(value) + return super().convert_durationfield_value(value, expression, connection) + def subtract_temporals(self, internal_type, lhs, rhs): lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs if internal_type == 'TimeField': + if self.connection.mysql_is_mariadb: + # MariaDB includes the microsecond component in TIME_TO_SEC as + # a decimal. MySQL returns an integer without microseconds. + return '((TIME_TO_SEC(%(lhs)s) - TIME_TO_SEC(%(rhs)s)) * 1000000)' % { + 'lhs': lhs_sql, 'rhs': rhs_sql + }, lhs_params + rhs_params return ( "((TIME_TO_SEC(%(lhs)s) * 1000000 + MICROSECOND(%(lhs)s)) -" " (TIME_TO_SEC(%(rhs)s) * 1000000 + MICROSECOND(%(rhs)s)))" diff --git a/tests/backends/mysql/test_features.py b/tests/backends/mysql/test_features.py index 51282792b3a..1385c9b88c8 100644 --- a/tests/backends/mysql/test_features.py +++ b/tests/backends/mysql/test_features.py @@ -22,11 +22,13 @@ class TestFeatures(TestCase): def test_skip_locked_no_wait(self): with mock.MagicMock() as _connection: _connection.mysql_version = (8, 0, 1) + _connection.mysql_is_mariadb = False database_features = DatabaseFeatures(_connection) self.assertTrue(database_features.has_select_for_update_skip_locked) self.assertTrue(database_features.has_select_for_update_nowait) with mock.MagicMock() as _connection: _connection.mysql_version = (8, 0, 0) + _connection.mysql_is_mariadb = False database_features = DatabaseFeatures(_connection) self.assertFalse(database_features.has_select_for_update_skip_locked) self.assertFalse(database_features.has_select_for_update_nowait)