Four scalar functions perform unchecked 64-bit arithmetic on their user-supplied BIGINT arguments. When a value near the BIGINT bounds is passed, the arithmetic overflows and the function either returns a wrong result or fails with a raw ArithmeticException instead of the documented NULL / 0.
| Query |
Actual |
Expected |
SELECT slice(ARRAY[1, 2, 3, 4], 1, 9223372036854775807) |
[] |
[1, 2, 3, 4] |
SELECT element_at(ARRAY[1, 2, 3], -9223372036854775808) |
ArithmeticException: integer overflow |
NULL |
SELECT split_part('abc', '', 3000000000) |
ArithmeticException: integer overflow |
NULL |
SELECT strpos('abc/xyz', '/', -9223372036854775808) |
'instance' must be a positive or negative number. |
0 |
The slice case is the most serious: it returns a wrong result silently rather than failing, so a query asking for "everything from position N onwards" via a large sentinel length quietly loses all its rows.
Details
slice computes its end index as fromIndex + length without an overflow check. A large length wraps the sum negative, which then loses the min(..., size + 1) comparison, so the fromIndex >= toIndex guard treats a fully in-range slice as empty.
element_at guards its bounds with Math.abs(index) > arrayLength. Math.abs(Long.MIN_VALUE) is negative, so the guard does not fire and execution falls through to toIntExact(arrayLength + index), which throws.
split_part converts its index with toIntExact when the delimiter is empty, so any index above Integer.MAX_VALUE throws — even though such an index is simply past the end of the string, a case the function already handles by returning NULL (split_part('abc', '', 99) → NULL).
strpos negates a negative instance with Math.abs before searching from the end. Math.abs(Long.MIN_VALUE) is negative, so stringPositionFromEnd rejects it as non-negative and reports "'instance' must be a positive or negative number" for a value that is in fact negative. The @ScalarFunction(value = "strpos", neverFails = false /* for instance==0 */) annotation documents instance == 0 as the only failure mode, so this is an unintended second one.
Related existing handling
This class of input is already guarded elsewhere in the same code:
substring uses Ints.saturatedCast
overlay uses LongMath.saturatedAdd
json_array_get special-cases Long.MIN_VALUE and returns NULL
These four functions appear to have been missed.
Environment
Reproduced on master.
Four scalar functions perform unchecked 64-bit arithmetic on their user-supplied
BIGINTarguments. When a value near theBIGINTbounds is passed, the arithmetic overflows and the function either returns a wrong result or fails with a rawArithmeticExceptioninstead of the documentedNULL/0.SELECT slice(ARRAY[1, 2, 3, 4], 1, 9223372036854775807)[][1, 2, 3, 4]SELECT element_at(ARRAY[1, 2, 3], -9223372036854775808)ArithmeticException: integer overflowNULLSELECT split_part('abc', '', 3000000000)ArithmeticException: integer overflowNULLSELECT strpos('abc/xyz', '/', -9223372036854775808)'instance' must be a positive or negative number.0The
slicecase is the most serious: it returns a wrong result silently rather than failing, so a query asking for "everything from position N onwards" via a large sentinel length quietly loses all its rows.Details
slicecomputes its end index asfromIndex + lengthwithout an overflow check. A largelengthwraps the sum negative, which then loses themin(..., size + 1)comparison, so thefromIndex >= toIndexguard treats a fully in-range slice as empty.element_atguards its bounds withMath.abs(index) > arrayLength.Math.abs(Long.MIN_VALUE)is negative, so the guard does not fire and execution falls through totoIntExact(arrayLength + index), which throws.split_partconverts its index withtoIntExactwhen the delimiter is empty, so any index aboveInteger.MAX_VALUEthrows — even though such an index is simply past the end of the string, a case the function already handles by returningNULL(split_part('abc', '', 99)→NULL).strposnegates a negativeinstancewithMath.absbefore searching from the end.Math.abs(Long.MIN_VALUE)is negative, sostringPositionFromEndrejects it as non-negative and reports "'instance' must be a positive or negative number" for a value that is in fact negative. The@ScalarFunction(value = "strpos", neverFails = false /* for instance==0 */)annotation documentsinstance == 0as the only failure mode, so this is an unintended second one.Related existing handling
This class of input is already guarded elsewhere in the same code:
substringusesInts.saturatedCastoverlayusesLongMath.saturatedAddjson_array_getspecial-casesLong.MIN_VALUEand returnsNULLThese four functions appear to have been missed.
Environment
Reproduced on
master.