Fixed #30750 -- Added support for check constraints on MySQL 8.0.16+.
This commit is contained in:
parent
b93d786251
commit
e2c6a0858d
|
@ -61,6 +61,7 @@ class CursorWrapper:
|
||||||
codes_for_integrityerror = (
|
codes_for_integrityerror = (
|
||||||
1048, # Column cannot be null
|
1048, # Column cannot be null
|
||||||
1690, # BIGINT UNSIGNED value is out of range
|
1690, # BIGINT UNSIGNED value is out of range
|
||||||
|
3819, # CHECK constraint is violated
|
||||||
4025, # CHECK constraint failed
|
4025, # CHECK constraint failed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,9 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def supports_column_check_constraints(self):
|
def supports_column_check_constraints(self):
|
||||||
return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 2, 1)
|
if self.connection.mysql_is_mariadb:
|
||||||
|
return self.connection.mysql_version >= (10, 2, 1)
|
||||||
|
return self.connection.mysql_version >= (8, 0, 16)
|
||||||
|
|
||||||
supports_table_check_constraints = property(operator.attrgetter('supports_column_check_constraints'))
|
supports_table_check_constraints = property(operator.attrgetter('supports_column_check_constraints'))
|
||||||
|
|
||||||
|
@ -99,7 +101,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
version = self.connection.mysql_version
|
version = self.connection.mysql_version
|
||||||
if (version >= (10, 2, 22) and version < (10, 3)) or version >= (10, 3, 10):
|
if (version >= (10, 2, 22) and version < (10, 3)) or version >= (10, 3, 10):
|
||||||
return True
|
return True
|
||||||
return False
|
return self.connection.mysql_version >= (8, 0, 16)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def has_select_for_update_skip_locked(self):
|
def has_select_for_update_skip_locked(self):
|
||||||
|
|
|
@ -209,13 +209,27 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
||||||
if self.connection.features.can_introspect_check_constraints:
|
if self.connection.features.can_introspect_check_constraints:
|
||||||
unnamed_constraints_index = 0
|
unnamed_constraints_index = 0
|
||||||
columns = {info.name for info in self.get_table_description(cursor, table_name)}
|
columns = {info.name for info in self.get_table_description(cursor, table_name)}
|
||||||
type_query = """
|
if self.connection.mysql_is_mariadb:
|
||||||
SELECT c.constraint_name, c.check_clause
|
type_query = """
|
||||||
FROM information_schema.check_constraints AS c
|
SELECT c.constraint_name, c.check_clause
|
||||||
WHERE
|
FROM information_schema.check_constraints AS c
|
||||||
c.constraint_schema = DATABASE() AND
|
WHERE
|
||||||
c.table_name = %s
|
c.constraint_schema = DATABASE() AND
|
||||||
"""
|
c.table_name = %s
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
type_query = """
|
||||||
|
SELECT cc.constraint_name, cc.check_clause
|
||||||
|
FROM
|
||||||
|
information_schema.check_constraints AS cc,
|
||||||
|
information_schema.table_constraints AS tc
|
||||||
|
WHERE
|
||||||
|
cc.constraint_schema = DATABASE() AND
|
||||||
|
tc.table_schema = cc.constraint_schema AND
|
||||||
|
cc.constraint_name = tc.constraint_name AND
|
||||||
|
tc.constraint_type = 'CHECK' AND
|
||||||
|
tc.table_name = %s
|
||||||
|
"""
|
||||||
cursor.execute(type_query, [table_name])
|
cursor.execute(type_query, [table_name])
|
||||||
for constraint, check_clause in cursor.fetchall():
|
for constraint, check_clause in cursor.fetchall():
|
||||||
constraint_columns = self._parse_constraint_columns(check_clause, columns)
|
constraint_columns = self._parse_constraint_columns(check_clause, columns)
|
||||||
|
|
|
@ -28,10 +28,15 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY"
|
sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY"
|
||||||
|
|
||||||
sql_create_index = 'CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(extra)s'
|
sql_create_index = 'CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(extra)s'
|
||||||
# The name of the column check constraint is the same as the field name on
|
|
||||||
# MariaDB. Adding IF EXISTS clause prevents migrations crash. Constraint is
|
@property
|
||||||
# removed during a "MODIFY" column statement.
|
def sql_delete_check(self):
|
||||||
sql_delete_check = 'ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(name)s'
|
if self.connection.mysql_is_mariadb:
|
||||||
|
# The name of the column check constraint is the same as the field
|
||||||
|
# name on MariaDB. Adding IF EXISTS clause prevents migrations
|
||||||
|
# crash. Constraint is removed during a "MODIFY" column statement.
|
||||||
|
return 'ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(name)s'
|
||||||
|
return 'ALTER TABLE %(table)s DROP CHECK %(name)s'
|
||||||
|
|
||||||
def quote_value(self, value):
|
def quote_value(self, value):
|
||||||
self.connection.ensure_connection()
|
self.connection.ensure_connection()
|
||||||
|
|
|
@ -353,6 +353,8 @@ Models
|
||||||
|
|
||||||
* :attr:`.FileField.upload_to` now supports :class:`pathlib.Path`.
|
* :attr:`.FileField.upload_to` now supports :class:`pathlib.Path`.
|
||||||
|
|
||||||
|
* :class:`~django.db.models.CheckConstraint` is now supported on MySQL 8.0.16+.
|
||||||
|
|
||||||
Requests and Responses
|
Requests and Responses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -579,6 +581,9 @@ Miscellaneous
|
||||||
:ref:`x-content-type-options` header on all responses that do not already
|
:ref:`x-content-type-options` header on all responses that do not already
|
||||||
have it.
|
have it.
|
||||||
|
|
||||||
|
* On MySQL 8.0.16+, ``PositiveIntegerField`` and ``PositiveSmallIntegerField``
|
||||||
|
now include a check constraint to prevent negative values in the database.
|
||||||
|
|
||||||
.. _deprecated-features-3.0:
|
.. _deprecated-features-3.0:
|
||||||
|
|
||||||
Features deprecated in 3.0
|
Features deprecated in 3.0
|
||||||
|
|
Loading…
Reference in New Issue