From 4f25b7bb416f0268e40cbd909dba9c898c261eff Mon Sep 17 00:00:00 2001 From: Ewan Young Date: Thu, 27 Aug 2026 18:06:09 +0800 Subject: [PATCH 1/2] Use pg_neg_s{16,32,64}_overflow() for open-coded negation overflow checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several integer, bigint, money and interval-input functions guard the negation of the most negative value by open-coding an explicit "x == PG_INTnn_MIN" test before computing -x. common/int.h already provides pg_neg_s16_overflow(), pg_neg_s32_overflow() and pg_neg_s64_overflow() for exactly this, which use __builtin_sub_overflow() where available and fall back to the same open-coded test otherwise. Convert the hand-rolled checks to use these helpers, as suggested by Dagfinn Ilmari Mannsåker. This is a cleanup with no behavioral change: every converted site keeps its existing error (or soft-error) path, and on platforms without __builtin_sub_overflow() the inlined fallback is identical to the code being replaced. Discussion: https://postgr.es/m/CAON2xHO4tTFiow2KKLbpiOEJNL4Th5p1QVTm4mSdEU+KW1rrfQ@mail.gmail.com --- src/backend/utils/adt/cash.c | 9 +++--- src/backend/utils/adt/datetime.c | 17 ++++------- src/backend/utils/adt/int.c | 52 +++++++++++++++++--------------- src/backend/utils/adt/int8.c | 31 +++++++++---------- src/backend/utils/adt/numeric.c | 3 +- 5 files changed, 54 insertions(+), 58 deletions(-) diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index 310b3bb3fca..0820187b8e6 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -156,6 +156,8 @@ cash_mul_int64(Cash c, int64 i) static inline Cash cash_div_int64(Cash c, int64 i) { + Cash res; + if (unlikely(i == 0)) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), @@ -169,11 +171,11 @@ cash_div_int64(Cash c, int64 i) */ if (i == -1) { - if (unlikely(c == PG_INT64_MIN)) + if (pg_neg_s64_overflow(c, &res)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("money out of range"))); - return -c; + return res; } /* No overflow is possible */ @@ -379,12 +381,11 @@ cash_in(PG_FUNCTION_ARGS) */ if (sgn > 0) { - if (value == PG_INT64_MIN) + if (pg_neg_s64_overflow(value, &result)) ereturn(escontext, (Datum) 0, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value \"%s\" is out of range for type %s", str, "money"))); - result = -value; } else result = value; diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 04ebc632178..32be8480dda 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -3595,9 +3595,9 @@ DecodeInterval(char **field, int *ftype, int nf, int range, if (*field[i] == '-') { /* flip the sign on time field */ - if (itm_in->tm_usec == PG_INT64_MIN) + if (pg_neg_s64_overflow(itm_in->tm_usec, + &itm_in->tm_usec)) return DTERR_FIELD_OVERFLOW; - itm_in->tm_usec = -itm_in->tm_usec; } if (force_negative && @@ -3880,16 +3880,11 @@ DecodeInterval(char **field, int *ftype, int nf, int range, /* finally, AGO negates everything */ if (is_before) { - if (itm_in->tm_usec == PG_INT64_MIN || - itm_in->tm_mday == INT_MIN || - itm_in->tm_mon == INT_MIN || - itm_in->tm_year == INT_MIN) + if (pg_neg_s64_overflow(itm_in->tm_usec, &itm_in->tm_usec) || + pg_neg_s32_overflow(itm_in->tm_mday, &itm_in->tm_mday) || + pg_neg_s32_overflow(itm_in->tm_mon, &itm_in->tm_mon) || + pg_neg_s32_overflow(itm_in->tm_year, &itm_in->tm_year)) return DTERR_FIELD_OVERFLOW; - - itm_in->tm_usec = -itm_in->tm_usec; - itm_in->tm_mday = -itm_in->tm_mday; - itm_in->tm_mon = -itm_in->tm_mon; - itm_in->tm_year = -itm_in->tm_year; } return 0; diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 01608d8ca42..8e2fdbe2be0 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -800,12 +800,13 @@ Datum int4um(PG_FUNCTION_ARGS) { int32 arg = PG_GETARG_INT32(0); + int32 result; - if (unlikely(arg == PG_INT32_MIN)) + if (pg_neg_s32_overflow(arg, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); - PG_RETURN_INT32(-arg); + PG_RETURN_INT32(result); } Datum @@ -882,11 +883,10 @@ int4div(PG_FUNCTION_ARGS) */ if (arg2 == -1) { - if (unlikely(arg1 == PG_INT32_MIN)) + if (pg_neg_s32_overflow(arg1, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); - result = -arg1; PG_RETURN_INT32(result); } @@ -915,12 +915,13 @@ Datum int2um(PG_FUNCTION_ARGS) { int16 arg = PG_GETARG_INT16(0); + int16 result; - if (unlikely(arg == PG_INT16_MIN)) + if (pg_neg_s16_overflow(arg, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); - PG_RETURN_INT16(-arg); + PG_RETURN_INT16(result); } Datum @@ -998,11 +999,10 @@ int2div(PG_FUNCTION_ARGS) */ if (arg2 == -1) { - if (unlikely(arg1 == PG_INT16_MIN)) + if (pg_neg_s16_overflow(arg1, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); - result = -arg1; PG_RETURN_INT16(result); } @@ -1140,11 +1140,10 @@ int42div(PG_FUNCTION_ARGS) */ if (arg2 == -1) { - if (unlikely(arg1 == PG_INT32_MIN)) + if (pg_neg_s32_overflow(arg1, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); - result = -arg1; PG_RETURN_INT32(result); } @@ -1223,11 +1222,15 @@ int4abs(PG_FUNCTION_ARGS) int32 arg1 = PG_GETARG_INT32(0); int32 result; - if (unlikely(arg1 == PG_INT32_MIN)) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); - result = (arg1 < 0) ? -arg1 : arg1; + if (arg1 < 0) + { + if (pg_neg_s32_overflow(arg1, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + } + else + result = arg1; PG_RETURN_INT32(result); } @@ -1237,11 +1240,15 @@ int2abs(PG_FUNCTION_ARGS) int16 arg1 = PG_GETARG_INT16(0); int16 result; - if (unlikely(arg1 == PG_INT16_MIN)) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("smallint out of range"))); - result = (arg1 < 0) ? -arg1 : arg1; + if (arg1 < 0) + { + if (pg_neg_s16_overflow(arg1, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + } + else + result = arg1; PG_RETURN_INT16(result); } @@ -1361,14 +1368,11 @@ int4lcm(PG_FUNCTION_ARGS) errmsg("integer out of range"))); /* If the result is INT_MIN, it cannot be represented. */ - if (unlikely(result == PG_INT32_MIN)) + if (result < 0 && pg_neg_s32_overflow(result, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); - if (result < 0) - result = -result; - PG_RETURN_INT32(result); } diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 1f59d831600..5a59de0f5c4 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -449,11 +449,10 @@ int8um(PG_FUNCTION_ARGS) int64 arg = PG_GETARG_INT64(0); int64 result; - if (unlikely(arg == PG_INT64_MIN)) + if (pg_neg_s64_overflow(arg, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); - result = -arg; PG_RETURN_INT64(result); } @@ -531,11 +530,10 @@ int8div(PG_FUNCTION_ARGS) */ if (arg2 == -1) { - if (unlikely(arg1 == PG_INT64_MIN)) + if (pg_neg_s64_overflow(arg1, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); - result = -arg1; PG_RETURN_INT64(result); } @@ -556,11 +554,15 @@ int8abs(PG_FUNCTION_ARGS) int64 arg1 = PG_GETARG_INT64(0); int64 result; - if (unlikely(arg1 == PG_INT64_MIN)) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("bigint out of range"))); - result = (arg1 < 0) ? -arg1 : arg1; + if (arg1 < 0) + { + if (pg_neg_s64_overflow(arg1, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + } + else + result = arg1; PG_RETURN_INT64(result); } @@ -713,14 +715,11 @@ int8lcm(PG_FUNCTION_ARGS) errmsg("bigint out of range"))); /* If the result is INT64_MIN, it cannot be represented. */ - if (unlikely(result == PG_INT64_MIN)) + if (result < 0 && pg_neg_s64_overflow(result, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); - if (result < 0) - result = -result; - PG_RETURN_INT64(result); } @@ -991,11 +990,10 @@ int84div(PG_FUNCTION_ARGS) */ if (arg2 == -1) { - if (unlikely(arg1 == PG_INT64_MIN)) + if (pg_neg_s64_overflow(arg1, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); - result = -arg1; PG_RETURN_INT64(result); } @@ -1133,11 +1131,10 @@ int82div(PG_FUNCTION_ARGS) */ if (arg2 == -1) { - if (unlikely(arg1 == PG_INT64_MIN)) + if (pg_neg_s64_overflow(arg1, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); - result = -arg1; PG_RETURN_INT64(result); } diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 523cd3cd608..37f24e33857 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -7870,9 +7870,8 @@ numericvar_to_int64(const NumericVar *var, int64 *result) if (!neg) { - if (unlikely(val == PG_INT64_MIN)) + if (unlikely(pg_neg_s64_overflow(val, &val))) return false; - val = -val; } *result = val; -- 2.47.3