diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 8fc67ff8e3..f81bf683b3 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -13,8 +13,8 @@ class DatabaseOperations(BaseDatabaseOperations): # MySQL stores positive fields as UNSIGNED ints. integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges, - PositiveSmallIntegerField=(0, 4294967295), - PositiveIntegerField=(0, 18446744073709551615), + PositiveSmallIntegerField=(0, 65535), + PositiveIntegerField=(0, 4294967295), ) def date_extract_sql(self, lookup_type, field_name): diff --git a/docs/releases/1.8.7.txt b/docs/releases/1.8.7.txt index b11ea9781a..25f7712f10 100644 --- a/docs/releases/1.8.7.txt +++ b/docs/releases/1.8.7.txt @@ -46,3 +46,8 @@ Bugfixes * Fixed ``set_FOO_order()`` crash when the ``ForeignKey`` of a model with ``order_with_respect_to`` references a model with a ``OneToOneField`` primary key (:ticket:`25786`). + +* Fixed incorrect validation for ``PositiveIntegerField`` and + ``PositiveSmallIntegerField`` on MySQL resulting in values greater than + 4294967295 or 65535, respectively, passing validation and being silently + truncated by the database (:ticket:`25767`). diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index 23e90cccb8..34793eacdb 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -624,6 +624,12 @@ class IntegerFieldTests(test.TestCase): model = IntegerModel documented_range = (-2147483648, 2147483647) + @property + def backend_range(self): + field = self.model._meta.get_field('value') + internal_type = field.get_internal_type() + return connection.ops.integer_field_range(internal_type) + def test_documented_range(self): """ Ensure that values within the documented safe range pass validation, @@ -645,14 +651,34 @@ class IntegerFieldTests(test.TestCase): self.assertEqual(qs.count(), 1) self.assertEqual(qs[0].value, max_value) + def test_backend_range_save(self): + """ + Ensure that backend specific range can be saved without corruption. + """ + min_value, max_value = self.backend_range + + if min_value is not None: + instance = self.model(value=min_value) + instance.full_clean() + instance.save() + qs = self.model.objects.filter(value__lte=min_value) + self.assertEqual(qs.count(), 1) + self.assertEqual(qs[0].value, min_value) + + if max_value is not None: + instance = self.model(value=max_value) + instance.full_clean() + instance.save() + qs = self.model.objects.filter(value__gte=max_value) + self.assertEqual(qs.count(), 1) + self.assertEqual(qs[0].value, max_value) + def test_backend_range_validation(self): """ Ensure that backend specific range are enforced at the model validation level. ref #12030. """ - field = self.model._meta.get_field('value') - internal_type = field.get_internal_type() - min_value, max_value = connection.ops.integer_field_range(internal_type) + min_value, max_value = self.backend_range if min_value is not None: instance = self.model(value=min_value - 1)