diff options
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 70 |
1 files changed, 49 insertions, 21 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 3e7c77ee48f..fbc6bad20d6 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -22,6 +22,7 @@ #include "access/xact.h" #include "catalog/pg_type.h" +#include "common/int.h" #include "common/int128.h" #include "funcapi.h" #include "libpq/pqformat.h" @@ -3189,19 +3190,13 @@ interval_mul(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); result_double = span->month * factor; - if (isnan(result_double) || - result_double > INT_MAX || result_double < INT_MIN) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); + if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double)) + goto out_of_range; result->month = (int32) result_double; result_double = span->day * factor; - if (isnan(result_double) || - result_double > INT_MAX || result_double < INT_MIN) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); + if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double)) + goto out_of_range; result->day = (int32) result_double; /* @@ -3235,20 +3230,30 @@ interval_mul(PG_FUNCTION_ARGS) */ if (Abs(sec_remainder) >= SECS_PER_DAY) { - result->day += (int) (sec_remainder / SECS_PER_DAY); + if (pg_add_s32_overflow(result->day, + (int) (sec_remainder / SECS_PER_DAY), + &result->day)) + goto out_of_range; sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY; } /* cascade units down */ - result->day += (int32) month_remainder_days; + if (pg_add_s32_overflow(result->day, (int32) month_remainder_days, + &result->day)) + goto out_of_range; result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC); if (isnan(result_double) || !FLOAT8_FITS_IN_INT64(result_double)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); + goto out_of_range; result->time = (int64) result_double; PG_RETURN_INTERVAL_P(result); + +out_of_range: + ereport(ERROR, + errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range")); + + PG_RETURN_NULL(); /* keep compiler quiet */ } Datum @@ -3267,7 +3272,8 @@ interval_div(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(0); float8 factor = PG_GETARG_FLOAT8(1); double month_remainder_days, - sec_remainder; + sec_remainder, + result_double; int32 orig_month = span->month, orig_day = span->day; Interval *result; @@ -3279,8 +3285,15 @@ interval_div(PG_FUNCTION_ARGS) (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - result->month = (int32) (span->month / factor); - result->day = (int32) (span->day / factor); + result_double = span->month / factor; + if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double)) + goto out_of_range; + result->month = (int32) result_double; + + result_double = span->day / factor; + if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double)) + goto out_of_range; + result->day = (int32) result_double; /* * Fractional months full days into days. See comment in interval_mul(). @@ -3292,15 +3305,30 @@ interval_div(PG_FUNCTION_ARGS) sec_remainder = TSROUND(sec_remainder); if (Abs(sec_remainder) >= SECS_PER_DAY) { - result->day += (int) (sec_remainder / SECS_PER_DAY); + if (pg_add_s32_overflow(result->day, + (int) (sec_remainder / SECS_PER_DAY), + &result->day)) + goto out_of_range; sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY; } /* cascade units down */ - result->day += (int32) month_remainder_days; - result->time = rint(span->time / factor + sec_remainder * USECS_PER_SEC); + if (pg_add_s32_overflow(result->day, (int32) month_remainder_days, + &result->day)) + goto out_of_range; + result_double = rint(span->time / factor + sec_remainder * USECS_PER_SEC); + if (isnan(result_double) || !FLOAT8_FITS_IN_INT64(result_double)) + goto out_of_range; + result->time = (int64) result_double; PG_RETURN_INTERVAL_P(result); + +out_of_range: + ereport(ERROR, + errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range")); + + PG_RETURN_NULL(); /* keep compiler quiet */ } |