summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Pope <nick.pope@flightdataservices.com>2019-10-21 17:34:19 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-10-24 15:13:26 +0200
commit55df1750be3c88db89444335f77dca10681dcbe3 (patch)
tree71c8547cd87b78ba47659ad34aff957352172251
parent742961332e1e2221e6fb9506c7254164e0c2cb5a (diff)
Refs #30897 -- Added support for ANALYZE option to Queryset.explain() on MariaDB and MySQL 8.0.18+.
-rw-r--r--django/db/backends/mysql/features.py4
-rw-r--r--django/db/backends/mysql/operations.py11
-rw-r--r--docs/ref/models/querysets.txt9
-rw-r--r--docs/releases/3.1.txt5
-rw-r--r--tests/queries/test_explain.py19
5 files changed, 40 insertions, 8 deletions
diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py
index 0bcb05b1d1..6a7f87ba27 100644
--- a/django/db/backends/mysql/features.py
+++ b/django/db/backends/mysql/features.py
@@ -118,6 +118,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
return not self.connection.mysql_is_mariadb and self.connection.mysql_version < (5, 7)
@cached_property
+ def supports_explain_analyze(self):
+ return self.connection.mysql_is_mariadb or self.connection.mysql_version >= (8, 0, 18)
+
+ @cached_property
def supported_explain_formats(self):
# Alias MySQL's TRADITIONAL to TEXT for consistency with other
# backends.
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py
index ef5d23214e..b801afef4a 100644
--- a/django/db/backends/mysql/operations.py
+++ b/django/db/backends/mysql/operations.py
@@ -299,11 +299,16 @@ class DatabaseOperations(BaseDatabaseOperations):
elif not format and 'TREE' in self.connection.features.supported_explain_formats:
# Use TREE by default (if supported) as it's more informative.
format = 'TREE'
+ analyze = options.pop('analyze', False)
prefix = super().explain_query_prefix(format, **options)
- if format:
+ if analyze and self.connection.features.supports_explain_analyze:
+ # MariaDB uses ANALYZE instead of EXPLAIN ANALYZE.
+ prefix = 'ANALYZE' if self.connection.mysql_is_mariadb else prefix + ' ANALYZE'
+ if format and not (analyze and not self.connection.mysql_is_mariadb):
+ # Only MariaDB supports the analyze option with formats.
prefix += ' FORMAT=%s' % format
- if self.connection.features.needs_explain_extended and format is None:
- # EXTENDED and FORMAT are mutually exclusive options.
+ if self.connection.features.needs_explain_extended and not analyze and format is None:
+ # ANALYZE, EXTENDED, and FORMAT are mutually exclusive options.
prefix += ' EXTENDED'
return prefix
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 136674be2a..766a379a13 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -2587,13 +2587,14 @@ Pass these flags as keyword arguments. For example, when using PostgreSQL::
Execution time: 0.058 ms
On some databases, flags may cause the query to be executed which could have
-adverse effects on your database. For example, PostgreSQL's ``ANALYZE`` flag
-could result in changes to data if there are triggers or if a function is
-called, even for a ``SELECT`` query.
+adverse effects on your database. For example, the ``ANALYZE`` flag supported
+by MariaDB, MySQL 8.0.18+, and PostgreSQL could result in changes to data if
+there are triggers or if a function is called, even for a ``SELECT`` query.
.. versionchanged:: 3.1
- Support for the ``'TREE'`` format on MySQL 8.0.16+ was added.
+ Support for the ``'TREE'`` format on MySQL 8.0.16+ and ``analyze`` option
+ on MariaDB and MySQL 8.0.18+ were added.
.. _field-lookups:
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index cf77bb15db..70297b5311 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -169,7 +169,10 @@ Models
:class:`~django.db.models.DateTimeField`, and the new :lookup:`iso_week_day`
lookup allows querying by an ISO-8601 day of week.
-* :meth:`.QuerySet.explain` now supports ``TREE`` format on MySQL 8.0.16+.
+* :meth:`.QuerySet.explain` now supports:
+
+ * ``TREE`` format on MySQL 8.0.16+,
+ * ``analyze`` option on MySQL 8.0.18+ and MariaDB.
Pagination
~~~~~~~~~~
diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py
index e3872213a0..481924a2e4 100644
--- a/tests/queries/test_explain.py
+++ b/tests/queries/test_explain.py
@@ -80,6 +80,25 @@ class ExplainTests(TestCase):
self.assertEqual(len(captured_queries), 1)
self.assertIn('FORMAT=TRADITIONAL', captured_queries[0]['sql'])
+ @unittest.skipUnless(connection.vendor == 'mysql', 'MariaDB and MySQL >= 8.0.18 specific.')
+ def test_mysql_analyze(self):
+ # Inner skip to avoid module level query for MySQL version.
+ if not connection.features.supports_explain_analyze:
+ raise unittest.SkipTest('MariaDB and MySQL >= 8.0.18 specific.')
+ qs = Tag.objects.filter(name='test')
+ with CaptureQueriesContext(connection) as captured_queries:
+ qs.explain(analyze=True)
+ self.assertEqual(len(captured_queries), 1)
+ prefix = 'ANALYZE ' if connection.mysql_is_mariadb else 'EXPLAIN ANALYZE '
+ self.assertTrue(captured_queries[0]['sql'].startswith(prefix))
+ with CaptureQueriesContext(connection) as captured_queries:
+ qs.explain(analyze=True, format='JSON')
+ self.assertEqual(len(captured_queries), 1)
+ if connection.mysql_is_mariadb:
+ self.assertIn('FORMAT=JSON', captured_queries[0]['sql'])
+ else:
+ self.assertNotIn('FORMAT=JSON', captured_queries[0]['sql'])
+
@unittest.skipUnless(connection.vendor == 'mysql', 'MySQL < 5.7 specific')
def test_mysql_extended(self):
# Inner skip to avoid module level query for MySQL version.