mirror of https://github.com/django/django.git
Fixed #28552 -- Dropped support for MySQL 5.5.
This commit is contained in:
parent
6da140724d
commit
8a1768432b
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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+
|
||||||
================== ============================== ================== =========================================
|
================== ============================== ================== =========================================
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue