Fixed #28552 -- Dropped support for MySQL 5.5.

This commit is contained in:
Tim Graham 2017-08-24 14:56:09 -04:00
parent 6da140724d
commit 8a1768432b
10 changed files with 38 additions and 88 deletions

View File

@ -15,17 +15,10 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
mysql = True mysql = True
name = 'mysql' name = 'mysql'
geom_func_prefix = 'ST_'
Adapter = WKTAdapter 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 @cached_property
def is_mysql_5_6(self): def is_mysql_5_6(self):
return self.connection.mysql_version < (5, 7, 6) return self.connection.mysql_version < (5, 7, 6)
@ -56,10 +49,6 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
'within': SpatialOperator(func='MBRWithin'), 'within': SpatialOperator(func='MBRWithin'),
} }
@cached_property
def function_names(self):
return {'Length': 'GLength'} if self.is_mysql_5_5 else {}
disallowed_aggregates = ( disallowed_aggregates = (
aggregates.Collect, aggregates.Extent, aggregates.Extent3D, aggregates.Collect, aggregates.Extent, aggregates.Extent3D,
aggregates.MakeLine, aggregates.Union, aggregates.MakeLine, aggregates.Union,
@ -75,8 +64,6 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
} }
if self.connection.mysql_version < (5, 7, 5): if self.connection.mysql_version < (5, 7, 5):
unsupported.update({'AsGeoJSON', 'GeoHash', 'IsValid'}) unsupported.update({'AsGeoJSON', 'GeoHash', 'IsValid'})
if self.is_mysql_5_5:
unsupported.update({'Difference', 'Distance', 'Intersection', 'SymDifference', 'Union'})
return unsupported return unsupported
def geo_db_type(self, f): def geo_db_type(self, f):

View File

@ -97,14 +97,14 @@ class DatabaseWrapper(BaseDatabaseWrapper):
# types, as strings. Column-type strings can contain format strings; they'll # types, as strings. Column-type strings can contain format strings; they'll
# be interpolated against the values of Field.__dict__ before being output. # 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. # If a column type is set to None, it won't be included in the output.
_data_types = { data_types = {
'AutoField': 'integer AUTO_INCREMENT', 'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT', 'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob', 'BinaryField': 'longblob',
'BooleanField': 'bool', 'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)', 'CharField': 'varchar(%(max_length)s)',
'DateField': 'date', 'DateField': 'date',
'DateTimeField': 'datetime', 'DateTimeField': 'datetime(6)',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint', 'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)', 'FileField': 'varchar(%(max_length)s)',
@ -121,17 +121,10 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'SlugField': 'varchar(%(max_length)s)', 'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'longtext', 'TextField': 'longtext',
'TimeField': 'time', 'TimeField': 'time(6)',
'UUIDField': 'char(32)', '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: # For these columns, MySQL doesn't:
# - accept default values and implicitly treats these columns as nullable # - accept default values and implicitly treats these columns as nullable
# - support a database index # - support a database index

View File

@ -60,10 +60,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
"Confirm support for introspected foreign keys" "Confirm support for introspected foreign keys"
return self._mysql_storage_engine != 'MyISAM' return self._mysql_storage_engine != 'MyISAM'
@cached_property
def supports_microsecond_precision(self):
return self.connection.mysql_version >= (5, 6, 4)
@cached_property @cached_property
def has_zoneinfo_database(self): def has_zoneinfo_database(self):
# Test if the time zone definitions are installed. # Test if the time zone definitions are installed.

View File

@ -109,10 +109,7 @@ class DatabaseOperations(BaseDatabaseOperations):
return "INTERVAL '%06f' SECOND_MICROSECOND" % timedelta.total_seconds() return "INTERVAL '%06f' SECOND_MICROSECOND" % timedelta.total_seconds()
def format_for_duration_arithmetic(self, sql): def format_for_duration_arithmetic(self, sql):
if self.connection.features.supports_microsecond_precision: return 'INTERVAL %s MICROSECOND' % sql
return 'INTERVAL %s MICROSECOND' % sql
else:
return 'INTERVAL FLOOR(%s / 1000000) SECOND' % sql
def force_no_ordering(self): def force_no_ordering(self):
""" """
@ -178,10 +175,6 @@ class DatabaseOperations(BaseDatabaseOperations):
value = timezone.make_naive(value, self.connection.timezone) value = timezone.make_naive(value, self.connection.timezone)
else: else:
raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.") 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) return str(value)
def adapt_timefield_value(self, value): def adapt_timefield_value(self, value):
@ -258,17 +251,10 @@ class DatabaseOperations(BaseDatabaseOperations):
def subtract_temporals(self, internal_type, lhs, rhs): def subtract_temporals(self, internal_type, lhs, rhs):
lhs_sql, lhs_params = lhs lhs_sql, lhs_params = lhs
rhs_sql, rhs_params = rhs rhs_sql, rhs_params = rhs
if self.connection.features.supports_microsecond_precision: if internal_type == 'TimeField':
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':
return ( return (
"(TIME_TO_SEC(%s) * POW(10, 6) - TIME_TO_SEC(%s) * POW(10, 6))" "((TIME_TO_SEC(%(lhs)s) * POW(10, 6) + MICROSECOND(%(lhs)s)) -"
) % (lhs_sql, rhs_sql), lhs_params + rhs_params " (TIME_TO_SEC(%(rhs)s) * POW(10, 6) + MICROSECOND(%(rhs)s)))"
) % {'lhs': lhs_sql, 'rhs': rhs_sql}, lhs_params * 2 + rhs_params * 2
else: 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

View File

@ -379,12 +379,12 @@ Function PostGIS Oracle MySQL Spat
:class:`Azimuth` X X (LWGEOM) :class:`Azimuth` X X (LWGEOM)
:class:`BoundingCircle` X X :class:`BoundingCircle` X X
:class:`Centroid` X X X X :class:`Centroid` X X X X
:class:`Difference` X X X (≥ 5.6.1) X :class:`Difference` X X X X
:class:`Distance` X X X (≥ 5.6.1) X :class:`Distance` X X X X
:class:`Envelope` X X X :class:`Envelope` X X X
:class:`ForceRHR` X :class:`ForceRHR` X
:class:`GeoHash` X X (≥ 5.7.5) X (LWGEOM) :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:`IsValid` X X X (≥ 5.7.5) X (LWGEOM)
:class:`Length` X X X X :class:`Length` X X X X
:class:`LineLocatePoint` X X :class:`LineLocatePoint` X X
@ -397,10 +397,10 @@ Function PostGIS Oracle MySQL Spat
:class:`Reverse` X X X :class:`Reverse` X X X
:class:`Scale` X X :class:`Scale` X X
:class:`SnapToGrid` 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:`Transform` X X X
:class:`Translate` X X :class:`Translate` X X
:class:`Union` X X X (≥ 5.6.1) X :class:`Union` X X X X
==================================== ======= ============== =========== ========== ==================================== ======= ============== =========== ==========
Aggregate Functions Aggregate Functions

View File

@ -208,7 +208,7 @@ value of the geometry.
.. class:: Difference(expr1, expr2, **extra) .. class:: Difference(expr1, expr2, **extra)
*Availability*: MySQL (≥ 5.6.1), `PostGIS *Availability*: MySQL, `PostGIS
<https://postgis.net/docs/ST_Difference.html>`__, Oracle, SpatiaLite <https://postgis.net/docs/ST_Difference.html>`__, Oracle, SpatiaLite
Accepts two geographic fields or expressions and returns the geometric Accepts two geographic fields or expressions and returns the geometric
@ -220,8 +220,8 @@ geometry B.
.. class:: Distance(expr1, expr2, spheroid=None, **extra) .. class:: Distance(expr1, expr2, spheroid=None, **extra)
*Availability*: MySQL (≥ 5.6.1), `PostGIS *Availability*: MySQL, `PostGIS <https://postgis.net/docs/ST_Distance.html>`__,
<https://postgis.net/docs/ST_Distance.html>`__, Oracle, SpatiaLite Oracle, SpatiaLite
Accepts two geographic fields or expressions and returns the distance between 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 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) .. class:: Intersection(expr1, expr2, **extra)
*Availability*: MySQL (≥ 5.6.1), `PostGIS *Availability*: MySQL, `PostGIS
<https://postgis.net/docs/ST_Intersection.html>`__, Oracle, SpatiaLite <https://postgis.net/docs/ST_Intersection.html>`__, Oracle, SpatiaLite
Accepts two geographic fields or expressions and returns the geometric Accepts two geographic fields or expressions and returns the geometric
@ -480,7 +480,7 @@ Number of Arguments Description
.. class:: SymDifference(expr1, expr2, **extra) .. class:: SymDifference(expr1, expr2, **extra)
*Availability*: MySQL (≥ 5.6.1), `PostGIS *Availability*: MySQL, `PostGIS
<https://postgis.net/docs/ST_SymDifference.html>`__, Oracle, SpatiaLite <https://postgis.net/docs/ST_SymDifference.html>`__, Oracle, SpatiaLite
Accepts two geographic fields or expressions and returns the geometric Accepts two geographic fields or expressions and returns the geometric
@ -522,8 +522,8 @@ parameters.
.. class:: Union(expr1, expr2, **extra) .. class:: Union(expr1, expr2, **extra)
*Availability*: MySQL (≥ 5.6.1), `PostGIS *Availability*: MySQL, `PostGIS <https://postgis.net/docs/ST_Union.html>`__,
<https://postgis.net/docs/ST_Union.html>`__, Oracle, SpatiaLite Oracle, SpatiaLite
Accepts two geographic fields or expressions and returns the union of both Accepts two geographic fields or expressions and returns the union of both
geometries. geometries.

View File

@ -59,7 +59,7 @@ supported versions, and any notes for each of the supported database backends:
Database Library Requirements Supported Versions Notes Database Library Requirements Supported Versions Notes
================== ============================== ================== ========================================= ================== ============================== ================== =========================================
PostgreSQL GEOS, GDAL, PROJ.4, PostGIS 9.3+ Requires PostGIS. 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. Oracle GEOS, GDAL 12.1+ XE not supported.
SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 4.0+ SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 4.0+
================== ============================== ================== ========================================= ================== ============================== ================== =========================================

View File

@ -276,7 +276,7 @@ MySQL notes
Version support 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 Django's ``inspectdb`` feature uses the ``information_schema`` database, which
contains detailed data on all database schemas. 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 MySQL has several `storage engines`_. You can change the default storage engine
in the server configuration. in the server configuration.
Until MySQL 5.5.4, the default engine was MyISAM_ [#]_. The main drawbacks of MySQL's default storage engine is InnoDB_. This engine is fully transactional
MyISAM are that it doesn't support transactions or enforce foreign-key and supports foreign key references. It's the recommended choice. However, the
constraints. On the plus side, it was the only engine that supported full-text InnoDB autoincrement counter is lost on a MySQL restart because it does not
indexing and searching until MySQL 5.6.4. 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 The main drawbacks of MyISAM_ are that it doesn't support transactions or
transactional and supports foreign key references. It's probably the best enforce foreign-key constraints.
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)"
)
.. _storage engines: https://dev.mysql.com/doc/refman/en/storage-engines.html .. _storage engines: https://dev.mysql.com/doc/refman/en/storage-engines.html
.. _MyISAM: https://dev.mysql.com/doc/refman/en/myisam-storage-engine.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 .. _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:
MySQL DB API Drivers MySQL DB API Drivers

View File

@ -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 Miscellaneous
------------- -------------

View File

@ -2302,8 +2302,6 @@ class SchemaTests(TransactionTestCase):
Changing the primary key field name of a model with a self-referential Changing the primary key field name of a model with a self-referential
foreign key (#26384). 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: with connection.schema_editor() as editor:
editor.create_model(Node) editor.create_model(Node)
old_field = Node._meta.get_field('node_id') old_field = Node._meta.get_field('node_id')