Allow leading and trailing spaces around NaN in numeric_in.
authorTom Lane
Wed, 8 Apr 2009 22:08:40 +0000 (22:08 +0000)
committerTom Lane
Wed, 8 Apr 2009 22:08:40 +0000 (22:08 +0000)
Sam Mason, rewritten a bit by Tom.

src/backend/utils/adt/numeric.c
src/test/regress/expected/numeric.out

index 74b0b99acd0622146c3551658bfa448f3dd0d4f3..0fbca231756f4845d4242ba4b4c24e05e8025c7f 100644 (file)
@@ -14,7 +14,7 @@
  * Copyright (c) 1998-2009, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.116 2009/01/01 17:23:49 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.117 2009/04/08 22:08:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -242,7 +242,8 @@ static void alloc_var(NumericVar *var, int ndigits);
 static void free_var(NumericVar *var);
 static void zero_var(NumericVar *var);
 
-static void set_var_from_str(const char *str, NumericVar *dest);
+static const char *set_var_from_str(const char *str, const char *cp,
+                                   NumericVar *dest);
 static void set_var_from_num(Numeric value, NumericVar *dest);
 static void set_var_from_var(NumericVar *value, NumericVar *dest);
 static char *get_str_from_var(NumericVar *var, int dscale);
@@ -321,26 +322,69 @@ numeric_in(PG_FUNCTION_ARGS)
    Oid         typelem = PG_GETARG_OID(1);
 #endif
    int32       typmod = PG_GETARG_INT32(2);
-   NumericVar  value;
    Numeric     res;
+   const char *cp;
+
+   /* Skip leading spaces */
+   cp = str;
+   while (*cp)
+   {
+       if (!isspace((unsigned char) *cp))
+           break;
+       cp++;
+   }
 
    /*
     * Check for NaN
     */
-   if (pg_strcasecmp(str, "NaN") == 0)
-       PG_RETURN_NUMERIC(make_result(&const_nan));
+   if (pg_strncasecmp(cp, "NaN", 3) == 0)
+   {
+       res = make_result(&const_nan);
 
-   /*
-    * Use set_var_from_str() to parse the input string and return it in the
-    * packed DB storage format
-    */
-   init_var(&value);
-   set_var_from_str(str, &value);
+       /* Should be nothing left but spaces */
+       cp += 3;
+       while (*cp)
+       {
+           if (!isspace((unsigned char) *cp))
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                    errmsg("invalid input syntax for type numeric: \"%s\"",
+                           str)));
+           cp++;
+       }
+   }
+   else
+   {
+       /*
+        * Use set_var_from_str() to parse a normal numeric value
+        */
+       NumericVar  value;
 
-   apply_typmod(&value, typmod);
+       init_var(&value);
 
-   res = make_result(&value);
-   free_var(&value);
+       cp = set_var_from_str(str, cp, &value);
+
+       /*
+        * We duplicate a few lines of code here because we would like to
+        * throw any trailing-junk syntax error before any semantic error
+        * resulting from apply_typmod.  We can't easily fold the two
+        * cases together because we mustn't apply apply_typmod to a NaN.
+        */
+       while (*cp)
+       {
+           if (!isspace((unsigned char) *cp))
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                    errmsg("invalid input syntax for type numeric: \"%s\"",
+                           str)));
+           cp++;
+       }
+
+       apply_typmod(&value, typmod);
+
+       res = make_result(&value);
+       free_var(&value);
+   }
 
    PG_RETURN_NUMERIC(res);
 }
@@ -2121,7 +2165,9 @@ float8_numeric(PG_FUNCTION_ARGS)
 
    init_var(&result);
 
-   set_var_from_str(buf, &result);
+   /* Assume we need not worry about leading/trailing spaces */
+   (void) set_var_from_str(buf, buf, &result);
+
    res = make_result(&result);
 
    free_var(&result);
@@ -2181,7 +2227,9 @@ float4_numeric(PG_FUNCTION_ARGS)
 
    init_var(&result);
 
-   set_var_from_str(buf, &result);
+   /* Assume we need not worry about leading/trailing spaces */
+   (void) set_var_from_str(buf, buf, &result);
+
    res = make_result(&result);
 
    free_var(&result);
@@ -2972,11 +3020,17 @@ zero_var(NumericVar *var)
  * set_var_from_str()
  *
  * Parse a string and put the number into a variable
+ *
+ * This function does not handle leading or trailing spaces, and it doesn't
+ * accept "NaN" either.  It returns the end+1 position so that caller can
+ * check for trailing spaces/garbage if deemed necessary.
+ *
+ * cp is the place to actually start parsing; str is what to use in error
+ * reports.  (Typically cp would be the same except advanced over spaces.)
  */
-static void
-set_var_from_str(const char *str, NumericVar *dest)
+static const char *
+set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 {
-   const char *cp = str;
    bool        have_dp = FALSE;
    int         i;
    unsigned char *decdigits;
@@ -2993,15 +3047,6 @@ set_var_from_str(const char *str, NumericVar *dest)
     * We first parse the string to extract decimal digits and determine the
     * correct decimal weight.  Then convert to NBASE representation.
     */
-
-   /* skip leading spaces */
-   while (*cp)
-   {
-       if (!isspace((unsigned char) *cp))
-           break;
-       cp++;
-   }
-
    switch (*cp)
    {
        case '+':
@@ -3086,17 +3131,6 @@ set_var_from_str(const char *str, NumericVar *dest)
            dscale = 0;
    }
 
-   /* Should be nothing left but spaces */
-   while (*cp)
-   {
-       if (!isspace((unsigned char) *cp))
-           ereport(ERROR,
-                   (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                    errmsg("invalid input syntax for type numeric: \"%s\"",
-                           str)));
-       cp++;
-   }
-
    /*
     * Okay, convert pure-decimal representation to base NBASE.  First we need
     * to determine the converted weight and ndigits.  offset is the number of
@@ -3137,6 +3171,9 @@ set_var_from_str(const char *str, NumericVar *dest)
 
    /* Strip any leading/trailing zeroes, and normalize weight if zero */
    strip_var(dest);
+
+   /* Return end+1 position for caller */
+   return cp;
 }
 
 
index c2fe7070b50411ac667d5dabef558145e2654fbc..a3b631aabe0d642b0ac060f2261be861adc15395 100644 (file)
@@ -1230,13 +1230,7 @@ INSERT INTO num_input_test(n1) VALUES ('  -93853');
 INSERT INTO num_input_test(n1) VALUES ('555.50');
 INSERT INTO num_input_test(n1) VALUES ('-555.50');
 INSERT INTO num_input_test(n1) VALUES ('NaN ');
-ERROR:  invalid input syntax for type numeric: "NaN "
-LINE 1: INSERT INTO num_input_test(n1) VALUES ('NaN ');
-                                               ^
 INSERT INTO num_input_test(n1) VALUES ('        nan');
-ERROR:  invalid input syntax for type numeric: "        nan"
-LINE 1: INSERT INTO num_input_test(n1) VALUES ('        nan');
-                                               ^
 -- bad inputs
 INSERT INTO num_input_test(n1) VALUES ('     ');
 ERROR:  invalid input syntax for type numeric: "     "
@@ -1278,7 +1272,9 @@ SELECT * FROM num_input_test;
   -93853
   555.50
  -555.50
-(5 rows)
+     NaN
+     NaN
+(7 rows)
 
 --
 -- Test some corner cases for division