From 8a1768432b1ec3ecfa390ac5eb70dbfb0cff59b3 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 24 Aug 2017 14:56:09 -0400 Subject: [PATCH] Fixed #28552 -- Dropped support for MySQL 5.5. --- .../gis/db/backends/mysql/operations.py | 15 +------- django/db/backends/mysql/base.py | 13 ++----- django/db/backends/mysql/features.py | 4 --- django/db/backends/mysql/operations.py | 26 ++++---------- docs/ref/contrib/gis/db-api.txt | 10 +++--- docs/ref/contrib/gis/functions.txt | 14 ++++---- docs/ref/contrib/gis/install/index.txt | 2 +- docs/ref/databases.txt | 34 +++++-------------- docs/releases/2.1.txt | 6 ++++ tests/schema/tests.py | 2 -- 10 files changed, 38 insertions(+), 88 deletions(-) diff --git a/django/contrib/gis/db/backends/mysql/operations.py b/django/contrib/gis/db/backends/mysql/operations.py index 12f9eff955..f336ea1fb7 100644 --- a/django/contrib/gis/db/backends/mysql/operations.py +++ b/django/contrib/gis/db/backends/mysql/operations.py @@ -15,17 +15,10 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): mysql = True name = 'mysql' + geom_func_prefix = 'ST_' Adapter = WKTAdapter - @cached_property - def geom_func_prefix(self): - return '' if self.is_mysql_5_5 else 'ST_' - - @cached_property - def is_mysql_5_5(self): - return self.connection.mysql_version < (5, 6, 1) - @cached_property def is_mysql_5_6(self): return self.connection.mysql_version < (5, 7, 6) @@ -56,10 +49,6 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): 'within': SpatialOperator(func='MBRWithin'), } - @cached_property - def function_names(self): - return {'Length': 'GLength'} if self.is_mysql_5_5 else {} - disallowed_aggregates = ( aggregates.Collect, aggregates.Extent, aggregates.Extent3D, aggregates.MakeLine, aggregates.Union, @@ -75,8 +64,6 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): } if self.connection.mysql_version < (5, 7, 5): unsupported.update({'AsGeoJSON', 'GeoHash', 'IsValid'}) - if self.is_mysql_5_5: - unsupported.update({'Difference', 'Distance', 'Intersection', 'SymDifference', 'Union'}) return unsupported def geo_db_type(self, f): diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 0e5c25fc23..1eb3677b80 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -97,14 +97,14 @@ class DatabaseWrapper(BaseDatabaseWrapper): # types, as strings. Column-type strings can contain format strings; they'll # be interpolated against the values of Field.__dict__ before being output. # If a column type is set to None, it won't be included in the output. - _data_types = { + data_types = { 'AutoField': 'integer AUTO_INCREMENT', 'BigAutoField': 'bigint AUTO_INCREMENT', 'BinaryField': 'longblob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'DateField': 'date', - 'DateTimeField': 'datetime', + 'DateTimeField': 'datetime(6)', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'DurationField': 'bigint', 'FileField': 'varchar(%(max_length)s)', @@ -121,17 +121,10 @@ class DatabaseWrapper(BaseDatabaseWrapper): 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', 'TextField': 'longtext', - 'TimeField': 'time', + 'TimeField': 'time(6)', 'UUIDField': 'char(32)', } - @cached_property - def data_types(self): - if self.features.supports_microsecond_precision: - return dict(self._data_types, DateTimeField='datetime(6)', TimeField='time(6)') - else: - return self._data_types - # For these columns, MySQL doesn't: # - accept default values and implicitly treats these columns as nullable # - support a database index diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index 18ab088941..542b621aa9 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -60,10 +60,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): "Confirm support for introspected foreign keys" return self._mysql_storage_engine != 'MyISAM' - @cached_property - def supports_microsecond_precision(self): - return self.connection.mysql_version >= (5, 6, 4) - @cached_property def has_zoneinfo_database(self): # Test if the time zone definitions are installed. diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index e4492f866f..474cd8d467 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -109,10 +109,7 @@ class DatabaseOperations(BaseDatabaseOperations): return "INTERVAL '%06f' SECOND_MICROSECOND" % timedelta.total_seconds() def format_for_duration_arithmetic(self, sql): - if self.connection.features.supports_microsecond_precision: - return 'INTERVAL %s MICROSECOND' % sql - else: - return 'INTERVAL FLOOR(%s / 1000000) SECOND' % sql + return 'INTERVAL %s MICROSECOND' % sql def force_no_ordering(self): """ @@ -178,10 +175,6 @@ class DatabaseOperations(BaseDatabaseOperations): value = timezone.make_naive(value, self.connection.timezone) else: raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.") - - if not self.connection.features.supports_microsecond_precision: - value = value.replace(microsecond=0) - return str(value) def adapt_timefield_value(self, value): @@ -258,17 +251,10 @@ class DatabaseOperations(BaseDatabaseOperations): def subtract_temporals(self, internal_type, lhs, rhs): lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs - if self.connection.features.supports_microsecond_precision: - if internal_type == 'TimeField': - return ( - "((TIME_TO_SEC(%(lhs)s) * POW(10, 6) + MICROSECOND(%(lhs)s)) -" - " (TIME_TO_SEC(%(rhs)s) * POW(10, 6) + MICROSECOND(%(rhs)s)))" - ) % {'lhs': lhs_sql, 'rhs': rhs_sql}, lhs_params * 2 + rhs_params * 2 - else: - return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), rhs_params + lhs_params - elif internal_type == 'TimeField': + if internal_type == 'TimeField': return ( - "(TIME_TO_SEC(%s) * POW(10, 6) - TIME_TO_SEC(%s) * POW(10, 6))" - ) % (lhs_sql, rhs_sql), lhs_params + rhs_params + "((TIME_TO_SEC(%(lhs)s) * POW(10, 6) + MICROSECOND(%(lhs)s)) -" + " (TIME_TO_SEC(%(rhs)s) * POW(10, 6) + MICROSECOND(%(rhs)s)))" + ) % {'lhs': lhs_sql, 'rhs': rhs_sql}, lhs_params * 2 + rhs_params * 2 else: - return "(TIMESTAMPDIFF(SECOND, %s, %s) * POW(10, 6))" % (rhs_sql, lhs_sql), rhs_params + lhs_params + return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), rhs_params + lhs_params diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt index cce38a3711..453eaef966 100644 --- a/docs/ref/contrib/gis/db-api.txt +++ b/docs/ref/contrib/gis/db-api.txt @@ -379,12 +379,12 @@ Function PostGIS Oracle MySQL Spat :class:`Azimuth` X X (LWGEOM) :class:`BoundingCircle` X X :class:`Centroid` X X X X -:class:`Difference` X X X (≥ 5.6.1) X -:class:`Distance` X X X (≥ 5.6.1) X +:class:`Difference` X X X X +:class:`Distance` X X X X :class:`Envelope` X X X :class:`ForceRHR` X :class:`GeoHash` X X (≥ 5.7.5) X (LWGEOM) -:class:`Intersection` X X X (≥ 5.6.1) X +:class:`Intersection` X X X X :class:`IsValid` X X X (≥ 5.7.5) X (LWGEOM) :class:`Length` X X X X :class:`LineLocatePoint` X X @@ -397,10 +397,10 @@ Function PostGIS Oracle MySQL Spat :class:`Reverse` X X X :class:`Scale` X X :class:`SnapToGrid` X X -:class:`SymDifference` X X X (≥ 5.6.1) X +:class:`SymDifference` X X X X :class:`Transform` X X X :class:`Translate` X X -:class:`Union` X X X (≥ 5.6.1) X +:class:`Union` X X X X ==================================== ======= ============== =========== ========== Aggregate Functions diff --git a/docs/ref/contrib/gis/functions.txt b/docs/ref/contrib/gis/functions.txt index bde36d443d..66099c3317 100644 --- a/docs/ref/contrib/gis/functions.txt +++ b/docs/ref/contrib/gis/functions.txt @@ -208,7 +208,7 @@ value of the geometry. .. class:: Difference(expr1, expr2, **extra) -*Availability*: MySQL (≥ 5.6.1), `PostGIS +*Availability*: MySQL, `PostGIS `__, Oracle, SpatiaLite Accepts two geographic fields or expressions and returns the geometric @@ -220,8 +220,8 @@ geometry B. .. class:: Distance(expr1, expr2, spheroid=None, **extra) -*Availability*: MySQL (≥ 5.6.1), `PostGIS -`__, Oracle, SpatiaLite +*Availability*: MySQL, `PostGIS `__, +Oracle, SpatiaLite Accepts two geographic fields or expressions and returns the distance between them, as a :class:`~django.contrib.gis.measure.Distance` object. On MySQL, a raw @@ -307,7 +307,7 @@ __ https://en.wikipedia.org/wiki/Geohash .. class:: Intersection(expr1, expr2, **extra) -*Availability*: MySQL (≥ 5.6.1), `PostGIS +*Availability*: MySQL, `PostGIS `__, Oracle, SpatiaLite Accepts two geographic fields or expressions and returns the geometric @@ -480,7 +480,7 @@ Number of Arguments Description .. class:: SymDifference(expr1, expr2, **extra) -*Availability*: MySQL (≥ 5.6.1), `PostGIS +*Availability*: MySQL, `PostGIS `__, Oracle, SpatiaLite Accepts two geographic fields or expressions and returns the geometric @@ -522,8 +522,8 @@ parameters. .. class:: Union(expr1, expr2, **extra) -*Availability*: MySQL (≥ 5.6.1), `PostGIS -`__, Oracle, SpatiaLite +*Availability*: MySQL, `PostGIS `__, +Oracle, SpatiaLite Accepts two geographic fields or expressions and returns the union of both geometries. diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index acfbaa68a4..c547c9b268 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.4, PostGIS 9.3+ Requires PostGIS. -MySQL GEOS, GDAL 5.5+ Not OGC-compliant; :ref:`limited functionality `. +MySQL GEOS, GDAL 5.6+ Not OGC-compliant; :ref:`limited functionality `. Oracle GEOS, GDAL 12.1+ XE not supported. SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 4.0+ ================== ============================== ================== ========================================= diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 821d6e8d1b..e7d474a81d 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -276,7 +276,7 @@ MySQL notes Version support --------------- -Django supports MySQL 5.5 and higher. +Django supports MySQL 5.6 and higher. Django's ``inspectdb`` feature uses the ``information_schema`` database, which contains detailed data on all database schemas. @@ -294,36 +294,20 @@ Storage engines MySQL has several `storage engines`_. You can change the default storage engine in the server configuration. -Until MySQL 5.5.4, the default engine was MyISAM_ [#]_. The main drawbacks of -MyISAM are that it doesn't support transactions or enforce foreign-key -constraints. On the plus side, it was the only engine that supported full-text -indexing and searching until MySQL 5.6.4. +MySQL's default storage engine is InnoDB_. This engine is fully transactional +and supports foreign key references. It's the recommended choice. However, the +InnoDB autoincrement counter is lost on a MySQL restart because it does not +remember the ``AUTO_INCREMENT`` value, instead recreating it as "max(id)+1". +This may result in an inadvertent reuse of :class:`~django.db.models.AutoField` +values. -Since MySQL 5.5.5, the default storage engine is InnoDB_. This engine is fully -transactional and supports foreign key references. It's probably the best -choice at this point. However, note that the InnoDB autoincrement counter -is lost on a MySQL restart because it does not remember the -``AUTO_INCREMENT`` value, instead recreating it as "max(id)+1". This may -result in an inadvertent reuse of :class:`~django.db.models.AutoField` values. - -If you upgrade an existing project to MySQL 5.5.5 and subsequently add some -tables, ensure that your tables are using the same storage engine (i.e. MyISAM -vs. InnoDB). Specifically, if tables that have a ``ForeignKey`` between them -use different storage engines, you may see an error like the following when -running ``migrate``:: - - _mysql_exceptions.OperationalError: ( - 1005, "Can't create table '\\db_name\\.#sql-4a8_ab' (errno: 150)" - ) +The main drawbacks of MyISAM_ are that it doesn't support transactions or +enforce foreign-key constraints. .. _storage engines: https://dev.mysql.com/doc/refman/en/storage-engines.html .. _MyISAM: https://dev.mysql.com/doc/refman/en/myisam-storage-engine.html .. _InnoDB: https://dev.mysql.com/doc/refman/en/innodb-storage-engine.html -.. [#] Unless this was changed by the packager of your MySQL package. We've - had reports that the Windows Community Server installer sets up InnoDB as - the default storage engine, for example. - .. _mysql-db-api-drivers: MySQL DB API Drivers diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt index 55dfb09ea9..ead4d2fde3 100644 --- a/docs/releases/2.1.txt +++ b/docs/releases/2.1.txt @@ -200,6 +200,12 @@ Database backend API * ... +Dropped support for MySQL 5.5 +----------------------------- + +The end of upstream support for MySQL 5.5 is December 2018. Django 2.1 supports +MySQL 5.6 and higher. + Miscellaneous ------------- diff --git a/tests/schema/tests.py b/tests/schema/tests.py index e7f47865d4..f2a96294cc 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -2302,8 +2302,6 @@ class SchemaTests(TransactionTestCase): Changing the primary key field name of a model with a self-referential foreign key (#26384). """ - if connection.vendor == 'mysql' and connection.mysql_version < (5, 6, 6): - self.skipTest('Skip known bug renaming primary keys on older MySQL versions (#24995).') with connection.schema_editor() as editor: editor.create_model(Node) old_field = Node._meta.get_field('node_id')