json-error-context-1.patch

text/x-patch

Filename: json-error-context-1.patch
Type: text/x-patch
Part: 0
Message: Re: [COMMITTERS] pgsql: Mark JSON error detail messages for translation.

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/utils/adt/json.c 0 0
src/test/regress/expected/json.out 0 0
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index e79c294..59335db 100644
*** a/src/backend/utils/adt/json.c
--- b/src/backend/utils/adt/json.c
*************** typedef struct					/* state of JSON lexe
*** 43,50 ****
  	char	   *token_start;	/* start of current token within input */
  	char	   *token_terminator; /* end of previous or current token */
  	JsonValueType token_type;	/* type of current token, once it's known */
- 	int			line_number;	/* current line number (counting from 1) */
- 	char	   *line_start;		/* start of current line within input (BROKEN!!) */
  } JsonLexContext;
  
  typedef enum					/* states of JSON parser */
--- 43,48 ----
*************** static void json_lex_string(JsonLexConte
*** 78,83 ****
--- 76,82 ----
  static void json_lex_number(JsonLexContext *lex, char *s);
  static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
  static void report_invalid_token(JsonLexContext *lex);
+ static int report_json_context(JsonLexContext *lex);
  static char *extract_mb_char(char *s);
  static void composite_to_json(Datum composite, StringInfo result,
  							  bool use_line_feeds);
*************** json_validate_cstring(char *input)
*** 185,192 ****
  	/* Set up lexing context. */
  	lex.input = input;
  	lex.token_terminator = lex.input;
- 	lex.line_number = 1;
- 	lex.line_start = input;
  
  	/* Set up parse stack. */
  	stacksize = 32;
--- 184,189 ----
*************** json_lex(JsonLexContext *lex)
*** 335,345 ****
  	/* Skip leading whitespace. */
  	s = lex->token_terminator;
  	while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
- 	{
- 		if (*s == '\n')
- 			lex->line_number++;
  		s++;
- 	}
  	lex->token_start = s;
  
  	/* Determine token type. */
--- 332,338 ----
*************** json_lex(JsonLexContext *lex)
*** 350,356 ****
  		{
  			/* End of string. */
  			lex->token_start = NULL;
! 			lex->token_terminator = NULL;
  		}
  		else
  		{
--- 343,349 ----
  		{
  			/* End of string. */
  			lex->token_start = NULL;
! 			lex->token_terminator = s;
  		}
  		else
  		{
*************** json_lex(JsonLexContext *lex)
*** 397,403 ****
  			/*
  			 * We got some sort of unexpected punctuation or an otherwise
  			 * unexpected character, so just complain about that one
! 			 * character.
  			 */
  			lex->token_terminator = s + 1;
  			report_invalid_token(lex);
--- 390,397 ----
  			/*
  			 * We got some sort of unexpected punctuation or an otherwise
  			 * unexpected character, so just complain about that one
! 			 * character.  (It can't be multibyte because the above loop
! 			 * will advance over any multibyte characters.)
  			 */
  			lex->token_terminator = s + 1;
  			report_invalid_token(lex);
*************** json_lex_string(JsonLexContext *lex)
*** 443,453 ****
  				lex->token_terminator = s;
  				report_invalid_token(lex);
  			}
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  					 errmsg("invalid input syntax for type json"),
! 					 errdetail("line %d: Character with value \"0x%02x\" must be escaped.",
! 									 lex->line_number, (unsigned char) *s)));
  		}
  		else if (*s == '\\')
  		{
--- 437,449 ----
  				lex->token_terminator = s;
  				report_invalid_token(lex);
  			}
+ 			lex->token_terminator = s + pg_mblen(s);
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  					 errmsg("invalid input syntax for type json"),
! 					 errdetail("Character with value 0x%02x must be escaped.",
! 							   (unsigned char) *s),
! 					 report_json_context(lex)));
  		}
  		else if (*s == '\\')
  		{
*************** json_lex_string(JsonLexContext *lex)
*** 465,502 ****
  
  				for (i = 1; i <= 4; i++)
  				{
! 					if (s[i] == '\0')
  					{
! 						lex->token_terminator = s + i;
  						report_invalid_token(lex);
  					}
! 					else if (s[i] >= '0' && s[i] <= '9')
! 						ch = (ch * 16) + (s[i] - '0');
! 					else if (s[i] >= 'a' && s[i] <= 'f')
! 						ch = (ch * 16) + (s[i] - 'a') + 10;
! 					else if (s[i] >= 'A' && s[i] <= 'F')
! 						ch = (ch * 16) + (s[i] - 'A') + 10;
  					else
  					{
  						ereport(ERROR,
  								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  								 errmsg("invalid input syntax for type json"),
! 								 errdetail("line %d: \"\\u\" must be followed by four hexadecimal digits.",
! 													lex->line_number)));
  					}
  				}
- 
- 				/* Account for the four additional bytes we just parsed. */
- 				s += 4;
  			}
  			else if (strchr("\"\\/bfnrt", *s) == NULL)
  			{
  				/* Not a valid string escape, so error out. */
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  						 errmsg("invalid input syntax for type json"),
! 						 errdetail("line %d: Invalid escape \"\\%s\".",
! 									 lex->line_number, extract_mb_char(s))));
  			}
  		}
  	}
--- 461,499 ----
  
  				for (i = 1; i <= 4; i++)
  				{
! 					s++;
! 					if (*s == '\0')
  					{
! 						lex->token_terminator = s;
  						report_invalid_token(lex);
  					}
! 					else if (*s >= '0' && *s <= '9')
! 						ch = (ch * 16) + (*s - '0');
! 					else if (*s >= 'a' && *s <= 'f')
! 						ch = (ch * 16) + (*s - 'a') + 10;
! 					else if (*s >= 'A' && *s <= 'F')
! 						ch = (ch * 16) + (*s - 'A') + 10;
  					else
  					{
+ 						lex->token_terminator = s + pg_mblen(s);
  						ereport(ERROR,
  								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  								 errmsg("invalid input syntax for type json"),
! 								 errdetail("\"\\u\" must be followed by four hexadecimal digits."),
! 								 report_json_context(lex)));
  					}
  				}
  			}
  			else if (strchr("\"\\/bfnrt", *s) == NULL)
  			{
  				/* Not a valid string escape, so error out. */
+ 				lex->token_terminator = s + pg_mblen(s);
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Invalid string escape \"\\%s\".",
! 								   extract_mb_char(s)),
! 						 report_json_context(lex)));
  			}
  		}
  	}
*************** json_lex_number(JsonLexContext *lex, cha
*** 599,666 ****
  
  /*
   * Report a parse error.
   */
  static void
  report_parse_error(JsonParseStack *stack, JsonLexContext *lex)
  {
! 	char	   *detail = NULL;
! 	char	   *token = NULL;
  	int			toklen;
  
  	/* Handle case where the input ended prematurely. */
  	if (lex->token_start == NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 				 errmsg("invalid input syntax for type json: \"%s\"",
! 						lex->input),
! 				 errdetail("The input string ended unexpectedly.")));
  
! 	/* Separate out the offending token. */
  	toklen = lex->token_terminator - lex->token_start;
  	token = palloc(toklen + 1);
  	memcpy(token, lex->token_start, toklen);
  	token[toklen] = '\0';
  
! 	/* Select correct detail message. */
  	if (stack == NULL)
! 		detail = "line %d: Expected end of input, but found \"%s\".";
  	else
  	{
  		switch (stack->state)
  		{
  			case JSON_PARSE_VALUE:
! 				detail = "line %d: Expected string, number, object, array, true, false, or null, but found \"%s\".";
  				break;
  			case JSON_PARSE_ARRAY_START:
! 				detail = "line %d: Expected array element or \"]\", but found \"%s\".";
  				break;
  			case JSON_PARSE_ARRAY_NEXT:
! 				detail = "line %d: Expected \",\" or \"]\", but found \"%s\".";
  				break;
  			case JSON_PARSE_OBJECT_START:
! 				detail = "line %d: Expected string or \"}\", but found \"%s\".";
  				break;
  			case JSON_PARSE_OBJECT_LABEL:
! 				detail = "line %d: Expected \":\", but found \"%s\".";
  				break;
  			case JSON_PARSE_OBJECT_NEXT:
! 				detail = "line %d: Expected \",\" or \"}\", but found \"%s\".";
  				break;
  			case JSON_PARSE_OBJECT_COMMA:
! 				detail = "line %d: Expected string, but found \"%s\".";
  				break;
  		}
  	}
- 
- 	ereport(ERROR,
- 			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- 			 errmsg("invalid input syntax for type json: \"%s\"",
- 					lex->input),
- 		  detail ? errdetail(detail, lex->line_number, token) : 0));
  }
  
  /*
   * Report an invalid input token.
   */
  static void
  report_invalid_token(JsonLexContext *lex)
--- 596,703 ----
  
  /*
   * Report a parse error.
+  *
+  * lex->token_start and lex->token_terminator must identify the current token.
   */
  static void
  report_parse_error(JsonParseStack *stack, JsonLexContext *lex)
  {
! 	char	   *token;
  	int			toklen;
  
  	/* Handle case where the input ended prematurely. */
  	if (lex->token_start == NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 				 errmsg("invalid input syntax for type json"),
! 				 errdetail("The input string ended unexpectedly."),
! 				 report_json_context(lex)));
  
! 	/* Separate out the current token. */
  	toklen = lex->token_terminator - lex->token_start;
  	token = palloc(toklen + 1);
  	memcpy(token, lex->token_start, toklen);
  	token[toklen] = '\0';
  
! 	/* Complain, with the appropriate detail message. */
  	if (stack == NULL)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 				 errmsg("invalid input syntax for type json"),
! 				 errdetail("Expected end of input, but found \"%s\".",
! 						   token),
! 				 report_json_context(lex)));
  	else
  	{
  		switch (stack->state)
  		{
  			case JSON_PARSE_VALUE:
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Expected string, number, object, array, true, false, or null, but found \"%s\".",
! 								   token),
! 						 report_json_context(lex)));
  				break;
  			case JSON_PARSE_ARRAY_START:
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Expected array element or \"]\", but found \"%s\".",
! 								   token),
! 						 report_json_context(lex)));
  				break;
  			case JSON_PARSE_ARRAY_NEXT:
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Expected \",\" or \"]\", but found \"%s\".",
! 								   token),
! 						 report_json_context(lex)));
  				break;
  			case JSON_PARSE_OBJECT_START:
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Expected string or \"}\", but found \"%s\".",
! 								   token),
! 						 report_json_context(lex)));
  				break;
  			case JSON_PARSE_OBJECT_LABEL:
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Expected \":\", but found \"%s\".",
! 								   token),
! 						 report_json_context(lex)));
  				break;
  			case JSON_PARSE_OBJECT_NEXT:
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Expected \",\" or \"}\", but found \"%s\".",
! 								   token),
! 						 report_json_context(lex)));
  				break;
  			case JSON_PARSE_OBJECT_COMMA:
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
! 						 errmsg("invalid input syntax for type json"),
! 						 errdetail("Expected string, but found \"%s\".",
! 								   token),
! 						 report_json_context(lex)));
  				break;
+ 			default:
+ 				elog(ERROR, "unexpected json parse state: %d",
+ 					 (int) stack->state);
  		}
  	}
  }
  
  /*
   * Report an invalid input token.
+  *
+  * lex->token_start and lex->token_terminator must identify the token.
   */
  static void
  report_invalid_token(JsonLexContext *lex)
*************** report_invalid_token(JsonLexContext *lex
*** 668,673 ****
--- 705,711 ----
  	char	   *token;
  	int			toklen;
  
+ 	/* Separate out the offending token. */
  	toklen = lex->token_terminator - lex->token_start;
  	token = palloc(toklen + 1);
  	memcpy(token, lex->token_start, toklen);
*************** report_invalid_token(JsonLexContext *lex
*** 676,683 ****
  	ereport(ERROR,
  			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  			 errmsg("invalid input syntax for type json"),
! 			 errdetail("line %d: Token \"%s\" is invalid.",
! 								lex->line_number, token)));
  }
  
  /*
--- 714,793 ----
  	ereport(ERROR,
  			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  			 errmsg("invalid input syntax for type json"),
! 			 errdetail("Token \"%s\" is invalid.", token),
! 			 report_json_context(lex)));
! }
! 
! /*
!  * Report a CONTEXT line for bogus JSON input.
!  *
!  * lex->token_terminator must be set to identify the spot where we detected
!  * the error.  Note that lex->token_start might be NULL, in case we recognized
!  * error at EOF.
!  *
!  * The return value isn't meaningful, but we make it non-void so that this
!  * can be invoked inside ereport().
!  */
! static int
! 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_end = lex->token_terminator;
! 	line_start = context_start;
! 	line_number = 1;
! 	for (;;)
! 	{
! 		/* Always advance over newlines (context_end test is just paranoia) */
! 		if (*context_start == '\n' && context_start < context_end)
! 		{
! 			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);
! 		else
! 			context_start++;
! 	}
! 
! 	/*
! 	 * We add "..." to indicate that the excerpt doesn't start at the
! 	 * beginning of the line ... but if we're within 3 characters of the
! 	 * beginning of the line, we might as well just show the whole line.
! 	 */
! 	if (context_start - line_start <= 3)
! 		context_start = line_start;
! 
! 	/* Get a null-terminated copy of the data to present */
! 	ctxtlen = context_end - context_start;
! 	ctxt = palloc(ctxtlen + 1);
! 	memcpy(ctxt, context_start, ctxtlen);
! 	ctxt[ctxtlen] = '\0';
! 
! 	/*
! 	 * Show the context, prefixing "..." if not starting at start of line, and
! 	 * suffixing "..." if not ending at end of line.
! 	 */
! 	prefix = (context_start > line_start) ? "..." : "";
! 	suffix = (*context_end != '\0' && *context_end != '\n' && *context_end != '\r') ? "..." : "";
! 
! 	return errcontext("JSON data, line %d: %s%s%s",
! 					  line_number, prefix, ctxt, suffix);
  }
  
  /*
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index 4b1ad89..1f79e05 100644
*** a/src/test/regress/expected/json.out
--- b/src/test/regress/expected/json.out
*************** SELECT $$''$$::json;			-- ERROR, single
*** 9,15 ****
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT $$''$$::json;
                 ^
! DETAIL:  line 1: Token "'" is invalid.
  SELECT '"abc"'::json;			-- OK
   json  
  -------
--- 9,16 ----
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT $$''$$::json;
                 ^
! DETAIL:  Token "'" is invalid.
! CONTEXT:  JSON data, line 1: '...
  SELECT '"abc"'::json;			-- OK
   json  
  -------
*************** SELECT '"abc'::json;			-- ERROR, quotes
*** 20,32 ****
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"abc'::json;
                 ^
! DETAIL:  line 1: Token ""abc" is invalid.
  SELECT '"abc
  def"'::json;					-- ERROR, unescaped newline in string constant
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"abc
                 ^
! DETAIL:  line 1: Character with value "0x0a" must be escaped.
  SELECT '"\n\"\\"'::json;		-- OK, legal escapes
     json   
  ----------
--- 21,36 ----
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"abc'::json;
                 ^
! DETAIL:  Token ""abc" is invalid.
! CONTEXT:  JSON data, line 1: "abc
  SELECT '"abc
  def"'::json;					-- ERROR, unescaped newline in string constant
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"abc
                 ^
! DETAIL:  Character with value 0x0a must be escaped.
! CONTEXT:  JSON data, line 1: "abc
! ...
  SELECT '"\n\"\\"'::json;		-- OK, legal escapes
     json   
  ----------
*************** SELECT '"\v"'::json;			-- ERROR, not a v
*** 37,58 ****
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\v"'::json;
                 ^
! DETAIL:  line 1: Invalid escape "\v".
  SELECT '"\u"'::json;			-- ERROR, incomplete escape
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\u"'::json;
                 ^
! DETAIL:  line 1: "\u" must be followed by four hexadecimal digits.
  SELECT '"\u00"'::json;			-- ERROR, incomplete escape
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\u00"'::json;
                 ^
! DETAIL:  line 1: "\u" must be followed by four hexadecimal digits.
  SELECT '"\u000g"'::json;		-- ERROR, g is not a hex digit
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\u000g"'::json;
                 ^
! DETAIL:  line 1: "\u" must be followed by four hexadecimal digits.
  SELECT '"\u0000"'::json;		-- OK, legal escape
     json   
  ----------
--- 41,66 ----
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\v"'::json;
                 ^
! DETAIL:  Invalid string escape "\v".
! CONTEXT:  JSON data, line 1: "\v...
  SELECT '"\u"'::json;			-- ERROR, incomplete escape
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\u"'::json;
                 ^
! DETAIL:  "\u" must be followed by four hexadecimal digits.
! CONTEXT:  JSON data, line 1: "\u"
  SELECT '"\u00"'::json;			-- ERROR, incomplete escape
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\u00"'::json;
                 ^
! DETAIL:  "\u" must be followed by four hexadecimal digits.
! CONTEXT:  JSON data, line 1: "\u00"
  SELECT '"\u000g"'::json;		-- ERROR, g is not a hex digit
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '"\u000g"'::json;
                 ^
! DETAIL:  "\u" must be followed by four hexadecimal digits.
! CONTEXT:  JSON data, line 1: "\u000g...
  SELECT '"\u0000"'::json;		-- OK, legal escape
     json   
  ----------
*************** SELECT '01'::json;				-- ERROR, not vali
*** 82,88 ****
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '01'::json;
                 ^
! DETAIL:  line 1: Token "01" is invalid.
  SELECT '0.1'::json;				-- OK
   json 
  ------
--- 90,97 ----
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '01'::json;
                 ^
! DETAIL:  Token "01" is invalid.
! CONTEXT:  JSON data, line 1: 01
  SELECT '0.1'::json;				-- OK
   json 
  ------
*************** SELECT '1f2'::json;				-- ERROR
*** 111,127 ****
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '1f2'::json;
                 ^
! DETAIL:  line 1: Token "1f2" is invalid.
  SELECT '0.x1'::json;			-- ERROR
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '0.x1'::json;
                 ^
! DETAIL:  line 1: Token "0.x1" is invalid.
  SELECT '1.3ex100'::json;		-- ERROR
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '1.3ex100'::json;
                 ^
! DETAIL:  line 1: Token "1.3ex100" is invalid.
  -- Arrays.
  SELECT '[]'::json;				-- OK
   json 
--- 120,139 ----
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '1f2'::json;
                 ^
! DETAIL:  Token "1f2" is invalid.
! CONTEXT:  JSON data, line 1: 1f2
  SELECT '0.x1'::json;			-- ERROR
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '0.x1'::json;
                 ^
! DETAIL:  Token "0.x1" is invalid.
! CONTEXT:  JSON data, line 1: 0.x1
  SELECT '1.3ex100'::json;		-- ERROR
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '1.3ex100'::json;
                 ^
! DETAIL:  Token "1.3ex100" is invalid.
! CONTEXT:  JSON data, line 1: 1.3ex100
  -- Arrays.
  SELECT '[]'::json;				-- OK
   json 
*************** SELECT '[1,2]'::json;			-- OK
*** 142,161 ****
  (1 row)
  
  SELECT '[1,2,]'::json;			-- ERROR, trailing comma
! ERROR:  invalid input syntax for type json: "[1,2,]"
  LINE 1: SELECT '[1,2,]'::json;
                 ^
! DETAIL:  line 1: Expected string, number, object, array, true, false, or null, but found "]".
  SELECT '[1,2'::json;			-- ERROR, no closing bracket
! ERROR:  invalid input syntax for type json: "[1,2"
  LINE 1: SELECT '[1,2'::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
  SELECT '[1,[2]'::json;			-- ERROR, no closing bracket
! ERROR:  invalid input syntax for type json: "[1,[2]"
  LINE 1: SELECT '[1,[2]'::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
  -- Objects.
  SELECT '{}'::json;				-- OK
   json 
--- 154,176 ----
  (1 row)
  
  SELECT '[1,2,]'::json;			-- ERROR, trailing comma
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '[1,2,]'::json;
                 ^
! DETAIL:  Expected string, number, object, array, true, false, or null, but found "]".
! CONTEXT:  JSON data, line 1: [1,2,]
  SELECT '[1,2'::json;			-- ERROR, no closing bracket
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '[1,2'::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
+ CONTEXT:  JSON data, line 1: [1,2
  SELECT '[1,[2]'::json;			-- ERROR, no closing bracket
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '[1,[2]'::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
+ CONTEXT:  JSON data, line 1: [1,[2]
  -- Objects.
  SELECT '{}'::json;				-- OK
   json 
*************** SELECT '{}'::json;				-- OK
*** 164,173 ****
  (1 row)
  
  SELECT '{"abc"}'::json;			-- ERROR, no value
! ERROR:  invalid input syntax for type json: "{"abc"}"
  LINE 1: SELECT '{"abc"}'::json;
                 ^
! DETAIL:  line 1: Expected ":", but found "}".
  SELECT '{"abc":1}'::json;		-- OK
     json    
  -----------
--- 179,189 ----
  (1 row)
  
  SELECT '{"abc"}'::json;			-- ERROR, no value
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{"abc"}'::json;
                 ^
! DETAIL:  Expected ":", but found "}".
! CONTEXT:  JSON data, line 1: {"abc"}
  SELECT '{"abc":1}'::json;		-- OK
     json    
  -----------
*************** SELECT '{"abc":1}'::json;		-- OK
*** 175,199 ****
  (1 row)
  
  SELECT '{1:"abc"}'::json;		-- ERROR, keys must be strings
! ERROR:  invalid input syntax for type json: "{1:"abc"}"
  LINE 1: SELECT '{1:"abc"}'::json;
                 ^
! DETAIL:  line 1: Expected string or "}", but found "1".
  SELECT '{"abc",1}'::json;		-- ERROR, wrong separator
! ERROR:  invalid input syntax for type json: "{"abc",1}"
  LINE 1: SELECT '{"abc",1}'::json;
                 ^
! DETAIL:  line 1: Expected ":", but found ",".
  SELECT '{"abc"=1}'::json;		-- ERROR, totally wrong separator
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{"abc"=1}'::json;
                 ^
! DETAIL:  line 1: Token "=" is invalid.
  SELECT '{"abc"::1}'::json;		-- ERROR, another wrong separator
! ERROR:  invalid input syntax for type json: "{"abc"::1}"
  LINE 1: SELECT '{"abc"::1}'::json;
                 ^
! DETAIL:  line 1: Expected string, number, object, array, true, false, or null, but found ":".
  SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::json; -- OK
                            json                           
  ---------------------------------------------------------
--- 191,219 ----
  (1 row)
  
  SELECT '{1:"abc"}'::json;		-- ERROR, keys must be strings
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{1:"abc"}'::json;
                 ^
! DETAIL:  Expected string or "}", but found "1".
! CONTEXT:  JSON data, line 1: {1...
  SELECT '{"abc",1}'::json;		-- ERROR, wrong separator
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{"abc",1}'::json;
                 ^
! DETAIL:  Expected ":", but found ",".
! CONTEXT:  JSON data, line 1: {"abc",...
  SELECT '{"abc"=1}'::json;		-- ERROR, totally wrong separator
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{"abc"=1}'::json;
                 ^
! DETAIL:  Token "=" is invalid.
! CONTEXT:  JSON data, line 1: {"abc"=...
  SELECT '{"abc"::1}'::json;		-- ERROR, another wrong separator
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{"abc"::1}'::json;
                 ^
! DETAIL:  Expected string, number, object, array, true, false, or null, but found ":".
! CONTEXT:  JSON data, line 1: {"abc"::...
  SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::json; -- OK
                            json                           
  ---------------------------------------------------------
*************** SELECT '{"abc":1,"def":2,"ghi":[3,4],"hi
*** 201,215 ****
  (1 row)
  
  SELECT '{"abc":1:2}'::json;		-- ERROR, colon in wrong spot
! ERROR:  invalid input syntax for type json: "{"abc":1:2}"
  LINE 1: SELECT '{"abc":1:2}'::json;
                 ^
! DETAIL:  line 1: Expected "," or "}", but found ":".
  SELECT '{"abc":1,3}'::json;		-- ERROR, no value
! ERROR:  invalid input syntax for type json: "{"abc":1,3}"
  LINE 1: SELECT '{"abc":1,3}'::json;
                 ^
! DETAIL:  line 1: Expected string, but found "3".
  -- Miscellaneous stuff.
  SELECT 'true'::json;			-- OK
   json 
--- 221,237 ----
  (1 row)
  
  SELECT '{"abc":1:2}'::json;		-- ERROR, colon in wrong spot
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{"abc":1:2}'::json;
                 ^
! DETAIL:  Expected "," or "}", but found ":".
! CONTEXT:  JSON data, line 1: {"abc":1:...
  SELECT '{"abc":1,3}'::json;		-- ERROR, no value
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '{"abc":1,3}'::json;
                 ^
! DETAIL:  Expected string, but found "3".
! CONTEXT:  JSON data, line 1: {"abc":1,3...
  -- Miscellaneous stuff.
  SELECT 'true'::json;			-- OK
   json 
*************** SELECT ' true '::json;			-- OK, even wit
*** 236,270 ****
  (1 row)
  
  SELECT 'true false'::json;		-- ERROR, too many values
! ERROR:  invalid input syntax for type json: "true false"
  LINE 1: SELECT 'true false'::json;
                 ^
! DETAIL:  line 1: Expected end of input, but found "false".
  SELECT 'true, false'::json;		-- ERROR, too many values
! ERROR:  invalid input syntax for type json: "true, false"
  LINE 1: SELECT 'true, false'::json;
                 ^
! DETAIL:  line 1: Expected end of input, but found ",".
  SELECT 'truf'::json;			-- ERROR, not a keyword
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT 'truf'::json;
                 ^
! DETAIL:  line 1: Token "truf" is invalid.
  SELECT 'trues'::json;			-- ERROR, not a keyword
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT 'trues'::json;
                 ^
! DETAIL:  line 1: Token "trues" is invalid.
  SELECT ''::json;				-- ERROR, no value
! ERROR:  invalid input syntax for type json: ""
  LINE 1: SELECT ''::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
  SELECT '    '::json;			-- ERROR, no value
! ERROR:  invalid input syntax for type json: "    "
  LINE 1: SELECT '    '::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
  --constructors
  -- array_to_json
  SELECT array_to_json(array(select 1 as a));
--- 258,298 ----
  (1 row)
  
  SELECT 'true false'::json;		-- ERROR, too many values
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT 'true false'::json;
                 ^
! DETAIL:  Expected end of input, but found "false".
! CONTEXT:  JSON data, line 1: true false
  SELECT 'true, false'::json;		-- ERROR, too many values
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT 'true, false'::json;
                 ^
! DETAIL:  Expected end of input, but found ",".
! CONTEXT:  JSON data, line 1: true,...
  SELECT 'truf'::json;			-- ERROR, not a keyword
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT 'truf'::json;
                 ^
! DETAIL:  Token "truf" is invalid.
! CONTEXT:  JSON data, line 1: truf
  SELECT 'trues'::json;			-- ERROR, not a keyword
  ERROR:  invalid input syntax for type json
  LINE 1: SELECT 'trues'::json;
                 ^
! DETAIL:  Token "trues" is invalid.
! CONTEXT:  JSON data, line 1: trues
  SELECT ''::json;				-- ERROR, no value
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT ''::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
+ CONTEXT:  JSON data, line 1: 
  SELECT '    '::json;			-- ERROR, no value
! ERROR:  invalid input syntax for type json
  LINE 1: SELECT '    '::json;
                 ^
  DETAIL:  The input string ended unexpectedly.
+ CONTEXT:  JSON data, line 1:     
  --constructors
  -- array_to_json
  SELECT array_to_json(array(select 1 as a));