summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2019-09-04 16:54:27 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-09-05 09:18:34 +0200
commite2c6a0858d7d9ad85eda353076a5b46608b704a9 (patch)
tree6207019300056a7410d39464f362c6502846ce44
parentb93d786251513a5da102abc31f275ab3ee2850ed (diff)
Fixed #30750 -- Added support for check constraints on MySQL 8.0.16+.
-rw-r--r--django/db/backends/mysql/base.py1
-rw-r--r--django/db/backends/mysql/features.py6
-rw-r--r--django/db/backends/mysql/introspection.py28
-rw-r--r--django/db/backends/mysql/schema.py13
-rw-r--r--docs/releases/3.0.txt5
5 files changed, 40 insertions, 13 deletions
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index febc246e09..d60ff7937d 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -61,6 +61,7 @@ class CursorWrapper:
codes_for_integrityerror = (
1048, # Column cannot be null
1690, # BIGINT UNSIGNED value is out of range
+ 3819, # CHECK constraint is violated
4025, # CHECK constraint failed
)
diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py
index 229e26a750..5800ad6c95 100644
--- a/django/db/backends/mysql/features.py
+++ b/django/db/backends/mysql/features.py
@@ -89,7 +89,9 @@ class DatabaseFeatures(BaseDatabaseFeatures):
@cached_property
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'))
@@ -99,7 +101,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
version = self.connection.mysql_version
if (version >= (10, 2, 22) and version < (10, 3)) or version >= (10, 3, 10):
return True
- return False
+ return self.connection.mysql_version >= (8, 0, 16)
@cached_property
def has_select_for_update_skip_locked(self):
diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py
index 82637d15fb..a014b87916 100644
--- a/django/db/backends/mysql/introspection.py
+++ b/django/db/backends/mysql/introspection.py
@@ -209,13 +209,27 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
if self.connection.features.can_introspect_check_constraints:
unnamed_constraints_index = 0
columns = {info.name for info in self.get_table_description(cursor, table_name)}
- type_query = """
- SELECT c.constraint_name, c.check_clause
- FROM information_schema.check_constraints AS c
- WHERE
- c.constraint_schema = DATABASE() AND
- c.table_name = %s
- """
+ if self.connection.mysql_is_mariadb:
+ type_query = """
+ SELECT c.constraint_name, c.check_clause
+ FROM information_schema.check_constraints AS c
+ WHERE
+ 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])
for constraint, check_clause in cursor.fetchall():
constraint_columns = self._parse_constraint_columns(check_clause, columns)
diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py
index d138606791..d379965c27 100644
--- a/django/db/backends/mysql/schema.py
+++ b/django/db/backends/mysql/schema.py
@@ -28,10 +28,15 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY"
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
- # removed during a "MODIFY" column statement.
- sql_delete_check = 'ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(name)s'
+
+ @property
+ def sql_delete_check(self):
+ 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):
self.connection.ensure_connection()
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 2868e37e16..570effe6b3 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -353,6 +353,8 @@ Models
* :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
~~~~~~~~~~~~~~~~~~~~~~
@@ -579,6 +581,9 @@ Miscellaneous
:ref:`x-content-type-options` header on all responses that do not already
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:
Features deprecated in 3.0