From 45c035c823bfbd642dc1490f1c555316af403c4c Mon Sep 17 00:00:00 2001
From: Tom Forbes <tom@tomforb.es>
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 56370bfcd9..ec80c6e54e 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 31f92ac5cd..ddeb128d32 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 51282792b3..1385c9b88c 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)