diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index 0425ac6242f0dee2dc7ca940670b87b4e0107bde..1df3c2bb6f081d74aab05f5a89166eae2e935c57 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -73,7 +73,7 @@ typedef enum /* required operations on state stack */ static void json_validate_cstring(char *input); static void json_lex(JsonLexContext *lex); static void json_lex_string(JsonLexContext *lex); -static void json_lex_number(JsonLexContext *lex, char *s); +static void json_lex_number(JsonLexContext *lex, char *s, bool *num_err); static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex); static void report_invalid_token(JsonLexContext *lex); static int report_json_context(JsonLexContext *lex); @@ -89,8 +89,6 @@ static void array_to_json_internal(Datum array, StringInfo result, /* fake type category for JSON so we can distinguish it in datum_to_json */ #define TYPCATEGORY_JSON 'j' -/* letters appearing in numeric output that aren't valid in a JSON number */ -#define NON_NUMERIC_LETTER "NnAaIiFfTtYy" /* chars to consider as part of an alphanumeric token */ #define JSON_ALPHANUMERIC_CHAR(c) \ (((c) >= 'a' && (c) <= 'z') || \ @@ -361,13 +359,13 @@ json_lex(JsonLexContext *lex) else if (*s == '-') { /* Negative number. */ - json_lex_number(lex, s + 1); + json_lex_number(lex, s + 1, NULL); lex->token_type = JSON_VALUE_NUMBER; } else if (*s >= '0' && *s <= '9') { /* Positive number. */ - json_lex_number(lex, s); + json_lex_number(lex, s, NULL); lex->token_type = JSON_VALUE_NUMBER; } else @@ -530,7 +528,7 @@ json_lex_string(JsonLexContext *lex) *------------------------------------------------------------------------- */ static void -json_lex_number(JsonLexContext *lex, char *s) +json_lex_number(JsonLexContext *lex, char *s, bool *num_err) { bool error = false; char *p; @@ -584,15 +582,24 @@ json_lex_number(JsonLexContext *lex, char *s) } /* - * Check for trailing garbage. As in json_lex(), any alphanumeric stuff + * Check for trailing garbage. As in json_lex(), any alphanumeric stuff * here should be considered part of the token for error-reporting * purposes. */ for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++) error = true; - lex->token_terminator = p; - if (error) - report_invalid_token(lex); + + if (num_err != NULL) + { + /* let the caller handle the error */ + *num_err = error; + } + else + { + lex->token_terminator = p; + if (error) + report_invalid_token(lex); + } } /* @@ -819,6 +826,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result, TYPCATEGORY tcategory, Oid typoutputfunc) { char *outputstr; + bool numeric_error; + JsonLexContext dummy_lex; if (is_null) { @@ -845,11 +854,10 @@ datum_to_json(Datum val, bool is_null, StringInfo result, /* * Don't call escape_json here if it's a valid JSON number. - * Numeric output should usually be a valid JSON number and JSON - * numbers shouldn't be quoted. Quote cases like "Nan" and - * "Infinity", however. */ - if (strpbrk(outputstr, NON_NUMERIC_LETTER) == NULL) + dummy_lex.input = *outputstr == '-' ? outputstr + 1 : outputstr; + json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error); + if (!numeric_error) appendStringInfoString(result, outputstr); else escape_json(result, outputstr);