/* * For PostgreSQL Database Management System: * (formerly known as Postgres, then as Postgres95) * * Portions Copyright (c) 1996-2010, The PostgreSQL Global Development Group * * Portions Copyright (c) 1994, The Regents of the University of California * * Permission to use, copy, modify, and distribute this software and its documentation for any purpose, * without fee, and without a written agreement is hereby granted, provided that the above copyright notice * and this paragraph and the following two paragraphs appear in all copies. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA * HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #include "postgres.h" #include "utils/float.h" #include "utils/ag_float8_supp.h" /* * This is a copy of float8in_internal with a slight modification, it doesn't * error on bad input, it will set is_valid to false instead. * * float8in_internal_null - guts of float8in_null() * * 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. */ float8 float8in_internal_null(char *num, char **endptr_p, const char *type_name, const char *orig_string, bool *is_valid) { double val; char *endptr; *is_valid = false; /* 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. */ if (*num == '\0') return 0; errno = 0; val = strtod(num, &endptr); /* did we not see anything that looks like a double? */ if (endptr == num || errno != 0) { int save_errno = errno; /* * 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) { val = get_float8_nan(); endptr = num + 3; } else if (pg_strncasecmp(num, "Infinity", 8) == 0) { 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) { /* * Some platforms return ERANGE for denormalized numbers (those * that are not zero, but are too close to zero to have full * 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'; return 0; } } else return 0; } #ifdef HAVE_BUGGY_SOLARIS_STRTOD else { /* * Many versions of Solaris have a bug wherein strtod sets endptr to * point one byte beyond the end of the string when given "inf" or * "infinity". */ if (endptr != num && endptr[-1] == '\0') endptr--; } #endif /* HAVE_BUGGY_SOLARIS_STRTOD */ /* skip trailing whitespace */ while (*endptr != '\0' && isspace((unsigned char) *endptr)) endptr++; /* report stopping point if wanted, else complain if not end of string */ if (endptr_p) *endptr_p = endptr; else if (*endptr != '\0') return 0; *is_valid = true; return val; }