Recurse Center: Round Two!BlogJacob deGroot-Maggetti

Numbers in Drupal

...may not be exactly as you expect them to be...

I became curious about numbers in Drupal after entering a number with many decimal places and finding that Drupal gave me an error. I’m keeping this running notebook of things I’ve tried and/or discovered.

Thundernumber

Let’s start by generating a number that’s sufficiently large, that we can tweak in our explorations. One hundred digits should do! Here’s some Python to generate a hundred-digit number (let’s call it our thundernumber, after the hundred-letter thunderwords in James Joyce’s Finnegan’s Wake):

$ python3
>>> thundernumber = ""
>>> for x in "1234567890":
...     thundernumber += x + "234567890"
... 
>>> thundernumber
'12345678902234567890323456789042345678905234567890
62345678907234567890823456789092345678900234567890'
>>> len(thundernumber)
100

The Mystery of the Minimum and Maximum Integer Values

Let’s make a new Content Type, with a bunch of varied number fields. We’ll call it “Numbers”:

A new Content Type being created
Let's make a new Content Type

Let’s get rid of the “body” field and add some number fields.

Deleting the "Body" field from our Numbers Content Type
Let's get rid of the "Body" field

First, let’s create a field called integer. We’ll set its maximum value to our thundernumber and its minimum to its negative

We can do this without an error, but when we try to edit the field again, something unusual has happened:

A new Content Type being created
New minimum and maximum values have appeared

The maximum has been changed from our thundernumber to 9223372036854775807, and the minimum is now -9223372036854775808.

>>> 9223372036854775807 + (-9223372036854775808)
-1

Huh. I’m guessing that Drupal has provided the maximum and minimum values allowable by MySQL, the database running under the hood. If this is the case, how many unique integers can this represent?

>>> 9223372036854775808 * 2
        18446744073709551616

This should account for all the negative integers, plus all of the positive integers, plus 0. I’m curious whether this is a large power of two. The first prime factorization calculator Google turned up cannot handle numbers this big, so let’s check if this is the case ourselves by dividing this number by two as many times as we can:

min_to_max = 18446744073709551616
quotients = [min_to_max]
while min_to_max > 1:
    div, mod = divmod(min_to_max, 2)
    quotients.append(div)
    if mod != 0:
        print("not divisible by 2!")
        break

    min_to_max = div

print(f"{quotients=}, {len(quotients)=}")

It outputs:

quotients=[18446744073709551616, 9223372036854775808, 4611686018427387904, 2305843009213693952, 1152921504606846976, 576460752303423488, 288230376151711744, 144115188075855872, 72057594037927936, 36028797018963968, 18014398509481984, 9007199254740992, 4503599627370496, 2251799813685248, 1125899906842624, 562949953421312, 281474976710656, 140737488355328, 70368744177664, 35184372088832, 17592186044416, 8796093022208, 4398046511104, 2199023255552, 1099511627776, 549755813888, 274877906944, 137438953472, 68719476736, 34359738368, 17179869184, 8589934592, 4294967296, 2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1], len(quotients)=65

Just to check we’ve done this right,

>>> 2**64 # quotients includes 1, so we need to raise 2 to the 64
>>>       # instead of to the 65
18446744073709551616

Okay! So it looks like Drupal is using 64-bit signed integers. And a quick google search confirms that 9223372036854775807 and -9223372036854775808 are in fact the maximum and minimum values that can be stored in a 64-bit signed integer. In a perfect world, I feel that Drupal would give you a warning if you tried to enter a number beyond these values, but at least it makes some sort of sense.

Given that there are minimum and maximum values for the minimum and maximum fields themselves, I’m curious about what happens if you make a field without setting a minimum or maximum value, and then try to enter a larger number in that field. The help text on the Maximum field says “leave blank for no maximum”—is there actually no maximum?

Let’s create a new Integer field on our Numbers content type. Let’s call it “unlimited integer”

If I try to create a new Numbers node and try to enter our thundernumber in the “unlimited integer” field, there are three things I can imagine happening:

Let’s find out!

Error message, reading "This value should be of the correct primitive type"
Our thundernumber is not of the "correct primitive type"

Huh. So Drupal gives us an error message, but the message doesn’t mention a maximum or minimum value. I’m guessing that, according to MySQL, an integer greater than the largest possible 64-bit signed integer is not of the same “primitive type” as a 64-bit integer, and that Drupal simply passes the MySQL error message along to the user. Understandable, but not ideal!

To test the theory that this is a 64-bit signed integer thing, I tried saving 9223372036854775807 into our “unlimited integer” field - it saves just fine. But 9223372036854775808? “This value should be of the correct primitive type.”

Further, to test the theory that this is an error message being passed from the database, we should check what happens when a user enters a value beyond the configured minimum and maximum. If we create an integer field (let’s call it “million integer”), with a maximum of 1000000, what happens when we try to save a larger number into that field? Do we get the same kind of error message?

Error message, reading "Please select a value that is no more than 1000000."
"Please select a value that is no more than 1000000."

So that settles it. Drupal catches values that don’t fit within the minimum-maximum range and displays its own little pop-up error messages when it does. But if there is no minimum or maximum set, it will attempt to send any number to the database. And when the database complains that some number cannot be saved into a 64-bit signed integer field, Drupal simply echoes the database’s complaint.

Posted: Jun 07, 2024. Last updated: Jun 10, 2024.