postgres.git / summary / log / commit / refs

commit    07ea925e20ee80898638800c8479b48e7e97d431
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Wed Sep 27 21:05:53 2017 +0000

    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


src/backend/utils/adt/numeric.c | 14 ++++++++++++-- src/test/regress/expected/numeric.out | 21 +++++++++++++++++++++ src/test/regress/sql/numeric.sql | 8 ++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 3e5614ece30..7dc0df52634 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -3073,7 +3073,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); @@ -3139,7 +3144,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 ae0beb9b68f..7e55b0e2931 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 b51225c47f3..9675b6eabf3 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'); [parent: 93a1af0b3f63]