From b719688b21febd9a04c8623126558cd06cc27b80 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 3 Aug 2023 18:54:29 +0200 Subject: [PATCH] Fixed #34761 -- Dropped support for MySQL < 8.0.11. --- django/db/backends/mysql/features.py | 30 ++++++-------------------- django/db/backends/mysql/schema.py | 14 ++++++------ docs/ref/contrib/gis/install/index.txt | 2 +- docs/ref/databases.txt | 14 ++++++------ docs/ref/models/expressions.txt | 2 +- docs/ref/models/indexes.txt | 6 +++--- docs/ref/models/querysets.txt | 4 ++-- docs/releases/5.0.txt | 6 ++++++ tests/backends/mysql/test_features.py | 14 ------------ tests/backends/mysql/tests.py | 4 ++-- 10 files changed, 35 insertions(+), 61 deletions(-) diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index 7f733254ee..5af80fcb3c 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -11,6 +11,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): # MySQL doesn't support sliced subqueries with IN/ALL/ANY/SOME. allow_sliced_subqueries_with_in = False has_select_for_update = True + has_select_for_update_nowait = True supports_forward_references = False supports_regex_backreferencing = False supports_date_lookup_using_string = False @@ -23,6 +24,8 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_temporal_subtraction = True supports_slicing_ordering_in_compound = True supports_index_on_text_field = False + supports_over_clause = True + supports_frame_range_fixed_distance = True supports_update_conflicts = True delete_can_self_reference_subquery = False create_test_procedure_without_params_sql = """ @@ -62,7 +65,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): if self.connection.mysql_is_mariadb: return (10, 4) else: - return (8,) + return (8, 0, 11) @cached_property def test_collations(self): @@ -225,16 +228,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): def is_sql_auto_is_null_enabled(self): return self.connection.mysql_server_data["sql_auto_is_null"] - @cached_property - def supports_over_clause(self): - if self.connection.mysql_is_mariadb: - return True - return self.connection.mysql_version >= (8, 0, 2) - - supports_frame_range_fixed_distance = property( - operator.attrgetter("supports_over_clause") - ) - @cached_property def supports_column_check_constraints(self): if self.connection.mysql_is_mariadb: @@ -255,20 +248,11 @@ class DatabaseFeatures(BaseDatabaseFeatures): def has_select_for_update_skip_locked(self): if self.connection.mysql_is_mariadb: return self.connection.mysql_version >= (10, 6) - return self.connection.mysql_version >= (8, 0, 1) - - @cached_property - def has_select_for_update_nowait(self): - if self.connection.mysql_is_mariadb: - return True - return self.connection.mysql_version >= (8, 0, 1) + return True @cached_property def has_select_for_update_of(self): - return ( - not self.connection.mysql_is_mariadb - and self.connection.mysql_version >= (8, 0, 1) - ) + return not self.connection.mysql_is_mariadb @cached_property def supports_explain_analyze(self): @@ -319,7 +303,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): return False if self.connection.mysql_is_mariadb: return self.connection.mysql_version >= (10, 8) - return self.connection.mysql_version >= (8, 0, 1) + return True @cached_property def supports_expression_indexes(self): diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py index bfe5a2e805..905a26bfba 100644 --- a/django/db/backends/mysql/schema.py +++ b/django/db/backends/mysql/schema.py @@ -45,14 +45,12 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @property def sql_rename_column(self): - # MariaDB >= 10.5.2 and MySQL >= 8.0.4 support an - # "ALTER TABLE ... RENAME COLUMN" statement. - if self.connection.mysql_is_mariadb: - if self.connection.mysql_version >= (10, 5, 2): - return super().sql_rename_column - elif self.connection.mysql_version >= (8, 0, 4): - return super().sql_rename_column - return "ALTER TABLE %(table)s CHANGE %(old_column)s %(new_column)s %(type)s" + is_mariadb = self.connection.mysql_is_mariadb + if is_mariadb and self.connection.mysql_version < (10, 5, 2): + # MariaDB < 10.5.2 doesn't support an + # "ALTER TABLE ... RENAME COLUMN" statement. + return "ALTER TABLE %(table)s CHANGE %(old_column)s %(new_column)s %(type)s" + return super().sql_rename_column def quote_value(self, value): self.connection.ensure_connection() diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index 52cfdcdeb5..b207094dbb 100644 --- a/docs/ref/contrib/gis/install/index.txt +++ b/docs/ref/contrib/gis/install/index.txt @@ -57,7 +57,7 @@ supported versions, and any notes for each of the supported database backends: Database Library Requirements Supported Versions Notes ================== ============================== ================== ========================================= PostgreSQL GEOS, GDAL, PROJ, PostGIS 12+ Requires PostGIS. -MySQL GEOS, GDAL 8+ :ref:`Limited functionality `. +MySQL GEOS, GDAL 8.0.11+ :ref:`Limited functionality `. Oracle GEOS, GDAL 19+ XE not supported. SQLite GEOS, GDAL, PROJ, SpatiaLite 3.21.0+ Requires SpatiaLite 4.3+ ================== ============================== ================== ========================================= diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 0d0409180c..aa55446607 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -412,7 +412,7 @@ MySQL notes Version support --------------- -Django supports MySQL 8 and higher. +Django supports MySQL 8.0.11 and higher. Django's ``inspectdb`` feature uses the ``information_schema`` database, which contains detailed data on all database schemas. @@ -755,14 +755,14 @@ MySQL and MariaDB do not support some options to the ``SELECT ... FOR UPDATE`` statement. If ``select_for_update()`` is used with an unsupported option, then a :exc:`~django.db.NotSupportedError` is raised. -=============== ========= ========== +=============== ========= ===== Option MariaDB MySQL -=============== ========= ========== -``SKIP LOCKED`` X (≥10.6) X (≥8.0.1) -``NOWAIT`` X X (≥8.0.1) -``OF`` X (≥8.0.1) +=============== ========= ===== +``SKIP LOCKED`` X (≥10.6) X +``NOWAIT`` X X +``OF`` X ``NO KEY`` -=============== ========= ========== +=============== ========= ===== When using ``select_for_update()`` on MySQL, make sure you filter a queryset against at least a set of fields contained in unique constraints or only diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt index c72cf5a853..abae25f09c 100644 --- a/docs/ref/models/expressions.txt +++ b/docs/ref/models/expressions.txt @@ -880,7 +880,7 @@ from groups to be included: Support for filtering against window functions was added. -Among Django's built-in database backends, MySQL 8.0.2+, PostgreSQL, and Oracle +Among Django's built-in database backends, MySQL, PostgreSQL, and Oracle support window expressions. Support for different window expression features varies among the different databases. For example, the options in :meth:`~django.db.models.Expression.asc` and diff --git a/docs/ref/models/indexes.txt b/docs/ref/models/indexes.txt index d23a5fd1ce..6046abf029 100644 --- a/docs/ref/models/indexes.txt +++ b/docs/ref/models/indexes.txt @@ -82,10 +82,10 @@ field's name. For example ``Index(fields=['headline', '-pub_date'])`` would create SQL with ``(headline, pub_date DESC)``. -.. admonition:: MySQL and MariaDB +.. admonition:: MariaDB - Index ordering isn't supported on MySQL < 8.0.1 and MariaDB < 10.8. In that - case, a descending index is created as a normal index. + Index ordering isn't supported on MariaDB < 10.8. In that case, a + descending index is created as a normal index. ``name`` -------- diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 8a9733786a..1c73a7f085 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1928,8 +1928,8 @@ them: The ``postgresql``, ``oracle``, and ``mysql`` database backends support ``select_for_update()``. However, MariaDB only supports the ``nowait`` argument, MariaDB 10.6+ also supports the ``skip_locked`` argument, and MySQL -8.0.1+ supports the ``nowait``, ``skip_locked``, and ``of`` arguments. The -``no_key`` argument is only supported on PostgreSQL. +supports the ``nowait``, ``skip_locked``, and ``of`` arguments. The ``no_key`` +argument is only supported on PostgreSQL. Passing ``nowait=True``, ``skip_locked=True``, ``no_key=True``, or ``of`` to ``select_for_update()`` using database backends that do not support these diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index e3cba0c02b..5a758f8791 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -463,6 +463,12 @@ backends. ``False`` if the database doesn't support the ``DEFAULT`` keyword in bulk ``INSERT`` queries. +Dropped support for MySQL < 8.0.11 +---------------------------------- + +Support for pre-releases of MySQL 8.0.x series is removed. Django 5.0 supports +MySQL 8.0.11 and higher. + :mod:`django.contrib.gis` ------------------------- diff --git a/tests/backends/mysql/test_features.py b/tests/backends/mysql/test_features.py index 88e267f048..96e1ed3b49 100644 --- a/tests/backends/mysql/test_features.py +++ b/tests/backends/mysql/test_features.py @@ -23,20 +23,6 @@ class TestFeatures(TestCase): self.assertFalse(connection.features.supports_transactions) del connection.features.supports_transactions - 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) - def test_allows_auto_pk_0(self): with mock.MagicMock() as _connection: _connection.sql_mode = {"NO_AUTO_VALUE_ON_ZERO"} diff --git a/tests/backends/mysql/tests.py b/tests/backends/mysql/tests.py index b80294abca..d652d179ba 100644 --- a/tests/backends/mysql/tests.py +++ b/tests/backends/mysql/tests.py @@ -109,8 +109,8 @@ class Tests(TestCase): mocked_get_database_version.return_value = (10, 3) msg = "MariaDB 10.4 or later is required (found 10.3)." else: - mocked_get_database_version.return_value = (5, 7) - msg = "MySQL 8 or later is required (found 5.7)." + mocked_get_database_version.return_value = (8, 0, 4) + msg = "MySQL 8.0.11 or later is required (found 8.0.4)." with self.assertRaisesMessage(NotSupportedError, msg): connection.check_database_version_supported()