diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 77a5d7d42f..47c786f126 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -1212,15 +1212,8 @@ dtoi4(PG_FUNCTION_ARGS) */ num = rint(num); - /* - * Range check. We must be careful here that the boundary values are - * expressed exactly in the float domain. We expect PG_INT32_MIN to be an - * exact power of 2, so it will be represented exactly; but PG_INT32_MAX - * isn't, and might get rounded off, so avoid using it. - */ - if (unlikely(num < (float8) PG_INT32_MIN || - num >= -((float8) PG_INT32_MIN) || - isnan(num))) + /* Range check */ + if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT32(num))) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); @@ -1244,15 +1237,8 @@ dtoi2(PG_FUNCTION_ARGS) */ num = rint(num); - /* - * Range check. We must be careful here that the boundary values are - * expressed exactly in the float domain. We expect PG_INT16_MIN to be an - * exact power of 2, so it will be represented exactly; but PG_INT16_MAX - * isn't, and might get rounded off, so avoid using it. - */ - if (unlikely(num < (float8) PG_INT16_MIN || - num >= -((float8) PG_INT16_MIN) || - isnan(num))) + /* Range check */ + if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT16(num))) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); @@ -1300,15 +1286,8 @@ ftoi4(PG_FUNCTION_ARGS) */ num = rint(num); - /* - * Range check. We must be careful here that the boundary values are - * expressed exactly in the float domain. We expect PG_INT32_MIN to be an - * exact power of 2, so it will be represented exactly; but PG_INT32_MAX - * isn't, and might get rounded off, so avoid using it. - */ - if (unlikely(num < (float4) PG_INT32_MIN || - num >= -((float4) PG_INT32_MIN) || - isnan(num))) + /* Range check */ + if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT32(num))) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); @@ -1332,15 +1311,8 @@ ftoi2(PG_FUNCTION_ARGS) */ num = rint(num); - /* - * Range check. We must be careful here that the boundary values are - * expressed exactly in the float domain. We expect PG_INT16_MIN to be an - * exact power of 2, so it will be represented exactly; but PG_INT16_MAX - * isn't, and might get rounded off, so avoid using it. - */ - if (unlikely(num < (float4) PG_INT16_MIN || - num >= -((float4) PG_INT16_MIN) || - isnan(num))) + /* Range check */ + if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT16(num))) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 0ff9394a2f..93acabce42 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -1216,15 +1216,8 @@ dtoi8(PG_FUNCTION_ARGS) */ num = rint(num); - /* - * Range check. We must be careful here that the boundary values are - * expressed exactly in the float domain. We expect PG_INT64_MIN to be an - * exact power of 2, so it will be represented exactly; but PG_INT64_MAX - * isn't, and might get rounded off, so avoid using it. - */ - if (unlikely(num < (float8) PG_INT64_MIN || - num >= -((float8) PG_INT64_MIN) || - isnan(num))) + /* Range check */ + if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT64(num))) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); @@ -1258,15 +1251,8 @@ ftoi8(PG_FUNCTION_ARGS) */ num = rint(num); - /* - * Range check. We must be careful here that the boundary values are - * expressed exactly in the float domain. We expect PG_INT64_MIN to be an - * exact power of 2, so it will be represented exactly; but PG_INT64_MAX - * isn't, and might get rounded off, so avoid using it. - */ - if (unlikely(num < (float4) PG_INT64_MIN || - num >= -((float4) PG_INT64_MIN) || - isnan(num))) + /* Range check */ + if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT64(num))) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 1dc4c820de..8c1572bb2a 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -3233,7 +3233,7 @@ interval_mul(PG_FUNCTION_ARGS) /* cascade units down */ result->day += (int32) month_remainder_days; result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC); - if (result_double > PG_INT64_MAX || result_double < PG_INT64_MIN) + if (!FLOAT8_FITS_IN_INT64(result_double)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 03bcd22996..3d697cf7f0 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -1676,7 +1676,7 @@ coerceToInt(PgBenchValue *pval, int64 *ival) { double dval = pval->u.dval; - if (dval < PG_INT64_MIN || PG_INT64_MAX < dval) + if (!FLOAT8_FITS_IN_INT64(dval)) { fprintf(stderr, "double to int overflow for %f\n", dval); return false; diff --git a/src/include/c.h b/src/include/c.h index d752cc07dc..359f2ce937 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -444,6 +444,19 @@ typedef unsigned PG_INT128_TYPE uint128 #define PG_INT64_MAX INT64CONST(0x7FFFFFFFFFFFFFFF) #define PG_UINT64_MAX UINT64CONST(0xFFFFFFFFFFFFFFFF) +#define FLOAT4_FITS_IN_INT16(num) \ + ((num) >= (float4) PG_INT16_MIN && (num) < -((float4) PG_INT16_MIN)) +#define FLOAT4_FITS_IN_INT32(num) \ + ((num) >= (float4) PG_INT32_MIN && (num) < -((float4) PG_INT32_MIN)) +#define FLOAT4_FITS_IN_INT64(num) \ + ((num) >= (float4) PG_INT64_MIN && (num) < -((float4) PG_INT64_MIN)) +#define FLOAT8_FITS_IN_INT16(num) \ + ((num) >= (float8) PG_INT16_MIN && (num) < -((float8) PG_INT16_MIN)) +#define FLOAT8_FITS_IN_INT32(num) \ + ((num) >= (float8) PG_INT32_MIN && (num) < -((float8) PG_INT32_MIN)) +#define FLOAT8_FITS_IN_INT64(num) \ + ((num) >= (float8) PG_INT64_MIN && (num) < -((float8) PG_INT64_MIN)) + /* Max value of size_t might also be missing if we don't have stdint.h */ #ifndef SIZE_MAX #if SIZEOF_SIZE_T == 8 diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index f88f34550a..ec81b95606 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -232,6 +232,10 @@ INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years'); ERROR: interval out of range LINE 1: INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years'... ^ +-- Test an interval whose epoch is not representable as 64 bits +-- The next should fail as out-of-range +select extract(epoch from '256 microseconds'::interval * (2^55)::float8); +ERROR: interval out of range SELECT r1.*, r2.* FROM INTERVAL_TBL_OF r1, INTERVAL_TBL_OF r2 WHERE r1.f1 > r2.f1 diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index bc5537d1b9..ddd414e2aa 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -73,6 +73,10 @@ INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483649 days'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483647 years'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years'); +-- Test an interval whose epoch is not representable as 64 bits +-- The next should fail as out-of-range +select extract(epoch from '256 microseconds'::interval * (2^55)::float8); + SELECT r1.*, r2.* FROM INTERVAL_TBL_OF r1, INTERVAL_TBL_OF r2 WHERE r1.f1 > r2.f1