Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e482dcb

Browse files
committed
Make selectivity routines cope gracefully with NaNs, infinities, and
NUMERIC values that are out of the range of 'double'. Per trouble report from Mike Quinn.
1 parent d1c6983 commit e482dcb

File tree

3 files changed

+53
-9
lines changed

3 files changed

+53
-9
lines changed

src/backend/utils/adt/numeric.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* 1998 Jan Wieck
77
*
8-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.44 2001/10/03 05:29:24 thomas Exp $
8+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.45 2001/10/13 23:32:33 tgl Exp $
99
*
1010
* ----------
1111
*/
@@ -1663,6 +1663,35 @@ numeric_float8(PG_FUNCTION_ARGS)
16631663
}
16641664

16651665

1666+
/* Convert numeric to float8; if out of range, return +/- HUGE_VAL */
1667+
Datum
1668+
numeric_float8_no_overflow(PG_FUNCTION_ARGS)
1669+
{
1670+
Numeric num = PG_GETARG_NUMERIC(0);
1671+
char *tmp;
1672+
double val;
1673+
char *endptr;
1674+
1675+
if (NUMERIC_IS_NAN(num))
1676+
PG_RETURN_FLOAT8(NAN);
1677+
1678+
tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1679+
NumericGetDatum(num)));
1680+
1681+
/* unlike float8in, we ignore ERANGE from strtod */
1682+
val = strtod(tmp, &endptr);
1683+
if (*endptr != '\0')
1684+
{
1685+
/* shouldn't happen ... */
1686+
elog(ERROR, "Bad float8 input format '%s'", tmp);
1687+
}
1688+
1689+
pfree(tmp);
1690+
1691+
PG_RETURN_FLOAT8(val);
1692+
}
1693+
1694+
16661695
Datum
16671696
float4_numeric(PG_FUNCTION_ARGS)
16681697
{

src/backend/utils/adt/selfuncs.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*
1717
* IDENTIFICATION
18-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.98 2001/10/03 18:25:59 tgl Exp $
18+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.99 2001/10/13 23:32:33 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -581,7 +581,18 @@ scalarineqsel(Query *root, Oid operator, bool isgt,
581581
else if (val >= high)
582582
binfrac = 1.0;
583583
else
584+
{
584585
binfrac = (val - low) / (high - low);
586+
/*
587+
* Watch out for the possibility that we got a NaN
588+
* or Infinity from the division. This can happen
589+
* despite the previous checks, if for example
590+
* "low" is -Infinity.
591+
*/
592+
if (isnan(binfrac) ||
593+
binfrac < 0.0 || binfrac > 1.0)
594+
binfrac = 0.5;
595+
}
585596
}
586597
else
587598
{
@@ -1665,8 +1676,8 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
16651676
* subroutines in pg_type.
16661677
*
16671678
* All numeric datatypes are simply converted to their equivalent
1668-
* "double" values. XXX what about NUMERIC values that are outside
1669-
* the range of "double"?
1679+
* "double" values. (NUMERIC values that are outside the range of "double"
1680+
* are clamped to +/- HUGE_VAL.)
16701681
*
16711682
* String datatypes are converted by convert_string_to_scalar(),
16721683
* which is explained below. The reason why this routine deals with
@@ -1677,8 +1688,9 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
16771688
*
16781689
* The several datatypes representing absolute times are all converted
16791690
* to Timestamp, which is actually a double, and then we just use that
1680-
* double value. Note this will give bad results for the various "special"
1681-
* values of Timestamp --- what can we do with those?
1691+
* double value. Note this will give correct results even for the "special"
1692+
* values of Timestamp, since those are chosen to compare correctly;
1693+
* see timestamp_cmp.
16821694
*
16831695
* The several datatypes representing relative times (intervals) are all
16841696
* converted to measurements expressed in seconds.
@@ -1793,8 +1805,10 @@ convert_numeric_to_scalar(Datum value, Oid typid)
17931805
case FLOAT8OID:
17941806
return (double) DatumGetFloat8(value);
17951807
case NUMERICOID:
1796-
return (double) DatumGetFloat8(DirectFunctionCall1(numeric_float8,
1797-
value));
1808+
/* Note: out-of-range values will be clamped to +-HUGE_VAL */
1809+
return (double)
1810+
DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow,
1811+
value));
17981812
case OIDOID:
17991813
case REGPROCOID:
18001814
/* we can treat OIDs as integers... */

src/include/utils/builtins.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: builtins.h,v 1.167 2001/10/13 16:34:08 tgl Exp $
10+
* $Id: builtins.h,v 1.168 2001/10/13 23:32:34 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -553,6 +553,7 @@ extern Datum int2_numeric(PG_FUNCTION_ARGS);
553553
extern Datum numeric_int2(PG_FUNCTION_ARGS);
554554
extern Datum float8_numeric(PG_FUNCTION_ARGS);
555555
extern Datum numeric_float8(PG_FUNCTION_ARGS);
556+
extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS);
556557
extern Datum float4_numeric(PG_FUNCTION_ARGS);
557558
extern Datum numeric_float4(PG_FUNCTION_ARGS);
558559
extern Datum numeric_accum(PG_FUNCTION_ARGS);

0 commit comments

Comments
 (0)