diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index 26d293709a..94589d0cdb 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -312,6 +312,7 @@ makeJsonLexContextCstringLen(char *json, int len, bool need_escapes) lex->input = lex->token_terminator = lex->line_start = json; lex->line_number = 1; + lex->pos_last_linefeed = 0; lex->input_length = len; if (need_escapes) lex->strval = makeStringInfo(); @@ -623,7 +624,10 @@ json_lex(JsonLexContext *lex) (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')) { if (*s == '\n') + { ++lex->line_number; + lex->pos_last_linefeed = len; + } ++s; ++len; } @@ -1264,17 +1268,15 @@ report_json_context(JsonLexContext *lex) const char *context_start; const char *context_end; const char *line_start; - int line_number; char *ctxt; int ctxtlen; const char *prefix; const char *suffix; /* Choose boundaries for the part of the input we will display */ - context_start = lex->input; + context_start = lex->input + lex->pos_last_linefeed; context_end = lex->token_terminator; line_start = context_start; - line_number = 1; for (;;) { /* Always advance over newlines */ @@ -1282,7 +1284,6 @@ report_json_context(JsonLexContext *lex) { context_start++; line_start = context_start; - line_number++; continue; } /* Otherwise, done as soon as we are close enough to context_end */ @@ -1317,7 +1318,7 @@ report_json_context(JsonLexContext *lex) suffix = (lex->token_type != JSON_TOKEN_END && context_end - lex->input < lex->input_length && *context_end != '\n' && *context_end != '\r') ? "..." : ""; return errcontext("JSON data, line %d: %s%s%s", - line_number, prefix, ctxt, suffix); + lex->line_number, prefix, ctxt, suffix); } /* diff --git a/src/include/utils/jsonapi.h b/src/include/utils/jsonapi.h index 5f4d479a7b..3b47fae7e7 100644 --- a/src/include/utils/jsonapi.h +++ b/src/include/utils/jsonapi.h @@ -57,6 +57,7 @@ typedef struct JsonLexContext char *prev_token_terminator; JsonTokenType token_type; int lex_level; + int pos_last_linefeed; int line_number; char *line_start; StringInfo strval; diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out index 69b0a6ee6d..b772db7ef8 100644 --- a/src/test/regress/expected/json.out +++ b/src/test/regress/expected/json.out @@ -272,6 +272,42 @@ LINE 1: SELECT ' '::json; ^ DETAIL: The input string ended unexpectedly. CONTEXT: JSON data, line 1: +-- Multi-line JSON input to check ERROR reporting +SELECT jsonb_pretty('{ + "one": 1, + "two":"two", + "three": + true}'::jsonb); -- OK + jsonb_pretty +------------------- + { + + "one": 1, + + "two": "two",+ + "three": true+ + } +(1 row) + +SELECT jsonb_pretty('{ + "one": 1, + "two":,"two", -- ERROR extraneous comma before field "two" value, line 3 + "three": + true}'::jsonb); +ERROR: invalid input syntax for type json +LINE 1: SELECT jsonb_pretty('{ + ^ +DETAIL: Expected JSON value, but found ",". +CONTEXT: JSON data, line 3: "two":,... +SELECT jsonb_pretty('{ + "one": 1, + "two":"two", + "three": + }'::jsonb); -- ERROR missing value for field "three", line 5 +ERROR: invalid input syntax for type json +LINE 1: SELECT jsonb_pretty('{ + ^ +DETAIL: Expected JSON value, but found "}". +CONTEXT: JSON data, line 5: } +-- ERROR is on line 5 because a LF \n is legal whitespace at end of line 4, so the missing syntax is on line 5 --constructors -- array_to_json SELECT array_to_json(array(select 1 as a)); diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql index e021664592..f7187a7a0b 100644 --- a/src/test/regress/sql/json.sql +++ b/src/test/regress/sql/json.sql @@ -59,6 +59,24 @@ SELECT 'trues'::json; -- ERROR, not a keyword SELECT ''::json; -- ERROR, no value SELECT ' '::json; -- ERROR, no value +-- Multi-line JSON input to check ERROR reporting +SELECT jsonb_pretty('{ + "one": 1, + "two":"two", + "three": + true}'::jsonb); -- OK +SELECT jsonb_pretty('{ + "one": 1, + "two":,"two", -- ERROR extraneous comma before field "two" value, line 3 + "three": + true}'::jsonb); +SELECT jsonb_pretty('{ + "one": 1, + "two":"two", + "three": + }'::jsonb); -- ERROR missing value for field "three", line 5 +-- ERROR is on line 5 because a LF \n is legal whitespace at end of line 4, so the missing syntax is on line 5 + --constructors -- array_to_json