Fixed #25767 -- Fixed data truncation possibility with Positive(Small)IntegerField on MySQL.

This commit is contained in:
George Marshall 2015-11-16 22:28:03 -08:00 committed by Tim Graham
parent a918f8b089
commit 710e11d076
3 changed files with 36 additions and 5 deletions

View File

@ -13,8 +13,8 @@ class DatabaseOperations(BaseDatabaseOperations):
# MySQL stores positive fields as UNSIGNED ints. # MySQL stores positive fields as UNSIGNED ints.
integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges, integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges,
PositiveSmallIntegerField=(0, 4294967295), PositiveSmallIntegerField=(0, 65535),
PositiveIntegerField=(0, 18446744073709551615), PositiveIntegerField=(0, 4294967295),
) )
def date_extract_sql(self, lookup_type, field_name): def date_extract_sql(self, lookup_type, field_name):

View File

@ -46,3 +46,8 @@ Bugfixes
* Fixed ``set_FOO_order()`` crash when the ``ForeignKey`` of a model with * Fixed ``set_FOO_order()`` crash when the ``ForeignKey`` of a model with
``order_with_respect_to`` references a model with a ``OneToOneField`` ``order_with_respect_to`` references a model with a ``OneToOneField``
primary key (:ticket:`25786`). 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`).

View File

@ -624,6 +624,12 @@ class IntegerFieldTests(test.TestCase):
model = IntegerModel model = IntegerModel
documented_range = (-2147483648, 2147483647) 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): def test_documented_range(self):
""" """
Ensure that values within the documented safe range pass validation, 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.count(), 1)
self.assertEqual(qs[0].value, max_value) 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): def test_backend_range_validation(self):
""" """
Ensure that backend specific range are enforced at the model Ensure that backend specific range are enforced at the model
validation level. ref #12030. validation level. ref #12030.
""" """
field = self.model._meta.get_field('value') min_value, max_value = self.backend_range
internal_type = field.get_internal_type()
min_value, max_value = connection.ops.integer_field_range(internal_type)
if min_value is not None: if min_value is not None:
instance = self.model(value=min_value - 1) instance = self.model(value=min_value - 1)