diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2012-11-19 21:21:28 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2012-11-19 21:21:28 -0500 |
commit | 278b60598c648b677dbde1330f9f95676aa82e46 (patch) | |
tree | 105d7596e3838e9e6af346b77c0e3c5a8b7ee702 /src/backend/utils/adt/int8.c | |
parent | 83d48a81f8e25f16a133f56874f1bcdee7838841 (diff) | |
download | postgresql-278b60598c648b677dbde1330f9f95676aa82e46.tar.gz postgresql-278b60598c648b677dbde1330f9f95676aa82e46.zip |
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.
Back-patch of commit 1f7cb5c30983752ff8de833de30afcaee63536d0.
Per discussion with Xi Wang, though this is different from the patch he
submitted.
Diffstat (limited to 'src/backend/utils/adt/int8.c')
-rw-r--r-- | src/backend/utils/adt/int8.c | 90 |
1 files changed, 59 insertions, 31 deletions
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 59c110b0b30..c4cb1f2eff7 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -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(); } - 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. + * 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 && arg1 < 0 && result <= 0) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("bigint out of range"))); + 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; + PG_RETURN_INT64(result); } @@ -838,18 +848,27 @@ int84div(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } - 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. + * 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 && arg1 < 0 && result <= 0) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("bigint out of range"))); + 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; + PG_RETURN_INT64(result); } @@ -1026,18 +1045,27 @@ int82div(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } - 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. + * 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 && arg1 < 0 && result <= 0) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("bigint out of range"))); + 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; + PG_RETURN_INT64(result); } |