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.
This commit is contained in:
Tom Lane 2013-08-03 12:39:47 -04:00
parent 706f9dd914
commit 221e92f64c
1 changed files with 75 additions and 23 deletions

View File

@ -176,11 +176,7 @@ is_infinite(double val)
/*
* float4in - converts "num" to float
* restricted syntax:
* {<sp>} [+|-] {digit} [.{digit}] [<exp>]
* where <sp> is a space, digit is 0-9,
* <exp> is "e" or "E" followed by an integer.
* float4in - converts "num" to float4
*/
Datum
float4in(PG_FUNCTION_ARGS)
@ -197,6 +193,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.
@ -207,10 +207,6 @@ 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);
@ -220,9 +216,14 @@ float4in(PG_FUNCTION_ARGS)
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)
{
@ -234,11 +235,31 @@ 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 (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)
{
/*
@ -287,6 +308,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();
@ -382,10 +408,6 @@ float4send(PG_FUNCTION_ARGS)
/*
* float8in - converts "num" to float8
* restricted syntax:
* {<sp>} [+|-] {digit} [.{digit}] [<exp>]
* where <sp> is a space, digit is 0-9,
* <exp> is "e" or "E" followed by an integer.
*/
Datum
float8in(PG_FUNCTION_ARGS)
@ -402,6 +424,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.
@ -412,10 +438,6 @@ 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);
@ -425,9 +447,14 @@ float8in(PG_FUNCTION_ARGS)
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)
{
@ -439,11 +466,31 @@ 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 (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)
{
/*
@ -492,6 +539,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();