mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Improve handling of INT_MIN / -1 and related cases.
Some platforms throw an exception for this division, rather than returning a necessarily-overflowed result. Since we were testing for overflow after the fact, an exception isn't nice. We can avoid the problem by treating division by -1 as negation. Add some regression tests so that we'll find out if any compilers try to optimize away the overflow check conditions. This ought to be back-patched, but I'm going to see what the buildfarm reports about the regression tests first. Per discussion with Xi Wang, though this is different from the patch he submitted.
This commit is contained in:
@ -574,7 +574,8 @@ int8mul(PG_FUNCTION_ARGS)
|
||||
if (arg1 != (int64) ((int32) arg1) || arg2 != (int64) ((int32) arg2))
|
||||
{
|
||||
if (arg2 != 0 &&
|
||||
(result / arg2 != arg1 || (arg2 == -1 && arg1 < 0 && result < 0)))
|
||||
((arg2 == -1 && arg1 < 0 && result < 0) ||
|
||||
result / arg2 != arg1))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
@ -598,18 +599,27 @@ int8div(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
/*
|
||||
* INT64_MIN / -1 is problematic, since the result can't be represented on
|
||||
* a two's-complement machine. Some machines produce INT64_MIN, some
|
||||
* produce zero, some throw an exception. We can dodge the problem by
|
||||
* recognizing that division by -1 is the same as negation.
|
||||
*/
|
||||
if (arg2 == -1)
|
||||
{
|
||||
result = -arg1;
|
||||
/* overflow check (needed for INT64_MIN) */
|
||||
if (arg1 != 0 && SAMESIGN(result, arg1))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
/* No overflow is possible */
|
||||
|
||||
result = arg1 / arg2;
|
||||
|
||||
/*
|
||||
* Overflow check. The only possible overflow case is for arg1 =
|
||||
* INT64_MIN, arg2 = -1, where the correct result is -INT64_MIN, which
|
||||
* can't be represented on a two's-complement machine. Most machines
|
||||
* produce INT64_MIN but it seems some produce zero.
|
||||
*/
|
||||
if (arg2 == -1 && arg1 < 0 && result <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
@ -838,18 +848,27 @@ int84div(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
/*
|
||||
* INT64_MIN / -1 is problematic, since the result can't be represented on
|
||||
* a two's-complement machine. Some machines produce INT64_MIN, some
|
||||
* produce zero, some throw an exception. We can dodge the problem by
|
||||
* recognizing that division by -1 is the same as negation.
|
||||
*/
|
||||
if (arg2 == -1)
|
||||
{
|
||||
result = -arg1;
|
||||
/* overflow check (needed for INT64_MIN) */
|
||||
if (arg1 != 0 && SAMESIGN(result, arg1))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
/* No overflow is possible */
|
||||
|
||||
result = arg1 / arg2;
|
||||
|
||||
/*
|
||||
* Overflow check. The only possible overflow case is for arg1 =
|
||||
* INT64_MIN, arg2 = -1, where the correct result is -INT64_MIN, which
|
||||
* can't be represented on a two's-complement machine. Most machines
|
||||
* produce INT64_MIN but it seems some produce zero.
|
||||
*/
|
||||
if (arg2 == -1 && arg1 < 0 && result <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
@ -1026,18 +1045,27 @@ int82div(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
/*
|
||||
* INT64_MIN / -1 is problematic, since the result can't be represented on
|
||||
* a two's-complement machine. Some machines produce INT64_MIN, some
|
||||
* produce zero, some throw an exception. We can dodge the problem by
|
||||
* recognizing that division by -1 is the same as negation.
|
||||
*/
|
||||
if (arg2 == -1)
|
||||
{
|
||||
result = -arg1;
|
||||
/* overflow check (needed for INT64_MIN) */
|
||||
if (arg1 != 0 && SAMESIGN(result, arg1))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
/* No overflow is possible */
|
||||
|
||||
result = arg1 / arg2;
|
||||
|
||||
/*
|
||||
* Overflow check. The only possible overflow case is for arg1 =
|
||||
* INT64_MIN, arg2 = -1, where the correct result is -INT64_MIN, which
|
||||
* can't be represented on a two's-complement machine. Most machines
|
||||
* produce INT64_MIN but it seems some produce zero.
|
||||
*/
|
||||
if (arg2 == -1 && arg1 < 0 && result <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user