Skip to content

Commit 6084c07

Browse files
committed
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.
1 parent c928dc9 commit 6084c07

File tree

1 file changed

+81
-25
lines changed

1 file changed

+81
-25
lines changed

src/backend/utils/adt/float.c

Lines changed: 81 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,7 @@ is_infinite(double val)
175175

176176

177177
/*
178-
* float4in - converts "num" to float
179-
* restricted syntax:
180-
* {<sp>} [+|-] {digit} [.{digit}] [<exp>]
181-
* where <sp> is a space, digit is 0-9,
182-
* <exp> is "e" or "E" followed by an integer.
178+
* float4in - converts "num" to float4
183179
*/
184180
Datum
185181
float4in(PG_FUNCTION_ARGS)
@@ -196,6 +192,10 @@ float4in(PG_FUNCTION_ARGS)
196192
*/
197193
orig_num = num;
198194

195+
/* skip leading whitespace */
196+
while (*num != '\0' && isspace((unsigned char) *num))
197+
num++;
198+
199199
/*
200200
* Check for an empty-string input to begin with, to avoid the vagaries of
201201
* strtod() on different platforms.
@@ -206,20 +206,23 @@ float4in(PG_FUNCTION_ARGS)
206206
errmsg("invalid input syntax for type real: \"%s\"",
207207
orig_num)));
208208

209-
/* skip leading whitespace */
210-
while (*num != '\0' && isspace((unsigned char) *num))
211-
num++;
212-
213209
errno = 0;
214210
val = strtod(num, &endptr);
215211

216212
/* did we not see anything that looks like a double? */
217213
if (endptr == num || errno != 0)
218214
{
215+
int save_errno = errno;
216+
219217
/*
220-
* C99 requires that strtod() accept NaN and [-]Infinity, but not all
221-
* platforms support that yet (and some accept them but set ERANGE
222-
* anyway...) Therefore, we check for these inputs ourselves.
218+
* C99 requires that strtod() accept NaN, [+-]Infinity, and [+-]Inf,
219+
* but not all platforms support all of these (and some accept them
220+
* but set ERANGE anyway...) Therefore, we check for these inputs
221+
* ourselves if strtod() fails.
222+
*
223+
* Note: C99 also requires hexadecimal input as well as some extended
224+
* forms of NaN, but we consider these forms unportable and don't try
225+
* to support them. You can use 'em if your strtod() takes 'em.
223226
*/
224227
if (pg_strncasecmp(num, "NaN", 3) == 0)
225228
{
@@ -231,12 +234,32 @@ float4in(PG_FUNCTION_ARGS)
231234
val = get_float4_infinity();
232235
endptr = num + 8;
233236
}
237+
else if (pg_strncasecmp(num, "+Infinity", 9) == 0)
238+
{
239+
val = get_float4_infinity();
240+
endptr = num + 9;
241+
}
234242
else if (pg_strncasecmp(num, "-Infinity", 9) == 0)
235243
{
236244
val = -get_float4_infinity();
237245
endptr = num + 9;
238246
}
239-
else if (errno == ERANGE)
247+
else if (pg_strncasecmp(num, "inf", 3) == 0)
248+
{
249+
val = get_float4_infinity();
250+
endptr = num + 3;
251+
}
252+
else if (pg_strncasecmp(num, "+inf", 4) == 0)
253+
{
254+
val = get_float4_infinity();
255+
endptr = num + 4;
256+
}
257+
else if (pg_strncasecmp(num, "-inf", 4) == 0)
258+
{
259+
val = -get_float4_infinity();
260+
endptr = num + 4;
261+
}
262+
else if (save_errno == ERANGE)
240263
ereport(ERROR,
241264
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
242265
errmsg("\"%s\" is out of range for type real",
@@ -274,6 +297,11 @@ float4in(PG_FUNCTION_ARGS)
274297
val = get_float4_infinity();
275298
endptr = num + 8;
276299
}
300+
else if (pg_strncasecmp(num, "+Infinity", 9) == 0)
301+
{
302+
val = get_float4_infinity();
303+
endptr = num + 9;
304+
}
277305
else if (pg_strncasecmp(num, "-Infinity", 9) == 0)
278306
{
279307
val = -get_float4_infinity();
@@ -369,10 +397,6 @@ float4send(PG_FUNCTION_ARGS)
369397

370398
/*
371399
* float8in - converts "num" to float8
372-
* restricted syntax:
373-
* {<sp>} [+|-] {digit} [.{digit}] [<exp>]
374-
* where <sp> is a space, digit is 0-9,
375-
* <exp> is "e" or "E" followed by an integer.
376400
*/
377401
Datum
378402
float8in(PG_FUNCTION_ARGS)
@@ -389,6 +413,10 @@ float8in(PG_FUNCTION_ARGS)
389413
*/
390414
orig_num = num;
391415

416+
/* skip leading whitespace */
417+
while (*num != '\0' && isspace((unsigned char) *num))
418+
num++;
419+
392420
/*
393421
* Check for an empty-string input to begin with, to avoid the vagaries of
394422
* strtod() on different platforms.
@@ -399,20 +427,23 @@ float8in(PG_FUNCTION_ARGS)
399427
errmsg("invalid input syntax for type double precision: \"%s\"",
400428
orig_num)));
401429

402-
/* skip leading whitespace */
403-
while (*num != '\0' && isspace((unsigned char) *num))
404-
num++;
405-
406430
errno = 0;
407431
val = strtod(num, &endptr);
408432

409433
/* did we not see anything that looks like a double? */
410434
if (endptr == num || errno != 0)
411435
{
436+
int save_errno = errno;
437+
412438
/*
413-
* C99 requires that strtod() accept NaN and [-]Infinity, but not all
414-
* platforms support that yet (and some accept them but set ERANGE
415-
* anyway...) Therefore, we check for these inputs ourselves.
439+
* C99 requires that strtod() accept NaN, [+-]Infinity, and [+-]Inf,
440+
* but not all platforms support all of these (and some accept them
441+
* but set ERANGE anyway...) Therefore, we check for these inputs
442+
* ourselves if strtod() fails.
443+
*
444+
* Note: C99 also requires hexadecimal input as well as some extended
445+
* forms of NaN, but we consider these forms unportable and don't try
446+
* to support them. You can use 'em if your strtod() takes 'em.
416447
*/
417448
if (pg_strncasecmp(num, "NaN", 3) == 0)
418449
{
@@ -424,12 +455,32 @@ float8in(PG_FUNCTION_ARGS)
424455
val = get_float8_infinity();
425456
endptr = num + 8;
426457
}
458+
else if (pg_strncasecmp(num, "+Infinity", 9) == 0)
459+
{
460+
val = get_float8_infinity();
461+
endptr = num + 9;
462+
}
427463
else if (pg_strncasecmp(num, "-Infinity", 9) == 0)
428464
{
429465
val = -get_float8_infinity();
430466
endptr = num + 9;
431467
}
432-
else if (errno == ERANGE)
468+
else if (pg_strncasecmp(num, "inf", 3) == 0)
469+
{
470+
val = get_float8_infinity();
471+
endptr = num + 3;
472+
}
473+
else if (pg_strncasecmp(num, "+inf", 4) == 0)
474+
{
475+
val = get_float8_infinity();
476+
endptr = num + 4;
477+
}
478+
else if (pg_strncasecmp(num, "-inf", 4) == 0)
479+
{
480+
val = -get_float8_infinity();
481+
endptr = num + 4;
482+
}
483+
else if (save_errno == ERANGE)
433484
ereport(ERROR,
434485
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
435486
errmsg("\"%s\" is out of range for type double precision",
@@ -467,6 +518,11 @@ float8in(PG_FUNCTION_ARGS)
467518
val = get_float8_infinity();
468519
endptr = num + 8;
469520
}
521+
else if (pg_strncasecmp(num, "+Infinity", 9) == 0)
522+
{
523+
val = get_float8_infinity();
524+
endptr = num + 9;
525+
}
470526
else if (pg_strncasecmp(num, "-Infinity", 9) == 0)
471527
{
472528
val = -get_float8_infinity();

0 commit comments

Comments
 (0)