Commit 581b9e50 authored by George Marshall's avatar George Marshall Committed by Tim Graham
Browse files

[1.8.x] Fixed #25767 -- Fixed data truncation possibility with...

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

Backport of 710e11d0 from master
parent acaf30ad
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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):
+5 −0
Original line number Diff line number Diff line
@@ -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`).
+29 −3
Original line number Diff line number Diff line
@@ -506,6 +506,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,
@@ -527,14 +533,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)