diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out index 0f4775b88..c2a2a3af1 100644 --- a/regress/expected/agtype.out +++ b/regress/expected/agtype.out @@ -3906,6 +3906,54 @@ SELECT * FROM cypher('issue_2243', $$ 0::numeric (1 row) +-- +-- Integer overflow tests (issue #2471) +-- +SELECT create_graph('integer_overflow'); +NOTICE: graph "integer_overflow" has been created + create_graph +-------------- + +(1 row) + +-- Addition overflow +SELECT * FROM cypher('integer_overflow', $$ + RETURN 9223372036854775807 + 1 AS r + $$) AS (r agtype); +ERROR: integer addition overflow +-- Subtraction overflow (underflow) +SELECT * FROM cypher('integer_overflow', $$ + RETURN -9223372036854775808 - 1 AS r + $$) AS (r agtype); +ERROR: integer subtraction overflow +-- Multiplication overflow +SELECT * FROM cypher('integer_overflow', $$ + RETURN 9223372036854775807 * 2 AS r + $$) AS (r agtype); +ERROR: integer multiplication overflow +-- Negation overflow +SELECT * FROM cypher('integer_overflow', $$ + RETURN -(-9223372036854775808) AS r + $$) AS (r agtype); +ERROR: integer negation overflow +-- Division overflow (INT64_MIN / -1) +SELECT * FROM cypher('integer_overflow', $$ + RETURN -9223372036854775808 / -1 AS r + $$) AS (r agtype); +ERROR: integer division overflow +-- Modulo overflow (INT64_MIN % -1) +SELECT * FROM cypher('integer_overflow', $$ + RETURN -9223372036854775808 % -1 AS r + $$) AS (r agtype); +ERROR: integer modulo overflow +-- Cleanup overflow tests +SELECT drop_graph('integer_overflow', true); +NOTICE: graph "integer_overflow" has been dropped + drop_graph +------------ + +(1 row) + -- -- Cleanup -- diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql index e13edb16d..7c047a5dc 100644 --- a/regress/sql/agtype.sql +++ b/regress/sql/agtype.sql @@ -1127,6 +1127,44 @@ SELECT * FROM cypher('issue_2243', $$ RETURN 9223372036854775807::integer % 9223372036854775807::numeric $$ ) as (result agtype); +-- +-- Integer overflow tests (issue #2471) +-- +SELECT create_graph('integer_overflow'); + +-- Addition overflow +SELECT * FROM cypher('integer_overflow', $$ + RETURN 9223372036854775807 + 1 AS r + $$) AS (r agtype); + +-- Subtraction overflow (underflow) +SELECT * FROM cypher('integer_overflow', $$ + RETURN -9223372036854775808 - 1 AS r + $$) AS (r agtype); + +-- Multiplication overflow +SELECT * FROM cypher('integer_overflow', $$ + RETURN 9223372036854775807 * 2 AS r + $$) AS (r agtype); + +-- Negation overflow +SELECT * FROM cypher('integer_overflow', $$ + RETURN -(-9223372036854775808) AS r + $$) AS (r agtype); + +-- Division overflow (INT64_MIN / -1) +SELECT * FROM cypher('integer_overflow', $$ + RETURN -9223372036854775808 / -1 AS r + $$) AS (r agtype); + +-- Modulo overflow (INT64_MIN % -1) +SELECT * FROM cypher('integer_overflow', $$ + RETURN -9223372036854775808 % -1 AS r + $$) AS (r agtype); + +-- Cleanup overflow tests +SELECT drop_graph('integer_overflow', true); + -- -- Cleanup -- diff --git a/src/backend/utils/adt/agtype_ops.c b/src/backend/utils/adt/agtype_ops.c index f7f45b467..d7838532a 100644 --- a/src/backend/utils/adt/agtype_ops.c +++ b/src/backend/utils/adt/agtype_ops.c @@ -188,12 +188,21 @@ Datum agtype_add(PG_FUNCTION_ARGS) concat_to_agtype_string(&agtv_result, lhs, llen, rhs, rlen); } - /* Both are integers - regular addition */ + /* Both are integers - addition with overflow check */ else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER) { + int64 result; + + if (__builtin_add_overflow(agtv_lhs->val.int_value, + agtv_rhs->val.int_value, &result)) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer addition overflow"))); + } + agtv_result.type = AGTV_INTEGER; - agtv_result.val.int_value = agtv_lhs->val.int_value + - agtv_rhs->val.int_value; + agtv_result.val.int_value = result; } /* Both are floats - regular addition */ else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT) @@ -539,9 +548,18 @@ Datum agtype_sub(PG_FUNCTION_ARGS) if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER) { + int64 result; + + if (__builtin_sub_overflow(agtv_lhs->val.int_value, + agtv_rhs->val.int_value, &result)) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer subtraction overflow"))); + } + agtv_result.type = AGTV_INTEGER; - agtv_result.val.int_value = agtv_lhs->val.int_value - - agtv_rhs->val.int_value; + agtv_result.val.int_value = result; } else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT) { @@ -635,11 +653,25 @@ Datum agtype_neg(PG_FUNCTION_ARGS) if (agtv_value->type == AGTV_INTEGER) { + if (agtv_value->val.int_value == PG_INT64_MIN) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer negation overflow"))); + } + agtv_result.type = AGTV_INTEGER; agtv_result.val.int_value = -agtv_value->val.int_value; } else if (agtv_value->type == AGTV_FLOAT) { + if (agtv_value->val.float_value == -(double)PG_INT64_MIN) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer negation overflow"))); + } + agtv_result.type = AGTV_FLOAT; agtv_result.val.float_value = -agtv_value->val.float_value; } @@ -692,9 +724,18 @@ Datum agtype_mul(PG_FUNCTION_ARGS) if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER) { + int64 result; + + if (__builtin_mul_overflow(agtv_lhs->val.int_value, + agtv_rhs->val.int_value, &result)) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer multiplication overflow"))); + } + agtv_result.type = AGTV_INTEGER; - agtv_result.val.int_value = agtv_lhs->val.int_value * - agtv_rhs->val.int_value; + agtv_result.val.int_value = result; } else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT) { @@ -795,6 +836,14 @@ Datum agtype_div(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } + if (agtv_lhs->val.int_value == PG_INT64_MIN && + agtv_rhs->val.int_value == -1) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer division overflow"))); + } + agtv_result.type = AGTV_INTEGER; agtv_result.val.int_value = agtv_lhs->val.int_value / agtv_rhs->val.int_value; @@ -912,6 +961,21 @@ Datum agtype_mod(PG_FUNCTION_ARGS) if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER) { + if (agtv_rhs->val.int_value == 0) + { + ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + PG_RETURN_NULL(); + } + + if (agtv_lhs->val.int_value == PG_INT64_MIN && + agtv_rhs->val.int_value == -1) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer modulo overflow"))); + } + agtv_result.type = AGTV_INTEGER; agtv_result.val.int_value = agtv_lhs->val.int_value % agtv_rhs->val.int_value;