From 6084c0705ae03fa7641369e33a2c487585b8f942 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 3 Aug 2013 12:39:57 -0400 Subject: [PATCH] Make sure float4in/float8in accept all standard spellings of "infinity". The C99 and POSIX standards require strtod() to accept all these spellings (case-insensitively): "inf", "+inf", "-inf", "infinity", "+infinity", "-infinity". However, pre-C99 systems might accept only some or none of these, and apparently Windows still doesn't accept "inf". To avoid surprising cross-platform behavioral differences, manually check for each of these spellings if strtod() fails. We were previously handling just "infinity" and "-infinity" that way, but since C99 is most of the world now, it seems likely that applications are expecting all these spellings to work. Per bug #8355 from Basil Peace. It turns out this fix won't actually resolve his problem, because Python isn't being this careful; but that doesn't mean we shouldn't be. --- src/backend/utils/adt/float.c | 106 ++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 25 deletions(-) diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index e3c84fd6c5..b929abeba7 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -175,11 +175,7 @@ is_infinite(double val) /* - * float4in - converts "num" to float - * restricted syntax: - * {} [+|-] {digit} [.{digit}] [] - * where is a space, digit is 0-9, - * is "e" or "E" followed by an integer. + * float4in - converts "num" to float4 */ Datum float4in(PG_FUNCTION_ARGS) @@ -196,6 +192,10 @@ float4in(PG_FUNCTION_ARGS) */ orig_num = num; + /* skip leading whitespace */ + while (*num != '\0' && isspace((unsigned char) *num)) + num++; + /* * Check for an empty-string input to begin with, to avoid the vagaries of * strtod() on different platforms. @@ -206,20 +206,23 @@ float4in(PG_FUNCTION_ARGS) errmsg("invalid input syntax for type real: \"%s\"", orig_num))); - /* skip leading whitespace */ - while (*num != '\0' && isspace((unsigned char) *num)) - num++; - errno = 0; val = strtod(num, &endptr); /* did we not see anything that looks like a double? */ if (endptr == num || errno != 0) { + int save_errno = errno; + /* - * C99 requires that strtod() accept NaN and [-]Infinity, but not all - * platforms support that yet (and some accept them but set ERANGE - * anyway...) Therefore, we check for these inputs ourselves. + * C99 requires that strtod() accept NaN, [+-]Infinity, and [+-]Inf, + * but not all platforms support all of these (and some accept them + * but set ERANGE anyway...) Therefore, we check for these inputs + * ourselves if strtod() fails. + * + * Note: C99 also requires hexadecimal input as well as some extended + * forms of NaN, but we consider these forms unportable and don't try + * to support them. You can use 'em if your strtod() takes 'em. */ if (pg_strncasecmp(num, "NaN", 3) == 0) { @@ -231,12 +234,32 @@ float4in(PG_FUNCTION_ARGS) val = get_float4_infinity(); endptr = num + 8; } + else if (pg_strncasecmp(num, "+Infinity", 9) == 0) + { + val = get_float4_infinity(); + endptr = num + 9; + } else if (pg_strncasecmp(num, "-Infinity", 9) == 0) { val = -get_float4_infinity(); endptr = num + 9; } - else if (errno == ERANGE) + else if (pg_strncasecmp(num, "inf", 3) == 0) + { + val = get_float4_infinity(); + endptr = num + 3; + } + else if (pg_strncasecmp(num, "+inf", 4) == 0) + { + val = get_float4_infinity(); + endptr = num + 4; + } + else if (pg_strncasecmp(num, "-inf", 4) == 0) + { + val = -get_float4_infinity(); + endptr = num + 4; + } + else if (save_errno == ERANGE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("\"%s\" is out of range for type real", @@ -274,6 +297,11 @@ float4in(PG_FUNCTION_ARGS) val = get_float4_infinity(); endptr = num + 8; } + else if (pg_strncasecmp(num, "+Infinity", 9) == 0) + { + val = get_float4_infinity(); + endptr = num + 9; + } else if (pg_strncasecmp(num, "-Infinity", 9) == 0) { val = -get_float4_infinity(); @@ -369,10 +397,6 @@ float4send(PG_FUNCTION_ARGS) /* * float8in - converts "num" to float8 - * restricted syntax: - * {} [+|-] {digit} [.{digit}] [] - * where is a space, digit is 0-9, - * is "e" or "E" followed by an integer. */ Datum float8in(PG_FUNCTION_ARGS) @@ -389,6 +413,10 @@ float8in(PG_FUNCTION_ARGS) */ orig_num = num; + /* skip leading whitespace */ + while (*num != '\0' && isspace((unsigned char) *num)) + num++; + /* * Check for an empty-string input to begin with, to avoid the vagaries of * strtod() on different platforms. @@ -399,20 +427,23 @@ float8in(PG_FUNCTION_ARGS) errmsg("invalid input syntax for type double precision: \"%s\"", orig_num))); - /* skip leading whitespace */ - while (*num != '\0' && isspace((unsigned char) *num)) - num++; - errno = 0; val = strtod(num, &endptr); /* did we not see anything that looks like a double? */ if (endptr == num || errno != 0) { + int save_errno = errno; + /* - * C99 requires that strtod() accept NaN and [-]Infinity, but not all - * platforms support that yet (and some accept them but set ERANGE - * anyway...) Therefore, we check for these inputs ourselves. + * C99 requires that strtod() accept NaN, [+-]Infinity, and [+-]Inf, + * but not all platforms support all of these (and some accept them + * but set ERANGE anyway...) Therefore, we check for these inputs + * ourselves if strtod() fails. + * + * Note: C99 also requires hexadecimal input as well as some extended + * forms of NaN, but we consider these forms unportable and don't try + * to support them. You can use 'em if your strtod() takes 'em. */ if (pg_strncasecmp(num, "NaN", 3) == 0) { @@ -424,12 +455,32 @@ float8in(PG_FUNCTION_ARGS) val = get_float8_infinity(); endptr = num + 8; } + else if (pg_strncasecmp(num, "+Infinity", 9) == 0) + { + val = get_float8_infinity(); + endptr = num + 9; + } else if (pg_strncasecmp(num, "-Infinity", 9) == 0) { val = -get_float8_infinity(); endptr = num + 9; } - else if (errno == ERANGE) + else if (pg_strncasecmp(num, "inf", 3) == 0) + { + val = get_float8_infinity(); + endptr = num + 3; + } + else if (pg_strncasecmp(num, "+inf", 4) == 0) + { + val = get_float8_infinity(); + endptr = num + 4; + } + else if (pg_strncasecmp(num, "-inf", 4) == 0) + { + val = -get_float8_infinity(); + endptr = num + 4; + } + else if (save_errno == ERANGE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("\"%s\" is out of range for type double precision", @@ -467,6 +518,11 @@ float8in(PG_FUNCTION_ARGS) val = get_float8_infinity(); endptr = num + 8; } + else if (pg_strncasecmp(num, "+Infinity", 9) == 0) + { + val = get_float8_infinity(); + endptr = num + 9; + } else if (pg_strncasecmp(num, "-Infinity", 9) == 0) { val = -get_float8_infinity(); -- 2.40.0