summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2001-10-13 23:32:34 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2001-10-13 23:32:34 +0000
commite482dcb0a438dfa1fcb2cb792730c00db337a834 (patch)
tree6474f00c50a7ccd98906635bd4ceabe8a5f909ab
parentd1c6983899df5b154c203c6f1ddf56914c0bbb30 (diff)
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.
-rw-r--r--src/backend/utils/adt/numeric.c31
-rw-r--r--src/backend/utils/adt/selfuncs.c28
-rw-r--r--src/include/utils/builtins.h3
3 files changed, 53 insertions, 9 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 797586018d0..5160f690c1e 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -5,7 +5,7 @@
*
* 1998 Jan Wieck
*
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.44 2001/10/03 05:29:24 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.45 2001/10/13 23:32:33 tgl Exp $
*
* ----------
*/
@@ -1663,6 +1663,35 @@ numeric_float8(PG_FUNCTION_ARGS)
}
+/* Convert numeric to float8; if out of range, return +/- HUGE_VAL */
+Datum
+numeric_float8_no_overflow(PG_FUNCTION_ARGS)
+{
+ Numeric num = PG_GETARG_NUMERIC(0);
+ char *tmp;
+ double val;
+ char *endptr;
+
+ if (NUMERIC_IS_NAN(num))
+ PG_RETURN_FLOAT8(NAN);
+
+ tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(num)));
+
+ /* unlike float8in, we ignore ERANGE from strtod */
+ val = strtod(tmp, &endptr);
+ if (*endptr != '\0')
+ {
+ /* shouldn't happen ... */
+ elog(ERROR, "Bad float8 input format '%s'", tmp);
+ }
+
+ pfree(tmp);
+
+ PG_RETURN_FLOAT8(val);
+}
+
+
Datum
float4_numeric(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 57c7e854614..306758ff77e 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.98 2001/10/03 18:25:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.99 2001/10/13 23:32:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -581,7 +581,18 @@ scalarineqsel(Query *root, Oid operator, bool isgt,
else if (val >= high)
binfrac = 1.0;
else
+ {
binfrac = (val - low) / (high - low);
+ /*
+ * Watch out for the possibility that we got a NaN
+ * or Infinity from the division. This can happen
+ * despite the previous checks, if for example
+ * "low" is -Infinity.
+ */
+ if (isnan(binfrac) ||
+ binfrac < 0.0 || binfrac > 1.0)
+ binfrac = 0.5;
+ }
}
else
{
@@ -1665,8 +1676,8 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
* subroutines in pg_type.
*
* All numeric datatypes are simply converted to their equivalent
- * "double" values. XXX what about NUMERIC values that are outside
- * the range of "double"?
+ * "double" values. (NUMERIC values that are outside the range of "double"
+ * are clamped to +/- HUGE_VAL.)
*
* String datatypes are converted by convert_string_to_scalar(),
* which is explained below. The reason why this routine deals with
@@ -1677,8 +1688,9 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
*
* The several datatypes representing absolute times are all converted
* to Timestamp, which is actually a double, and then we just use that
- * double value. Note this will give bad results for the various "special"
- * values of Timestamp --- what can we do with those?
+ * double value. Note this will give correct results even for the "special"
+ * values of Timestamp, since those are chosen to compare correctly;
+ * see timestamp_cmp.
*
* The several datatypes representing relative times (intervals) are all
* converted to measurements expressed in seconds.
@@ -1793,8 +1805,10 @@ convert_numeric_to_scalar(Datum value, Oid typid)
case FLOAT8OID:
return (double) DatumGetFloat8(value);
case NUMERICOID:
- return (double) DatumGetFloat8(DirectFunctionCall1(numeric_float8,
- value));
+ /* Note: out-of-range values will be clamped to +-HUGE_VAL */
+ return (double)
+ DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow,
+ value));
case OIDOID:
case REGPROCOID:
/* we can treat OIDs as integers... */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 12792f15288..36d85aa0421 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: builtins.h,v 1.167 2001/10/13 16:34:08 tgl Exp $
+ * $Id: builtins.h,v 1.168 2001/10/13 23:32:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -553,6 +553,7 @@ extern Datum int2_numeric(PG_FUNCTION_ARGS);
extern Datum numeric_int2(PG_FUNCTION_ARGS);
extern Datum float8_numeric(PG_FUNCTION_ARGS);
extern Datum numeric_float8(PG_FUNCTION_ARGS);
+extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS);
extern Datum float4_numeric(PG_FUNCTION_ARGS);
extern Datum numeric_float4(PG_FUNCTION_ARGS);
extern Datum numeric_accum(PG_FUNCTION_ARGS);