#include "access/xact.h"
#include "catalog/pg_type.h"
+#include "common/int.h"
#include "common/int128.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
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;
/*
*/
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
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;
(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().
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 */
}
| @ 5 mons 12 hours
(10 rows)
+-- multiplication and division overflow test cases
+SELECT '3000000 months'::interval * 1000;
+ERROR: interval out of range
+SELECT '3000000 months'::interval / 0.001;
+ERROR: interval out of range
+SELECT '3000000 days'::interval * 1000;
+ERROR: interval out of range
+SELECT '3000000 days'::interval / 0.001;
+ERROR: interval out of range
+SELECT '1 month 2146410 days'::interval * 1000.5002;
+ERROR: interval out of range
+SELECT make_interval(0, 0, 0, 0, 0, 0, 4611686018427.387904) / 0.1;
+ERROR: interval out of range
-- test avg(interval), which is somewhat fragile since people have been
-- known to change the allowed input syntax for type interval without
-- updating pg_aggregate.agginitval
SELECT '' AS ten, * FROM INTERVAL_TBL;
+-- multiplication and division overflow test cases
+SELECT '3000000 months'::interval * 1000;
+SELECT '3000000 months'::interval / 0.001;
+SELECT '3000000 days'::interval * 1000;
+SELECT '3000000 days'::interval / 0.001;
+SELECT '1 month 2146410 days'::interval * 1000.5002;
+SELECT make_interval(0, 0, 0, 0, 0, 0, 4611686018427.387904) / 0.1;
+
-- test avg(interval), which is somewhat fragile since people have been
-- known to change the allowed input syntax for type interval without
-- updating pg_aggregate.agginitval