diff --git a/django/contrib/gis/db/backends/mysql/schema.py b/django/contrib/gis/db/backends/mysql/schema.py index 5305bd2280..693c48dba6 100644 --- a/django/contrib/gis/db/backends/mysql/schema.py +++ b/django/contrib/gis/db/backends/mysql/schema.py @@ -16,8 +16,8 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor): self.geometry_sql = [] def skip_default(self, field): - # Geometry fields are stored as BLOB/TEXT, for which MySQL and MariaDB - # < 10.2.1 don't support defaults. + # Geometry fields are stored as BLOB/TEXT, for which MySQL < 8.0.13 and + # MariaDB < 10.2.1 don't support defaults. if isinstance(field, GeometryField) and not self._supports_limited_data_type_defaults: return True return super().skip_default(field) diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index efb291f4e6..3a0dc39238 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -217,14 +217,15 @@ class BaseDatabaseSchemaEditor: include_default = include_default and not self.skip_default(field) if include_default: default_value = self.effective_default(field) + column_default = ' DEFAULT ' + self._column_default_sql(field) if default_value is not None: if self.connection.features.requires_literal_defaults: # Some databases can't take defaults as a parameter (oracle) # If this is the case, the individual schema backend should # implement prepare_default - sql += " DEFAULT %s" % self.prepare_default(default_value) + sql += column_default % self.prepare_default(default_value) else: - sql += " DEFAULT %s" + sql += column_default params += [default_value] # Oracle treats the empty string ('') as null, so coerce the null # option whenever '' is a possible value. @@ -263,6 +264,13 @@ class BaseDatabaseSchemaEditor: 'requires_literal_defaults must provide a prepare_default() method' ) + def _column_default_sql(self, field): + """ + Return the SQL to use in a DEFAULT clause. The resulting string should + contain a '%s' placeholder for a default value. + """ + return '%s' + @staticmethod def _effective_default(field): # This method allows testing its logic without a connection. @@ -826,7 +834,7 @@ class BaseDatabaseSchemaEditor: argument) a default to new_field's column. """ new_default = self.effective_default(new_field) - default = '%s' + default = self._column_default_sql(new_field) params = [new_default] if drop: diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 58f54c70a8..febc246e09 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -131,8 +131,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): } # For these data types: - # - MySQL and MariaDB < 10.2.1 don't accept default values and implicitly - # treat them as nullable + # - MySQL < 8.0.13 and MariaDB < 10.2.1 don't accept default values and + # implicitly treat them as nullable # - all versions of MySQL and MariaDB don't support full width database # indexes _limited_data_types = ( diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py index 8c54565e3d..d138606791 100644 --- a/django/db/backends/mysql/schema.py +++ b/django/db/backends/mysql/schema.py @@ -54,10 +54,22 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @property def _supports_limited_data_type_defaults(self): - # Only MariaDB >= 10.2.1 supports defaults for BLOB and TEXT. + # MariaDB >= 10.2.1 and MySQL >= 8.0.13 supports defaults for BLOB + # and TEXT. if self.connection.mysql_is_mariadb: return self.connection.mysql_version >= (10, 2, 1) - return False + return self.connection.mysql_version >= (8, 0, 13) + + def _column_default_sql(self, field): + if ( + not self.connection.mysql_is_mariadb and + self._supports_limited_data_type_defaults and + self._is_limited_data_type(field) + ): + # MySQL supports defaults for BLOB and TEXT columns only if the + # default value is written as an expression i.e. in parentheses. + return '(%s)' + return super()._column_default_sql(field) def add_field(self, model, field): super().add_field(model, field)