diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index faa84f7d7c2..499c303d9e2 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -124,11 +124,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): return self.connection.mysql_version >= (10, 3, 0) return self.connection.mysql_version >= (8, 0, 1) - @cached_property - def needs_explain_extended(self): - # 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_explain_analyze(self): return self.connection.mysql_is_mariadb or self.connection.mysql_version >= (8, 0, 18) diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index bc04739f0d8..af8cfd86b52 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -350,14 +350,11 @@ class DatabaseOperations(BaseDatabaseOperations): 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 not analyze and format is None: - # ANALYZE, EXTENDED, and FORMAT are mutually exclusive options. - prefix += ' EXTENDED' return prefix def regex_lookup(self, lookup_type): # REGEXP BINARY doesn't work correctly in MySQL 8+ and REGEXP_LIKE - # doesn't exist in MySQL 5.6 or in MariaDB. + # doesn't exist in MySQL 5.x or in MariaDB. if self.connection.mysql_version < (8, 0, 0) or self.connection.mysql_is_mariadb: if lookup_type == 'regex': return '%s REGEXP BINARY %s' diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index 5242425aaa7..5d99cfc0096 100644 --- a/docs/ref/contrib/gis/install/index.txt +++ b/docs/ref/contrib/gis/install/index.txt @@ -59,7 +59,7 @@ supported versions, and any notes for each of the supported database backends: Database Library Requirements Supported Versions Notes ================== ============================== ================== ========================================= PostgreSQL GEOS, GDAL, PROJ, PostGIS 9.6+ Requires PostGIS. -MySQL GEOS, GDAL 5.6.1+ :ref:`Limited functionality `. +MySQL GEOS, GDAL 5.7+ :ref:`Limited functionality `. Oracle GEOS, GDAL 12.2+ XE not supported. SQLite GEOS, GDAL, PROJ, SpatiaLite 3.8.3+ Requires SpatiaLite 4.3+ ================== ============================== ================== ========================================= diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index d788f2f6770..d129a195360 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -300,7 +300,7 @@ MySQL notes Version support --------------- -Django supports MySQL 5.6 and higher. +Django supports MySQL 5.7 and higher. Django's ``inspectdb`` feature uses the ``information_schema`` database, which contains detailed data on all database schemas. @@ -479,11 +479,11 @@ Several other `MySQLdb connection options`_ may be useful, such as ``ssl``, Setting ``sql_mode`` ~~~~~~~~~~~~~~~~~~~~ -From MySQL 5.7 onwards and on fresh installs of MySQL 5.6, the default value of -the ``sql_mode`` option contains ``STRICT_TRANS_TABLES``. That option escalates -warnings into errors when data are truncated upon insertion, so Django highly -recommends activating a `strict mode`_ for MySQL to prevent data loss (either -``STRICT_TRANS_TABLES`` or ``STRICT_ALL_TABLES``). +From MySQL 5.7 onwards, the default value of the ``sql_mode`` option contains +``STRICT_TRANS_TABLES``. That option escalates warnings into errors when data +are truncated upon insertion, so Django highly recommends activating a +`strict mode`_ for MySQL to prevent data loss (either ``STRICT_TRANS_TABLES`` +or ``STRICT_ALL_TABLES``). .. _strict mode: https://dev.mysql.com/doc/refman/en/sql-mode.html#sql-mode-strict @@ -605,9 +605,8 @@ specification without a key length". Fractional seconds support for Time and DateTime fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -MySQL 5.6.4 and later can store fractional seconds, provided that the -column definition includes a fractional indication (e.g. ``DATETIME(6)``). -Earlier versions do not support them at all. +MySQL can store fractional seconds, provided that the column definition +includes a fractional indication (e.g. ``DATETIME(6)``). Django will not upgrade existing columns to include fractional seconds if the database server supports it. If you want to enable them on an existing database, diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt index 1603f8f1b30..54ffbbfd273 100644 --- a/docs/releases/3.2.txt +++ b/docs/releases/3.2.txt @@ -243,6 +243,12 @@ Dropped support for PostgreSQL 9.5 Upstream support for PostgreSQL 9.5 ends in February 2021. Django 3.2 supports PostgreSQL 9.6 and higher. +Dropped support for MySQL 5.6 +----------------------------- + +The end of upstream support for MySQL 5.6 is April 2021. Django 3.2 supports +MySQL 5.7 and higher. + Miscellaneous ------------- diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py index 481924a2e4d..a7b0e6ee3d2 100644 --- a/tests/queries/test_explain.py +++ b/tests/queries/test_explain.py @@ -26,9 +26,6 @@ class ExplainTests(TestCase): for idx, queryset in enumerate(querysets): for format in all_formats: with self.subTest(format=format, queryset=idx): - if connection.vendor == 'mysql': - # This does a query and caches the result. - connection.features.needs_explain_extended with self.assertNumQueries(1), CaptureQueriesContext(connection) as captured_queries: result = queryset.explain(format=format) self.assertTrue(captured_queries[0]['sql'].startswith(connection.ops.explain_prefix)) @@ -73,7 +70,6 @@ class ExplainTests(TestCase): def test_mysql_text_to_traditional(self): # Ensure these cached properties are initialized to prevent queries for # the MariaDB or MySQL version during the QuerySet evaluation. - connection.features.needs_explain_extended connection.features.supported_explain_formats with CaptureQueriesContext(connection) as captured_queries: Tag.objects.filter(name='test').explain(format='text') @@ -99,21 +95,6 @@ class ExplainTests(TestCase): 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. - if not connection.features.needs_explain_extended: - raise unittest.SkipTest('MySQL < 5.7 specific') - qs = Tag.objects.filter(name='test') - with CaptureQueriesContext(connection) as captured_queries: - qs.explain(format='json') - self.assertEqual(len(captured_queries), 1) - self.assertNotIn('EXTENDED', captured_queries[0]['sql']) - with CaptureQueriesContext(connection) as captured_queries: - qs.explain(format='text') - self.assertEqual(len(captured_queries), 1) - self.assertNotIn('EXTENDED', captured_queries[0]['sql']) - @skipIfDBFeature('supports_explaining_query_execution') class ExplainUnsupportedTests(TestCase):