From e0daf7fc3cc6489de2120f882ac2942f0c18ed82 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 8 Apr 2009 22:08:40 +0000 Subject: [PATCH] Allow leading and trailing spaces around NaN in numeric_in. Sam Mason, rewritten a bit by Tom. --- src/backend/utils/adt/numeric.c | 115 +++++++++++++++++--------- src/test/regress/expected/numeric.out | 10 +-- 2 files changed, 79 insertions(+), 46 deletions(-) diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 74b0b99acd..0fbca23175 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -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; } diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out index c2fe7070b5..a3b631aabe 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -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