diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index f194ff911b..a35cd63e56 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -641,30 +641,19 @@ 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->last_linestart; context_end = lex->token_terminator; line_start = context_start; - line_number = 1; - for (;;) + + /* Done as soon as we are close enough to context_end */ + while (context_end - context_start >= 50 && context_start < context_end) { - /* Always advance over newlines */ - if (context_start < context_end && *context_start == '\n') - { - context_start++; - line_start = context_start; - line_number++; - continue; - } - /* Otherwise, done as soon as we are close enough to context_end */ - if (context_end - context_start < 50) - break; /* Advance to next multibyte character */ if (IS_HIGHBIT_SET(*context_start)) context_start += pg_mblen(context_start); @@ -694,7 +683,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/common/jsonapi.c b/src/common/jsonapi.c index 831a44a2da..049785c264 100644 --- a/src/common/jsonapi.c +++ b/src/common/jsonapi.c @@ -158,6 +158,7 @@ makeJsonLexContextCstringLen(char *json, int len, int encoding, bool need_escape lex->input = lex->token_terminator = lex->line_start = json; lex->line_number = 1; + lex->last_linestart = 0; lex->input_length = len; lex->input_encoding = encoding; if (need_escapes) @@ -536,7 +537,10 @@ json_lex(JsonLexContext *lex) (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')) { if (*s == '\n') + { ++lex->line_number; + lex->last_linestart = len + 1; + } ++s; ++len; } diff --git a/src/include/common/jsonapi.h b/src/include/common/jsonapi.h index 03331f6d13..6e4cb12275 100644 --- a/src/include/common/jsonapi.h +++ b/src/include/common/jsonapi.h @@ -79,6 +79,7 @@ typedef struct JsonLexContext char *prev_token_terminator; JsonTokenType token_type; int lex_level; + int last_linestart; 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 c4156cf2a6..ff15acf3c0 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 20354f04e3..e15697c95a 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