aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/float.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/float.c')
-rw-r--r--src/backend/utils/adt/float.c85
1 files changed, 63 insertions, 22 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index d4e5d553477..4ee93ac04e1 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -411,17 +411,35 @@ Datum
float8in(PG_FUNCTION_ARGS)
{
char *num = PG_GETARG_CSTRING(0);
- char *orig_num;
+
+ PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num));
+}
+
+/*
+ * float8in_internal - guts of float8in()
+ *
+ * This is exposed for use by functions that want a reasonably
+ * platform-independent way of inputting doubles. The behavior is
+ * essentially like strtod + ereport on error, but note the following
+ * differences:
+ * 1. Both leading and trailing whitespace are skipped.
+ * 2. If endptr_p is NULL, we throw error if there's trailing junk.
+ * Otherwise, it's up to the caller to complain about trailing junk.
+ * 3. In event of a syntax error, the report mentions the given type_name
+ * and prints orig_string as the input; this is meant to support use of
+ * this function with types such as "box" and "point", where what we are
+ * parsing here is just a substring of orig_string.
+ *
+ * "num" could validly be declared "const char *", but that results in an
+ * unreasonable amount of extra casting both here and in callers, so we don't.
+ */
+double
+float8in_internal(char *num, char **endptr_p,
+ const char *type_name, const char *orig_string)
+{
double val;
char *endptr;
- /*
- * endptr points to the first character _after_ the sequence we recognized
- * as a valid floating point number. orig_num points to the original input
- * string.
- */
- orig_num = num;
-
/* skip leading whitespace */
while (*num != '\0' && isspace((unsigned char) *num))
num++;
@@ -433,8 +451,8 @@ float8in(PG_FUNCTION_ARGS)
if (*num == '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type double precision: \"%s\"",
- orig_num)));
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ type_name, orig_string)));
errno = 0;
val = strtod(num, &endptr);
@@ -497,18 +515,27 @@ float8in(PG_FUNCTION_ARGS)
* precision). We'd prefer not to throw error for that, so try to
* detect whether it's a "real" out-of-range condition by checking
* to see if the result is zero or huge.
+ *
+ * On error, we intentionally complain about double precision not
+ * the given type name, and we print only the part of the string
+ * that is the current number.
*/
if (val == 0.0 || val >= HUGE_VAL || val <= -HUGE_VAL)
+ {
+ char *errnumber = pstrdup(num);
+
+ errnumber[endptr - num] = '\0';
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"%s\" is out of range for type double precision",
- orig_num)));
+ errnumber)));
+ }
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type double precision: \"%s\"",
- orig_num)));
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ type_name, orig_string)));
}
#ifdef HAVE_BUGGY_SOLARIS_STRTOD
else
@@ -527,16 +554,16 @@ float8in(PG_FUNCTION_ARGS)
while (*endptr != '\0' && isspace((unsigned char) *endptr))
endptr++;
- /* if there is any junk left at the end of the string, bail out */
- if (*endptr != '\0')
+ /* report stopping point if wanted, else complain if not end of string */
+ if (endptr_p)
+ *endptr_p = endptr;
+ else if (*endptr != '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type double precision: \"%s\"",
- orig_num)));
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ type_name, orig_string)));
- CHECKFLOATVAL(val, true, true);
-
- PG_RETURN_FLOAT8(val);
+ return val;
}
/*
@@ -547,10 +574,24 @@ Datum
float8out(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
+
+ PG_RETURN_CSTRING(float8out_internal(num));
+}
+
+/*
+ * float8out_internal - guts of float8out()
+ *
+ * This is exposed for use by functions that want a reasonably
+ * platform-independent way of outputting doubles.
+ * The result is always palloc'd.
+ */
+char *
+float8out_internal(double num)
+{
char *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1);
if (isnan(num))
- PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
+ return strcpy(ascii, "NaN");
switch (is_infinite(num))
{
@@ -571,7 +612,7 @@ float8out(PG_FUNCTION_ARGS)
}
}
- PG_RETURN_CSTRING(ascii);
+ return ascii;
}
/*