]> granicus.if.org Git - postgresql/commitdiff
Allow leading and trailing spaces around NaN in numeric_in.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 8 Apr 2009 22:08:40 +0000 (22:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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