Skip to content

Commit 4825a9e

Browse files
committed
Properly detect invalid JSON numbers when generating JSON.
Instead of looking for characters that aren't valid in JSON numbers, we simply pass the output string through the JSON number parser, and if it fails the string is quoted. This means among other things that money and domains over money will be quoted correctly and generate valid JSON. Fixes bug #8676 reported by Anderson Cristian da Silva. Backpatched to 9.2 where JSON generation was introduced.
1 parent 150a30e commit 4825a9e

File tree

1 file changed

+22
-14
lines changed

1 file changed

+22
-14
lines changed

src/backend/utils/adt/json.c

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ typedef enum /* required operations on state stack */
7373
static void json_validate_cstring(char *input);
7474
static void json_lex(JsonLexContext *lex);
7575
static void json_lex_string(JsonLexContext *lex);
76-
static void json_lex_number(JsonLexContext *lex, char *s);
76+
static void json_lex_number(JsonLexContext *lex, char *s, bool *num_err);
7777
static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
7878
static void report_invalid_token(JsonLexContext *lex);
7979
static int report_json_context(JsonLexContext *lex);
@@ -89,8 +89,6 @@ static void array_to_json_internal(Datum array, StringInfo result,
8989

9090
/* fake type category for JSON so we can distinguish it in datum_to_json */
9191
#define TYPCATEGORY_JSON 'j'
92-
/* letters appearing in numeric output that aren't valid in a JSON number */
93-
#define NON_NUMERIC_LETTER "NnAaIiFfTtYy"
9492
/* chars to consider as part of an alphanumeric token */
9593
#define JSON_ALPHANUMERIC_CHAR(c) \
9694
(((c) >= 'a' && (c) <= 'z') || \
@@ -361,13 +359,13 @@ json_lex(JsonLexContext *lex)
361359
else if (*s == '-')
362360
{
363361
/* Negative number. */
364-
json_lex_number(lex, s + 1);
362+
json_lex_number(lex, s + 1, NULL);
365363
lex->token_type = JSON_VALUE_NUMBER;
366364
}
367365
else if (*s >= '0' && *s <= '9')
368366
{
369367
/* Positive number. */
370-
json_lex_number(lex, s);
368+
json_lex_number(lex, s, NULL);
371369
lex->token_type = JSON_VALUE_NUMBER;
372370
}
373371
else
@@ -530,7 +528,7 @@ json_lex_string(JsonLexContext *lex)
530528
*-------------------------------------------------------------------------
531529
*/
532530
static void
533-
json_lex_number(JsonLexContext *lex, char *s)
531+
json_lex_number(JsonLexContext *lex, char *s, bool *num_err)
534532
{
535533
bool error = false;
536534
char *p;
@@ -584,15 +582,24 @@ json_lex_number(JsonLexContext *lex, char *s)
584582
}
585583

586584
/*
587-
* Check for trailing garbage. As in json_lex(), any alphanumeric stuff
585+
* Check for trailing garbage. As in json_lex(), any alphanumeric stuff
588586
* here should be considered part of the token for error-reporting
589587
* purposes.
590588
*/
591589
for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
592590
error = true;
593-
lex->token_terminator = p;
594-
if (error)
595-
report_invalid_token(lex);
591+
592+
if (num_err != NULL)
593+
{
594+
/* let the caller handle the error */
595+
*num_err = error;
596+
}
597+
else
598+
{
599+
lex->token_terminator = p;
600+
if (error)
601+
report_invalid_token(lex);
602+
}
596603
}
597604

598605
/*
@@ -819,6 +826,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
819826
TYPCATEGORY tcategory, Oid typoutputfunc)
820827
{
821828
char *outputstr;
829+
bool numeric_error;
830+
JsonLexContext dummy_lex;
822831

823832
if (is_null)
824833
{
@@ -845,11 +854,10 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
845854

846855
/*
847856
* Don't call escape_json here if it's a valid JSON number.
848-
* Numeric output should usually be a valid JSON number and JSON
849-
* numbers shouldn't be quoted. Quote cases like "Nan" and
850-
* "Infinity", however.
851857
*/
852-
if (strpbrk(outputstr, NON_NUMERIC_LETTER) == NULL)
858+
dummy_lex.input = *outputstr == '-' ? outputstr + 1 : outputstr;
859+
json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
860+
if (!numeric_error)
853861
appendStringInfoString(result, outputstr);
854862
else
855863
escape_json(result, outputstr);

0 commit comments

Comments
 (0)