]> granicus.if.org Git - postgresql/commitdiff
Fix behavior when converting a float infinity to numeric.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 27 Sep 2017 21:05:53 +0000 (17:05 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 27 Sep 2017 21:05:53 +0000 (17:05 -0400)
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

src/backend/utils/adt/numeric.c
src/test/regress/expected/numeric.out
src/test/regress/sql/numeric.sql

index 620226cea11d9e51f76d6f3a2f48179d7997e8f5..aa17e21aaf4a2b5b51f4410f3bfa2436a60b5ea5 100644 (file)
@@ -3023,7 +3023,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);
 
@@ -3085,7 +3090,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);
 
index f1f50560ee1edd8e40f1b3d0543a5348223f9471..35a0f9cd42ddc4813f9e53b8df25758e286e3998 100644 (file)
@@ -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');
index fc472187d873d9cbb9fb34e696b12289bb21c9ae..c2d73c2d0b9ae054ad4179ca2bd15569f8b723b2 100644 (file)
@@ -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');