summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2017-08-24 14:56:09 -0400
committerTim Graham <timograham@gmail.com>2017-09-25 14:48:11 -0400
commit8a1768432b1ec3ecfa390ac5eb70dbfb0cff59b3 (patch)
tree47c842a0a041a0865de859cf7d333cef3250a897
parent6da140724dba546d2f3aced1308e617747b0385c (diff)
Fixed #28552 -- Dropped support for MySQL 5.5.
-rw-r--r--django/contrib/gis/db/backends/mysql/operations.py15
-rw-r--r--django/db/backends/mysql/base.py13
-rw-r--r--django/db/backends/mysql/features.py4
-rw-r--r--django/db/backends/mysql/operations.py26
-rw-r--r--docs/ref/contrib/gis/db-api.txt10
-rw-r--r--docs/ref/contrib/gis/functions.txt14
-rw-r--r--docs/ref/contrib/gis/install/index.txt2
-rw-r--r--docs/ref/databases.txt34
-rw-r--r--docs/releases/2.1.txt6
-rw-r--r--tests/schema/tests.py2
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,18 +15,11 @@ 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
@@ -61,10 +61,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
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.
with self.connection.cursor() as cursor:
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
<https://postgis.net/docs/ST_Difference.html>`__, 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
-<https://postgis.net/docs/ST_Distance.html>`__, Oracle, SpatiaLite
+*Availability*: MySQL, `PostGIS <https://postgis.net/docs/ST_Distance.html>`__,
+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
<https://postgis.net/docs/ST_Intersection.html>`__, 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
<https://postgis.net/docs/ST_SymDifference.html>`__, 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
-<https://postgis.net/docs/ST_Union.html>`__, Oracle, SpatiaLite
+*Availability*: MySQL, `PostGIS <https://postgis.net/docs/ST_Union.html>`__,
+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-spatial-limitations>`.
+MySQL GEOS, GDAL 5.6+ Not OGC-compliant; :ref:`limited functionality <mysql-spatial-limitations>`.
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')