summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/int8.c
diff options
context:
space:
mode:
authorTom Lane2018-11-24 01:57:11 +0000
committerTom Lane2018-11-24 01:57:11 +0000
commitcbdb8b4c0155b2d9679ecd79b886c29c2730b85d (patch)
treeb798c7243ff2601bdc4f99cc367470a603b89e19 /src/backend/utils/adt/int8.c
parenteb6f29141bed9dc95cb473614c30f470ef980705 (diff)
Fix float-to-integer coercions to handle edge cases correctly.
ftoi4 and its sibling coercion functions did their overflow checks in a way that looked superficially plausible, but actually depended on an assumption that the MIN and MAX comparison constants can be represented exactly in the float4 or float8 domain. That fails in ftoi4, ftoi8, and dtoi8, resulting in a possibility that values near the MAX limit will be wrongly converted (to negative values) when they need to be rejected. Also, because we compared before rounding off the fractional part, the other three functions threw errors for values that really ought to get rounded to the min or max integer value. Fix by doing rint() first (requiring an assumption that it handles NaN and Inf correctly; but dtoi8 and ftoi8 were assuming that already), and by comparing to values that should coerce to float exactly, namely INTxx_MIN and -INTxx_MIN. Also remove some random cosmetic discrepancies between these six functions. Per bug #15519 from Victor Petrovykh. This should get back-patched, but first let's see what the buildfarm thinks of it --- I'm not too sure about portability of some of the regression test cases. Patch by me; thanks to Andrew Gierth for analysis and discussion. Discussion: https://postgr.es/m/[email protected]
Diffstat (limited to 'src/backend/utils/adt/int8.c')
-rw-r--r--src/backend/utils/adt/int8.c52
1 files changed, 34 insertions, 18 deletions
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 3c595e800a4..437d41816a2 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1204,22 +1204,29 @@ i8tod(PG_FUNCTION_ARGS)
Datum
dtoi8(PG_FUNCTION_ARGS)
{
- float8 arg = PG_GETARG_FLOAT8(0);
- int64 result;
+ float8 num = PG_GETARG_FLOAT8(0);
- /* Round arg to nearest integer (but it's still in float form) */
- arg = rint(arg);
+ /*
+ * Get rid of any fractional part in the input. This is so we don't fail
+ * on just-out-of-range values that would round into range. Note
+ * assumption that rint() will pass through a NaN or Inf unchanged.
+ */
+ num = rint(num);
- if (unlikely(arg < (double) PG_INT64_MIN) ||
- unlikely(arg > (double) PG_INT64_MAX) ||
- unlikely(isnan(arg)))
+ /*
+ * 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)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
- result = (int64) arg;
-
- PG_RETURN_INT64(result);
+ PG_RETURN_INT64((int64) num);
}
Datum
@@ -1239,20 +1246,29 @@ i8tof(PG_FUNCTION_ARGS)
Datum
ftoi8(PG_FUNCTION_ARGS)
{
- float4 arg = PG_GETARG_FLOAT4(0);
- float8 darg;
+ float4 num = PG_GETARG_FLOAT4(0);
- /* Round arg to nearest integer (but it's still in float form) */
- darg = rint(arg);
+ /*
+ * Get rid of any fractional part in the input. This is so we don't fail
+ * on just-out-of-range values that would round into range. Note
+ * assumption that rint() will pass through a NaN or Inf unchanged.
+ */
+ num = rint(num);
- if (unlikely(arg < (float4) PG_INT64_MIN) ||
- unlikely(arg > (float4) PG_INT64_MAX) ||
- unlikely(isnan(arg)))
+ /*
+ * 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)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
- PG_RETURN_INT64((int64) darg);
+ PG_RETURN_INT64((int64) num);
}
Datum