From b9cf764be62e77b4777b3a75ec256f6209a57671 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 22 Jun 2018 14:42:51 -0400 Subject: [PATCH] Fixed #29517 -- Added support for SQLite column check constraints on positive integer fields. --- django/db/backends/sqlite3/base.py | 4 ++++ django/db/backends/sqlite3/features.py | 1 - django/db/backends/sqlite3/introspection.py | 26 +++++++++++++++++++++ docs/releases/2.2.txt | 5 +++- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index b644f4d42a..6237c3b6f8 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -76,6 +76,10 @@ class DatabaseWrapper(BaseDatabaseWrapper): 'TimeField': 'time', 'UUIDField': 'char(32)', } + data_type_check_constraints = { + 'PositiveIntegerField': '"%(column)s" >= 0', + 'PositiveSmallIntegerField': '"%(column)s" >= 0', + } data_types_suffix = { 'AutoField': 'AUTOINCREMENT', 'BigAutoField': 'AUTOINCREMENT', diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py index 7563edf1c0..4d9f4985d4 100644 --- a/django/db/backends/sqlite3/features.py +++ b/django/db/backends/sqlite3/features.py @@ -14,7 +14,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_timezones = False max_query_params = 999 supports_mixed_date_datetime_comparisons = False - supports_column_check_constraints = False autocommits_when_autocommit_is_off = True can_introspect_decimal_field = False can_introspect_positive_integer_field = True diff --git a/django/db/backends/sqlite3/introspection.py b/django/db/backends/sqlite3/introspection.py index 75fb9a7ced..0c82ea8844 100644 --- a/django/db/backends/sqlite3/introspection.py +++ b/django/db/backends/sqlite3/introspection.py @@ -231,6 +231,32 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): one or more columns. """ constraints = {} + # Find inline check constraints. + try: + table_schema = cursor.execute( + "SELECT sql FROM sqlite_master WHERE type='table' and name=%s" % ( + self.connection.ops.quote_name(table_name), + ) + ).fetchone()[0] + except TypeError: + # table_name is a view. + pass + else: + fields_with_check_constraints = [ + schema_row.strip().split(' ')[0][1:-1] + for schema_row in table_schema.split(',') + if schema_row.find('CHECK') >= 0 + ] + for field_name in fields_with_check_constraints: + # An arbitrary made up name. + constraints['__check__%s' % field_name] = { + 'columns': [field_name], + 'primary_key': False, + 'unique': False, + 'foreign_key': False, + 'check': True, + 'index': False, + } # Get the index info cursor.execute("PRAGMA index_list(%s)" % self.connection.ops.quote_name(table_name)) for row in cursor.fetchall(): diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt index 8bcb3a113a..840d4b4d0d 100644 --- a/docs/releases/2.2.txt +++ b/docs/releases/2.2.txt @@ -218,7 +218,10 @@ Database backend API Miscellaneous ------------- -* ... +* On SQLite, ``PositiveIntegerField`` and ``PositiveSmallIntegerField`` now + include a check constraint to prevent negative values in the database. If you + have existing invalid data and run a migration that recreates a table, you'll + see ``CHECK constraint failed``. .. _deprecated-features-2.2: