Commits

Andrew Dunstan committed a745268

passes regression

Comments (0)

Files changed (3)

src/backend/utils/adt/json.c

 typedef enum                    /* contexts of JSON parser */
 {
     JSON_PARSE_VALUE,           /* expecting a value */
+	JSON_PARSE_STRING,          /* expecting a string (for a field name) */
     JSON_PARSE_ARRAY_START,     /* saw '[', expecting value or ']' */
     JSON_PARSE_ARRAY_NEXT,      /* saw array element, expecting ',' or ']' */
     JSON_PARSE_OBJECT_START,    /* saw '{', expecting label or '}' */
 lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
 {
     if (! lex_accept(lex,token,NULL))
-		elog(ERROR,"expect: unexpected symbol");
+		report_parse_error(ctx, lex);;
 }
 
 
 static void
 parse_scalar(JsonLexContext *lex, JsonSemAction sem)
 {
-    char *val;
+    char *val = NULL;
     json_scalar_action sfunc = sem->scalar;
     JsonTokenType tok = lex_peek(lex);
 
     bool isnull;
 
     if (! lex_accept(lex, JSON_TOKEN_STRING, &fname))
-        elog(ERROR,"invalid json"); 
+        report_parse_error(JSON_PARSE_STRING, lex); 
 
     lex_expect(JSON_PARSE_OBJECT_LABEL, lex, JSON_TOKEN_COLON);
 
                 parse_object_field(lex, sem);
         
     }
+	else if (lex_peek(lex) != JSON_TOKEN_OBJECT_END)
+	{
+		/* case of an invalid initial token inside the object */
+		report_parse_error(JSON_PARSE_OBJECT_START, lex);
+	}
 
     lex_expect(JSON_PARSE_OBJECT_NEXT, lex, JSON_TOKEN_OBJECT_END);
 
 
 	/* Determine token type. */
 	if (*s == '\0')
+	{
+		lex->token_start = NULL;
+		lex->prev_token_terminator = lex->token_terminator;
+		lex->token_terminator = s;
 		lex->token_type =  JSON_TOKEN_END;
+	}
 	else if (strchr("{}[],:", s[0]))
 	{
-		lex->token_type = JSON_TOKEN_INVALID;
-		/* strchr() doesn't return false on a NUL input. */
-		if (s[0] == '\0')
-		{
-			/* End of string. */
-			lex->token_start = NULL;
-			lex->prev_token_terminator = lex->token_terminator;
-			lex->token_terminator = NULL;
-		}
-		else
+		/* Single-character token, some kind of punctuation mark. */
+		lex->prev_token_terminator = lex->token_terminator;
+		lex->token_terminator = s + 1;
+		switch (s[0])
 		{
-			/* Single-character token, some kind of punctuation mark. */
-			lex->prev_token_terminator = lex->token_terminator;
-			lex->token_terminator = s + 1;
-			switch (s[0])
-			{
-			    case '{': 
-					lex->token_type = JSON_TOKEN_OBJECT_START; 
-					break;
-				case '}':
-					lex->token_type = JSON_TOKEN_OBJECT_END; 
-					break;
-			    case '[': 
-					lex->token_type = JSON_TOKEN_ARRAY_START; 
-					break;
-				case ']':
-					lex->token_type = JSON_TOKEN_ARRAY_END; 
-					break;
-				case ',':
-					lex->token_type = JSON_TOKEN_COMMA; 
-					break;
-				case ':':
-					lex->token_type = JSON_TOKEN_COLON; 
-					break;
-				default:
-					break;
-			}
+			case '{': 
+				lex->token_type = JSON_TOKEN_OBJECT_START; 
+				break;
+			case '}':
+				lex->token_type = JSON_TOKEN_OBJECT_END; 
+				break;
+			case '[': 
+				lex->token_type = JSON_TOKEN_ARRAY_START; 
+				break;
+			case ']':
+				lex->token_type = JSON_TOKEN_ARRAY_END; 
+				break;
+			case ',':
+				lex->token_type = JSON_TOKEN_COMMA; 
+				break;
+			case ':':
+				lex->token_type = JSON_TOKEN_COLON; 
+				break;
+			default:
+				break;
 		}
 	}
 	else if (*s == '"')
 				}
 				if (lex->strval != NULL)
 				{
-					unsigned char utf8str[5];
+					char utf8str[5];
 					int utf8len;
 					char *converted;
 
-					unicode_to_utf8(ch, utf8str);
-					utf8len = pg_utf_mblen(utf8str);
-					utf8str[0] = '\0';
+					unicode_to_utf8(ch, (unsigned char *)utf8str);
+					utf8len = pg_utf_mblen((unsigned char *)utf8str);
+					utf8str[utf8len] = '\0';
 					converted = pg_any_to_server(utf8str, 1, PG_UTF8);
 					appendStringInfoString(lex->strval, converted);
 					if (converted != utf8str)
 
 	/* Part (2): parse main digit string. */
 	if (*s == '0')
-		++s;
+		s++;
 	else if (*s >= '1' && *s <= '9')
 	{
 		do
 		{
-			++s;
+			s++;
 		} while (*s >= '0' && *s <= '9');
 	}
 	else
 	/* Part (3): parse optional decimal portion. */
 	if (*s == '.')
 	{
-		++s;
-		if (*s < '0' && *s > '9')
+		s++;
+		if (*s < '0' || *s > '9')
 			error = true;
 		else
 		{
 			do
 			{
-				++s;
+				s++;
 			} while (*s >= '0' && *s <= '9');
 		}
 	}
 	/* Part (4): parse optional exponent. */
 	if (*s == 'e' || *s == 'E')
 	{
-		++s;
+		s++;
 		if (*s == '+' || *s == '-')
-			++s;
-		if (*s < '0' && *s > '9')
+			s++;
+		if (*s < '0' || *s > '9')
 			error = true;
 		else
 		{
 			do
 			{
-				++s;
+				s++;
 			} while (*s >= '0' && *s <= '9');
 		}
 	}
 
 	/* Check for trailing garbage. */
 	for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
-		;
+		error = true;
 	lex->prev_token_terminator = lex->token_terminator;
 	lex->token_terminator = p;
-	if (p > s || error)
+	if (error)
 		report_invalid_token(lex);
 }
 
 	int			toklen;
 
 	/* Handle case where the input ended prematurely. */
-	if (lex->token_start == NULL)
+	if (lex->token_start == NULL || lex->token_type == JSON_TOKEN_END)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type json"),
 								   token),
 						 report_json_context(lex)));
 				break;
+			case JSON_PARSE_STRING:
+				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;
 			case JSON_PARSE_ARRAY_START:
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 	 * suffixing "..." if not ending at end of line.
 	 */
 	prefix = (context_start > line_start) ? "..." : "";
-	suffix = (*context_end != '\0' && *context_end != '\n' && *context_end != '\r') ? "..." : "";
+	suffix = (lex->token_type != JSON_TOKEN_END && *context_end != '\0' && *context_end != '\n' && *context_end != '\r') ? "..." : "";
 
 	return errcontext("JSON data, line %d: %s%s%s",
 					  line_number, prefix, ctxt, suffix);

src/include/catalog/pg_proc.h

 DESCR("get json array element");
 DATA(insert OID = 5004 (  json_get_as_text PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 25 "114 23" _null_ _null_ _null_ _null_ json_get_aelem_as_text _null_ _null_ _null_ ));
 DESCR("get json array element as text");
-DATA(insert OID = 5005 (  json_object_keys PGNSP PGUID 12 1 0 0 0 f f f f t t s 1 0 25 "114" _null_ _null_ _null_ _null_ json_object_keys _null_ _null_ _null_ ));
+DATA(insert OID = 5005 (  json_object_keys PGNSP PGUID 12 1 100 0 0 f f f f t t s 1 0 25 "114" _null_ _null_ _null_ _null_ json_object_keys _null_ _null_ _null_ ));
 DESCR("get json object keys");
 
 

src/test/regress/expected/json.out

  {"jsonfield":{"a":1,"b": [2,3,4,"d","e","f"],"c":{"p":1,"q":2}}}
 (1 row)
 
+-- json extraction functions
+CREATE TEMP TABLE test_json (
+       json_type text,
+       test_json json
+);
+INSERT INTO test_json VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two","three","four","five"]'),
+('object','{"field1":"val1","field2":"val2"}');
+SELECT json_get(test_json,'x') 
+FROM test_json
+WHERE json_type = 'scalar';
+ERROR:  cannot call json_get on a scalar
+SELECT json_get(test_json,'x') 
+FROM test_json
+WHERE json_type = 'array';
+ERROR:  cannot call json_get(fieldname) on a non-object
+SELECT json_get(test_json,'x') 
+FROM test_json
+WHERE json_type = 'object';
+ json_get 
+----------
+ 
+(1 row)
+
+SELECT json_get(test_json,'field2') 
+FROM test_json
+WHERE json_type = 'object';
+ json_get 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_json
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2' 
+FROM test_json
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT json_get(test_json,2) 
+FROM test_json
+WHERE json_type = 'scalar';
+ERROR:  cannot call json_get on a scalar
+SELECT json_get(test_json,2) 
+FROM test_json
+WHERE json_type = 'array';
+ json_get 
+----------
+ "two"
+(1 row)
+
+SELECT json_get(test_json,2)
+FROM test_json
+WHERE json_type = 'object';
+ERROR:  cannot call json_get(int) on a non-array
+SELECT json_get(test_json,2) 
+FROM test_json
+WHERE json_type = 'array';
+ json_get 
+----------
+ "two"
+(1 row)
+
+SELECT test_json->2 
+FROM test_json
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json->>2
+FROM test_json
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT json_object_keys(test_json)
+FROM test_json
+WHERE json_type = 'scalar';
+ERROR:  cannot call json_object_keys on a scalar
+SELECT json_object_keys(test_json)
+FROM test_json
+WHERE json_type = 'array';
+ERROR:  cannot call json_object_keys on an array
+SELECT json_object_keys(test_json)
+FROM test_json
+WHERE json_type = 'object';
+ json_object_keys 
+------------------
+ field1
+ field2
+(2 rows)
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.