aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2017-09-27 17:05:53 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2017-09-27 17:05:53 -0400
commitad56dbd6c0b87e95fb1cf45025bb7dd8152a9f59 (patch)
treead01fd404fdc2bfdf825ed86cbb28010d7cf7075
parent6a21eb31b6deb0f76e8304fb9f12df272ed59f0c (diff)
downloadpostgresql-ad56dbd6c0b87e95fb1cf45025bb7dd8152a9f59.tar.gz
postgresql-ad56dbd6c0b87e95fb1cf45025bb7dd8152a9f59.zip
Fix behavior when converting a float infinity to numeric.
float8_numeric() and float4_numeric() failed to consider the possibility that the input is an IEEE infinity. The results depended on the platform-specific behavior of sprintf(): on most platforms you'd get something like ERROR: invalid input syntax for type numeric: "inf" but at least on Windows it's possible for the conversion to succeed and deliver a finite value (typically 1), due to a nonstandard output format from sprintf and lack of syntax error checking in these functions. Since our numeric type lacks the concept of infinity, a suitable conversion is impossible; the best thing to do is throw an explicit error before letting sprintf do its thing. While at it, let's use snprintf not sprintf. Overrunning the buffer should be impossible if sprintf does what it's supposed to, but this is cheap insurance against a stack smash if it doesn't. Problem reported by Taiki Kondo. Patch by me based on fix suggestion from KaiGai Kohei. Back-patch to all supported branches. Discussion: https://postgr.es/m/12A9442FBAE80D4E8953883E0B84E088C8C7A2@BPXM01GP.gisp.nec.co.jp
-rw-r--r--src/backend/utils/adt/numeric.c14
-rw-r--r--src/test/regress/expected/numeric.out21
-rw-r--r--src/test/regress/sql/numeric.sql8
3 files changed, 41 insertions, 2 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index f2826f4522c..f5de846ca51 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -3019,7 +3019,12 @@ float8_numeric(PG_FUNCTION_ARGS)
if (isnan(val))
PG_RETURN_NUMERIC(make_result(&const_nan));
- sprintf(buf, "%.*g", DBL_DIG, val);
+ if (isinf(val))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert infinity to numeric")));
+
+ snprintf(buf, sizeof(buf), "%.*g", DBL_DIG, val);
init_var(&result);
@@ -3081,7 +3086,12 @@ float4_numeric(PG_FUNCTION_ARGS)
if (isnan(val))
PG_RETURN_NUMERIC(make_result(&const_nan));
- sprintf(buf, "%.*g", FLT_DIG, val);
+ if (isinf(val))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert infinity to numeric")));
+
+ snprintf(buf, sizeof(buf), "%.*g", FLT_DIG, val);
init_var(&result);
diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out
index 394cc72b645..cc86706d25e 100644
--- a/src/test/regress/expected/numeric.out
+++ b/src/test/regress/expected/numeric.out
@@ -708,6 +708,27 @@ SELECT * FROM fract_only;
(6 rows)
DROP TABLE fract_only;
+-- Check inf/nan conversion behavior
+SELECT 'NaN'::float8::numeric;
+ numeric
+---------
+ NaN
+(1 row)
+
+SELECT 'Infinity'::float8::numeric;
+ERROR: cannot convert infinity to numeric
+SELECT '-Infinity'::float8::numeric;
+ERROR: cannot convert infinity to numeric
+SELECT 'NaN'::float4::numeric;
+ numeric
+---------
+ NaN
+(1 row)
+
+SELECT 'Infinity'::float4::numeric;
+ERROR: cannot convert infinity to numeric
+SELECT '-Infinity'::float4::numeric;
+ERROR: cannot convert infinity to numeric
-- Simple check that ceil(), floor(), and round() work correctly
CREATE TABLE ceil_floor_round (a numeric);
INSERT INTO ceil_floor_round VALUES ('-5.5');
diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql
index 5405ffadc79..c34dba29c29 100644
--- a/src/test/regress/sql/numeric.sql
+++ b/src/test/regress/sql/numeric.sql
@@ -655,6 +655,14 @@ INSERT INTO fract_only VALUES (8, '0.00017');
SELECT * FROM fract_only;
DROP TABLE fract_only;
+-- Check inf/nan conversion behavior
+SELECT 'NaN'::float8::numeric;
+SELECT 'Infinity'::float8::numeric;
+SELECT '-Infinity'::float8::numeric;
+SELECT 'NaN'::float4::numeric;
+SELECT 'Infinity'::float4::numeric;
+SELECT '-Infinity'::float4::numeric;
+
-- Simple check that ceil(), floor(), and round() work correctly
CREATE TABLE ceil_floor_round (a numeric);
INSERT INTO ceil_floor_round VALUES ('-5.5');