From eb3699ea775548a22e0407ad12bf8cbdeaf95ff5 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Fri, 8 Jul 2022 13:30:12 +0200 Subject: [PATCH] Fixed #33718 -- Dropped support for MySQL 5.7. --- .../contrib/gis/db/backends/mysql/features.py | 25 +-------- .../gis/db/backends/mysql/introspection.py | 8 +-- .../gis/db/backends/mysql/operations.py | 2 - .../contrib/gis/db/backends/mysql/schema.py | 5 +- django/db/backends/mysql/features.py | 56 +------------------ django/db/backends/mysql/operations.py | 8 +-- docs/ref/contrib/gis/db-api.txt | 16 +++--- docs/ref/contrib/gis/functions.txt | 6 +- docs/ref/contrib/gis/geoquerysets.txt | 4 +- docs/ref/contrib/gis/install/index.txt | 2 +- docs/ref/databases.txt | 11 ++-- docs/ref/models/fields.txt | 4 +- docs/releases/4.2.txt | 6 ++ tests/aggregation/tests.py | 6 +- tests/backends/mysql/tests.py | 4 +- tests/gis_tests/geoapp/tests.py | 5 +- 16 files changed, 42 insertions(+), 126 deletions(-) diff --git a/django/contrib/gis/db/backends/mysql/features.py b/django/contrib/gis/db/backends/mysql/features.py index 8999a38bf91..cd994203745 100644 --- a/django/contrib/gis/db/backends/mysql/features.py +++ b/django/contrib/gis/db/backends/mysql/features.py @@ -4,6 +4,7 @@ from django.utils.functional import cached_property class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures): + empty_intersection_returns_none = False has_spatialrefsys_table = False supports_add_srs_entry = False supports_distance_geodetic = False @@ -14,31 +15,7 @@ class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures): supports_num_points_poly = False unsupported_geojson_options = {"crs"} - @cached_property - def empty_intersection_returns_none(self): - return ( - not self.connection.mysql_is_mariadb - and self.connection.mysql_version < (5, 7, 5) - ) - @cached_property def supports_geometry_field_unique_index(self): # Not supported in MySQL since https://dev.mysql.com/worklog/task/?id=11808 return self.connection.mysql_is_mariadb - - @cached_property - def django_test_skips(self): - skips = super().django_test_skips - if not self.connection.mysql_is_mariadb and self.connection.mysql_version < ( - 8, - 0, - 0, - ): - skips.update( - { - "MySQL < 8 gives different results.": { - "gis_tests.geoapp.tests.GeoLookupTest.test_disjoint_lookup", - }, - } - ) - return skips diff --git a/django/contrib/gis/db/backends/mysql/introspection.py b/django/contrib/gis/db/backends/mysql/introspection.py index 7497ad520c7..4d6aea78a29 100644 --- a/django/contrib/gis/db/backends/mysql/introspection.py +++ b/django/contrib/gis/db/backends/mysql/introspection.py @@ -28,10 +28,6 @@ class MySQLIntrospection(DatabaseIntrospection): return field_type, field_params def supports_spatial_index(self, cursor, table_name): - # Supported with MyISAM/Aria, or InnoDB on MySQL 5.7.5+/MariaDB. + # Supported with MyISAM, Aria, or InnoDB. storage_engine = self.get_storage_engine(cursor, table_name) - if storage_engine == "InnoDB": - if self.connection.mysql_is_mariadb: - return True - return self.connection.mysql_version >= (5, 7, 5) - return storage_engine in ("MyISAM", "Aria") + return storage_engine in ("MyISAM", "Aria", "InnoDB") diff --git a/django/contrib/gis/db/backends/mysql/operations.py b/django/contrib/gis/db/backends/mysql/operations.py index 4ef014b36b3..6d048745376 100644 --- a/django/contrib/gis/db/backends/mysql/operations.py +++ b/django/contrib/gis/db/backends/mysql/operations.py @@ -86,8 +86,6 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): if self.connection.mysql_is_mariadb: unsupported.remove("PointOnSurface") unsupported.update({"GeoHash", "IsValid"}) - elif self.connection.mysql_version < (5, 7, 5): - unsupported.update({"AsGeoJSON", "GeoHash", "IsValid"}) return unsupported def geo_db_type(self, f): diff --git a/django/contrib/gis/db/backends/mysql/schema.py b/django/contrib/gis/db/backends/mysql/schema.py index b173df01980..1c9fe0e5658 100644 --- a/django/contrib/gis/db/backends/mysql/schema.py +++ b/django/contrib/gis/db/backends/mysql/schema.py @@ -81,8 +81,7 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor): self.execute(sql) except OperationalError: logger.error( - "Cannot create SPATIAL INDEX %s. Only MyISAM and (as of " - "MySQL 5.7.5) InnoDB support them.", - sql, + f"Cannot create SPATIAL INDEX {sql}. Only MyISAM, Aria, and InnoDB " + f"support them.", ) self.geometry_sql = [] diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index b4546186465..db04d42e240 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -61,15 +61,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): if self.connection.mysql_is_mariadb: return (10, 4) else: - return (5, 7) - - @cached_property - def bare_select_suffix(self): - if not self.connection.mysql_is_mariadb and self.connection.mysql_version < ( - 8, - ): - return " FROM DUAL" - return "" + return (8,) @cached_property def test_collations(self): @@ -128,27 +120,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): }, } ) - if not self.connection.mysql_is_mariadb and self.connection.mysql_version < ( - 8, - ): - skips.update( - { - "Casting to datetime/time is not supported by MySQL < 8.0. " - "(#30224)": { - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_time_from_python", - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_datetime_from_python", - }, - "MySQL < 8.0 returns string type instead of datetime/time. " - "(#30224)": { - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_time_from_database", - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_datetime_from_database", - }, - } - ) if self.connection.mysql_is_mariadb and ( 10, 4, @@ -175,21 +146,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): }, } ) - if not self.connection.mysql_is_mariadb and self.connection.mysql_version < ( - 8, - ): - skips.update( - { - "Parenthesized combined queries are not supported on MySQL < 8.": { - "queries.test_qs_combinators.QuerySetSetOperationTests." - "test_union_in_subquery", - "queries.test_qs_combinators.QuerySetSetOperationTests." - "test_union_in_subquery_related_outerref", - "queries.test_qs_combinators.QuerySetSetOperationTests." - "test_union_in_with_ordering", - } - } - ) if not self.supports_explain_analyze: skips.update( { @@ -341,17 +297,11 @@ class DatabaseFeatures(BaseDatabaseFeatures): # To be added in https://jira.mariadb.org/browse/MDEV-12981. return not self.connection.mysql_is_mariadb - @cached_property - def supports_json_field(self): - if self.connection.mysql_is_mariadb: - return True - return self.connection.mysql_version >= (5, 7, 8) - @cached_property def can_introspect_json_field(self): if self.connection.mysql_is_mariadb: - return self.supports_json_field and self.can_introspect_check_constraints - return self.supports_json_field + return self.can_introspect_check_constraints + return True @cached_property def supports_index_column_ordering(self): diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 34cdfc02925..6c0c6ebd2b1 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -389,12 +389,8 @@ class DatabaseOperations(BaseDatabaseOperations): 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.x or in MariaDB. - if ( - self.connection.mysql_version < (8, 0, 0) - or self.connection.mysql_is_mariadb - ): + # REGEXP_LIKE doesn't exist in MariaDB. + if self.connection.mysql_is_mariadb: if lookup_type == "regex": return "%s REGEXP BINARY %s" return "%s REGEXP %s" diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt index 4cc9ceafd2a..bc216c7cafc 100644 --- a/docs/ref/contrib/gis/db-api.txt +++ b/docs/ref/contrib/gis/db-api.txt @@ -22,11 +22,9 @@ GeoDjango currently provides the following spatial database backends: MySQL Spatial Limitations ------------------------- -Before MySQL 5.6.1, spatial extensions only support bounding box operations -(what MySQL calls minimum bounding rectangles, or MBR). Specifically, MySQL did -not conform to the OGC standard. Django supports spatial functions operating on -real geometries available in modern MySQL versions. However, the spatial -functions are not as rich as other backends like PostGIS. +Django supports spatial functions operating on real geometries available in +modern MySQL versions. However, the spatial functions are not as rich as other +backends like PostGIS. Raster Support -------------- @@ -318,7 +316,7 @@ Lookup Type PostGIS Oracle MariaDB MySQL [#]_ Sp :lookup:`equals` X X X X X C :lookup:`exact ` X X X X X B :lookup:`intersects` X X X X X B -:lookup:`isvalid` X X X (≥ 5.7.5) X +:lookup:`isvalid` X X X X :lookup:`overlaps` X X X X X B :lookup:`relate` X X X X C :lookup:`same_as` X X X X X B @@ -348,7 +346,7 @@ functions are available on each spatial backend. Function PostGIS Oracle MariaDB MySQL SpatiaLite ==================================== ======= ============== ============ =========== ================= :class:`Area` X X X X X -:class:`AsGeoJSON` X X X X (≥ 5.7.5) X +:class:`AsGeoJSON` X X X X X :class:`AsGML` X X X :class:`AsKML` X X :class:`AsSVG` X X @@ -361,9 +359,9 @@ Function PostGIS Oracle MariaDB MySQL :class:`Distance` X X X X X :class:`Envelope` X X X X X :class:`ForcePolygonCW` X X -:class:`GeoHash` X X (≥ 5.7.5) X (LWGEOM/RTTOPO) +:class:`GeoHash` X X X (LWGEOM/RTTOPO) :class:`Intersection` X X X X X -:class:`IsValid` X X X (≥ 5.7.5) X +:class:`IsValid` X X X X :class:`Length` X X X X X :class:`LineLocatePoint` X X :class:`MakeValid` X X (LWGEOM/RTTOPO) diff --git a/docs/ref/contrib/gis/functions.txt b/docs/ref/contrib/gis/functions.txt index 44cde315e22..00c48e665e8 100644 --- a/docs/ref/contrib/gis/functions.txt +++ b/docs/ref/contrib/gis/functions.txt @@ -53,7 +53,7 @@ geographic SRSes. .. class:: AsGeoJSON(expression, bbox=False, crs=False, precision=8, **extra) *Availability*: MariaDB, `MySQL -`__ (≥ 5.7.5), +`__, Oracle, `PostGIS `__, SpatiaLite Accepts a single geographic field or expression and returns a `GeoJSON @@ -333,7 +333,7 @@ are returned unchanged. .. class:: GeoHash(expression, precision=None, **extra) *Availability*: `MySQL -`__ (≥ 5.7.5), +`__, `PostGIS `__, SpatiaLite (LWGEOM/RTTOPO) @@ -374,7 +374,7 @@ intersection between them. .. class:: IsValid(expr) *Availability*: `MySQL -`__ (≥ 5.7.5), +`__, `PostGIS `__, Oracle, SpatiaLite Accepts a geographic field or expression and tests if the value is well formed. diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt index dac663e3b80..760d508b172 100644 --- a/docs/ref/contrib/gis/geoquerysets.txt +++ b/docs/ref/contrib/gis/geoquerysets.txt @@ -351,8 +351,8 @@ SpatiaLite ``Intersects(poly, geom)`` ``isvalid`` ----------- -*Availability*: MySQL (≥ 5.7.5), `PostGIS -`__, Oracle, SpatiaLite +*Availability*: MySQL, `PostGIS `__, +Oracle, SpatiaLite Tests if the geometry is valid. diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index 2277b7c5ee1..629a070bb87 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 12+ Requires PostGIS. -MySQL GEOS, GDAL 5.7+ :ref:`Limited functionality `. +MySQL GEOS, GDAL 8+ :ref:`Limited functionality `. Oracle GEOS, GDAL 19+ XE not supported. SQLite GEOS, GDAL, PROJ, SpatiaLite 3.9.0+ Requires SpatiaLite 4.3+ ================== ============================== ================== ========================================= diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 05ae78b8763..442b1a15a3c 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -352,7 +352,7 @@ MySQL notes Version support --------------- -Django supports MySQL 5.7 and higher. +Django supports MySQL 8 and higher. Django's ``inspectdb`` feature uses the ``information_schema`` database, which contains detailed data on all database schemas. @@ -535,11 +535,10 @@ Several other `MySQLdb connection options`_ may be useful, such as ``ssl``, Setting ``sql_mode`` ~~~~~~~~~~~~~~~~~~~~ -From MySQL 5.7 onward, 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``). +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 diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index da65c627665..384c5e50bf2 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1200,8 +1200,8 @@ A field for storing JSON encoded data. In Python the data is represented in its Python native format: dictionaries, lists, strings, numbers, booleans and ``None``. -``JSONField`` is supported on MariaDB, MySQL 5.7.8+, Oracle, PostgreSQL, and -SQLite (with the :ref:`JSON1 extension enabled `). +``JSONField`` is supported on MariaDB, MySQL, Oracle, PostgreSQL, and SQLite +(with the :ref:`JSON1 extension enabled `). .. attribute:: JSONField.encoder diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt index 6282bfa580a..a8de6269939 100644 --- a/docs/releases/4.2.txt +++ b/docs/releases/4.2.txt @@ -249,6 +249,12 @@ Dropped support for MariaDB 10.3 Upstream support for MariaDB 10.3 ends in May 2023. Django 4.2 supports MariaDB 10.4 and higher. +Dropped support for MySQL 5.7 +----------------------------- + +Upstream support for MySQL 5.7 ends in October 2023. Django 4.2 supports MySQL +8 and higher. + Dropped support for PostgreSQL 11 --------------------------------- diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index c6d73e80429..75a4e0d0222 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -1837,7 +1837,7 @@ class AggregateTestCase(TestCase): default=datetime.time(17), ) if connection.vendor == "mysql": - # Workaround for #30224 for MySQL 8.0+ & MariaDB. + # Workaround for #30224 for MySQL & MariaDB. expr.default = Cast(expr.default, TimeField()) queryset = Book.objects.annotate(oldest_store_opening=expr).order_by("isbn") self.assertSequenceEqual( @@ -1887,7 +1887,7 @@ class AggregateTestCase(TestCase): def test_aggregation_default_using_date_from_python(self): expr = Min("book__pubdate", default=datetime.date(1970, 1, 1)) if connection.vendor == "mysql": - # Workaround for #30224 for MySQL 5.7+ & MariaDB. + # Workaround for #30224 for MySQL & MariaDB. expr.default = Cast(expr.default, DateField()) queryset = Publisher.objects.annotate(earliest_pubdate=expr).order_by("name") self.assertSequenceEqual( @@ -1938,7 +1938,7 @@ class AggregateTestCase(TestCase): default=datetime.datetime(1970, 1, 1), ) if connection.vendor == "mysql": - # Workaround for #30224 for MySQL 8.0+ & MariaDB. + # Workaround for #30224 for MySQL & MariaDB. expr.default = Cast(expr.default, DateTimeField()) queryset = Book.objects.annotate(oldest_store_opening=expr).order_by("isbn") self.assertSequenceEqual( diff --git a/tests/backends/mysql/tests.py b/tests/backends/mysql/tests.py index d02f465cbf1..da6c53de48a 100644 --- a/tests/backends/mysql/tests.py +++ b/tests/backends/mysql/tests.py @@ -110,8 +110,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, 6) - msg = "MySQL 5.7 or later is required (found 5.6)." + mocked_get_database_version.return_value = (5, 7) + msg = "MySQL 8 or later is required (found 5.7)." with self.assertRaisesMessage(NotSupportedError, msg): connection.check_database_version_supported() diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index fd5fc3c7930..8a86d4a2be1 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -345,12 +345,9 @@ class GeoLookupTest(TestCase): invalid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))") State.objects.create(name="invalid", poly=invalid_geom) qs = State.objects.all() - if connection.ops.oracle or ( - connection.ops.mysql and connection.mysql_version < (8, 0, 0) - ): + if connection.ops.oracle: # Kansas has adjacent vertices with distance 6.99244813842e-12 # which is smaller than the default Oracle tolerance. - # It's invalid on MySQL < 8 also. qs = qs.exclude(name="Kansas") self.assertEqual( State.objects.filter(name="Kansas", poly__isvalid=False).count(), 1