Commits

Andrew Dunstan committed 4f98159

first cut

Comments (0)

Files changed (8)

src/backend/utils/adt/Makefile

 	array_userfuncs.o arrayutils.o bool.o \
 	cash.o char.o date.o datetime.o datum.o domains.o \
 	enum.o float.o format_type.o \
-	geo_ops.o geo_selfuncs.o int.o int8.o json.o like.o lockfuncs.o \
-	misc.o nabstime.o name.o numeric.o numutils.o \
+	geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
+	lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
 	oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.o \
 	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
 	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \

src/backend/utils/adt/json.c

 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/json.h"
+#include "utils/jsonapi.h"
 #include "utils/typcache.h"
 
-typedef enum					/* types of JSON values */
-{
-	JSON_VALUE_INVALID,			/* non-value tokens are reported as this */
-	JSON_VALUE_STRING,
-	JSON_VALUE_NUMBER,
-	JSON_VALUE_OBJECT,
-	JSON_VALUE_ARRAY,
-	JSON_VALUE_TRUE,
-	JSON_VALUE_FALSE,
-	JSON_VALUE_NULL
-} JsonValueType;
-
-typedef struct					/* state of JSON lexer */
-{
-	char	   *input;			/* whole string being parsed */
-	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 */
-} JsonLexContext;
-
-typedef enum					/* states of JSON parser */
-{
-	JSON_PARSE_VALUE,			/* expecting a value */
-	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 '}' */
-	JSON_PARSE_OBJECT_LABEL,	/* saw object label, expecting ':' */
-	JSON_PARSE_OBJECT_NEXT,		/* saw object value, expecting ',' or '}' */
-	JSON_PARSE_OBJECT_COMMA		/* saw object ',', expecting next label */
-} JsonParseState;
-
-typedef struct JsonParseStack	/* the parser state has to be stackable */
-{
-	JsonParseState state;
-	/* currently only need the state enum, but maybe someday more stuff */
-} JsonParseStack;
-
-typedef enum					/* required operations on state stack */
+/* 
+ * The context of the parser is maintained by the recursive descent
+ * mechanism, but is passed explicitly to the error reporting routine
+ * for better diagnostics.
+ */
+typedef enum                    /* contexts of JSON parser */
 {
-	JSON_STACKOP_NONE,			/* no-op */
-	JSON_STACKOP_PUSH,			/* push new JSON_PARSE_VALUE stack item */
-	JSON_STACKOP_PUSH_WITH_PUSHBACK, /* push, then rescan current token */
-	JSON_STACKOP_POP			/* pop, or expect end of input if no stack */
-} JsonStackOp;
+    JSON_PARSE_VALUE,           /* expecting a value */
+    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 '}' */
+    JSON_PARSE_OBJECT_LABEL,    /* saw object label, expecting ':' */
+    JSON_PARSE_OBJECT_NEXT,     /* saw object value, expecting ',' or '}' */
+    JSON_PARSE_OBJECT_COMMA,    /* saw object ',', expecting next label */
+	JSON_PARSE_END              /* saw the end of a document, expect nothing */
+} JsonParseContext;
 
 static void json_validate_cstring(char *input);
 static void json_lex(JsonLexContext *lex);
 static void json_lex_string(JsonLexContext *lex);
 static void json_lex_number(JsonLexContext *lex, char *s);
-static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
+static void parse_scalar(JsonLexContext *lex, JsonSemAction sem);
+static void parse_object_field(JsonLexContext *lex, JsonSemAction sem);
+static void parse_object(JsonLexContext *lex, JsonSemAction sem);
+static void parse_array_element(JsonLexContext *lex, JsonSemAction sem);
+static void parse_array(JsonLexContext *lex, JsonSemAction sem);
+static void report_parse_error(JsonParseContext ctx, 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 array_to_json_internal(Datum array, StringInfo result,
 								   bool use_line_feeds);
 
+/* the null action object used for pure validation */
+static jsonSemAction nullSemAction = 
+{ 
+	NULL, NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL
+};
+static JsonSemAction NullSemAction = &nullSemAction;
+
+
+/* Recursive Descent parser support routines */
+
+static inline JsonTokenType
+lex_peek(JsonLexContext *lex)
+{
+    return lex->token_type;
+}
+
+static inline bool 
+lex_accept(JsonLexContext *lex, JsonTokenType token, char **lexeme)
+{
+    if (lex->token_type == token) 
+	{
+		if (lexeme != NULL)
+		{
+			if (lex->token_type == JSON_TOKEN_STRING)
+			{
+				if (lex->strval != NULL)
+					*lexeme = pstrdup(lex->strval->data);
+			}
+			else
+			{
+				int len = (lex->token_terminator - lex->token_start);
+				char *tokstr = palloc(len+1);
+				memcpy(tokstr,lex->token_start,len);
+				tokstr[len] = '\0';
+				*lexeme = tokstr;
+			}
+		}
+        json_lex(lex);
+        return true;
+    }
+    return false;
+}
+
+static inline void 
+lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
+{
+    if (! lex_accept(lex,token,NULL))
+		elog(ERROR,"expect: unexpected symbol");
+}
+
+
+
 /* fake type category for JSON so we can distinguish it in datum_to_json */
 #define TYPCATEGORY_JSON 'j'
 /* letters appearing in numeric output that aren't valid in a JSON number */
 }
 
 /*
- * Check whether supplied input is valid JSON.
+ * parse routines
  */
+
+
+void
+pg_parse_json(JsonLexContext *lex, JsonSemAction sem)
+{
+
+    /* get the initial token */
+    json_lex(lex);
+
+
+    /* parse by recursive descent */
+    if (lex_peek(lex) == JSON_TOKEN_OBJECT_START)
+        parse_object(lex, sem);
+    else if (lex_peek(lex) == JSON_TOKEN_ARRAY_START)
+        parse_array(lex, sem);
+    else
+        parse_scalar(lex, sem);/* json can be a bare scalar */
+
+    lex_expect(JSON_PARSE_END, lex, JSON_TOKEN_END);
+
+}
+
+
 static void
-json_validate_cstring(char *input)
+parse_scalar(JsonLexContext *lex, JsonSemAction sem)
 {
-	JsonLexContext lex;
-	JsonParseStack *stack,
-			   *stacktop;
-	int			stacksize;
+    char *val;
+    json_scalar_action sfunc = sem->scalar;
+    JsonTokenType tok = lex_peek(lex);
+
+    if (lex_accept(lex, JSON_TOKEN_TRUE, &val) ||
+        lex_accept(lex, JSON_TOKEN_FALSE, &val) ||
+        lex_accept(lex, JSON_TOKEN_NULL, &val) ||
+        lex_accept(lex, JSON_TOKEN_NUMBER, &val) ||
+        lex_accept(lex, JSON_TOKEN_STRING, &val))
+    {
+        if (sfunc != NULL)
+            (*sfunc) (sem->semstate, val, tok);
+    }
+    else
+    {
+        report_parse_error(JSON_PARSE_VALUE, lex);
+    }
+}
 
-	/* Set up lexing context. */
-	lex.input = input;
-	lex.token_terminator = lex.input;
+static void
+parse_object_field(JsonLexContext *lex, JsonSemAction sem)
+{
 
-	/* Set up parse stack. */
-	stacksize = 32;
-	stacktop = (JsonParseStack *) palloc(sizeof(JsonParseStack) * stacksize);
-	stack = stacktop;
-	stack->state = JSON_PARSE_VALUE;
+    char *fname = NULL; /* keep compiler quiet */
+    json_ofield_action ostart = sem->object_field_start;
+    json_ofield_action oend = sem->object_field_end;
+    bool isnull;
 
-	/* Main parsing loop. */
-	for (;;)
-	{
-		JsonStackOp op;
+    if (! lex_accept(lex, JSON_TOKEN_STRING, &fname))
+        elog(ERROR,"invalid json"); 
 
-		/* Fetch next token. */
-		json_lex(&lex);
+    lex_expect(JSON_PARSE_OBJECT_LABEL, lex, JSON_TOKEN_COLON);
 
-		/* Check for unexpected end of input. */
-		if (lex.token_start == NULL)
-			report_parse_error(stack, &lex);
+    isnull = lex_peek(lex) == JSON_TOKEN_NULL;
 
-redo:
-		/* Figure out what to do with this token. */
-		op = JSON_STACKOP_NONE;
-		switch (stack->state)
-		{
-			case JSON_PARSE_VALUE:
-				if (lex.token_type != JSON_VALUE_INVALID)
-					op = JSON_STACKOP_POP;
-				else if (lex.token_start[0] == '[')
-					stack->state = JSON_PARSE_ARRAY_START;
-				else if (lex.token_start[0] == '{')
-					stack->state = JSON_PARSE_OBJECT_START;
-				else
-					report_parse_error(stack, &lex);
-				break;
-			case JSON_PARSE_ARRAY_START:
-				if (lex.token_type != JSON_VALUE_INVALID)
-					stack->state = JSON_PARSE_ARRAY_NEXT;
-				else if (lex.token_start[0] == ']')
-					op = JSON_STACKOP_POP;
-				else if (lex.token_start[0] == '[' ||
-						 lex.token_start[0] == '{')
-				{
-					stack->state = JSON_PARSE_ARRAY_NEXT;
-					op = JSON_STACKOP_PUSH_WITH_PUSHBACK;
-				}
-				else
-					report_parse_error(stack, &lex);
-				break;
-			case JSON_PARSE_ARRAY_NEXT:
-				if (lex.token_type != JSON_VALUE_INVALID)
-					report_parse_error(stack, &lex);
-				else if (lex.token_start[0] == ']')
-					op = JSON_STACKOP_POP;
-				else if (lex.token_start[0] == ',')
-					op = JSON_STACKOP_PUSH;
-				else
-					report_parse_error(stack, &lex);
-				break;
-			case JSON_PARSE_OBJECT_START:
-				if (lex.token_type == JSON_VALUE_STRING)
-					stack->state = JSON_PARSE_OBJECT_LABEL;
-				else if (lex.token_type == JSON_VALUE_INVALID &&
-						 lex.token_start[0] == '}')
-					op = JSON_STACKOP_POP;
-				else
-					report_parse_error(stack, &lex);
-				break;
-			case JSON_PARSE_OBJECT_LABEL:
-				if (lex.token_type == JSON_VALUE_INVALID &&
-					lex.token_start[0] == ':')
-				{
-					stack->state = JSON_PARSE_OBJECT_NEXT;
-					op = JSON_STACKOP_PUSH;
-				}
-				else
-					report_parse_error(stack, &lex);
-				break;
-			case JSON_PARSE_OBJECT_NEXT:
-				if (lex.token_type != JSON_VALUE_INVALID)
-					report_parse_error(stack, &lex);
-				else if (lex.token_start[0] == '}')
-					op = JSON_STACKOP_POP;
-				else if (lex.token_start[0] == ',')
-					stack->state = JSON_PARSE_OBJECT_COMMA;
-				else
-					report_parse_error(stack, &lex);
-				break;
-			case JSON_PARSE_OBJECT_COMMA:
-				if (lex.token_type == JSON_VALUE_STRING)
-					stack->state = JSON_PARSE_OBJECT_LABEL;
-				else
-					report_parse_error(stack, &lex);
-				break;
-			default:
-				elog(ERROR, "unexpected json parse state: %d",
-					 (int) stack->state);
-		}
+    if (ostart != NULL)
+        (*ostart) (sem->semstate, fname, isnull);
 
-		/* Push or pop the state stack, if needed. */
-		switch (op)
-		{
-			case JSON_STACKOP_PUSH:
-			case JSON_STACKOP_PUSH_WITH_PUSHBACK:
-				stack++;
-				if (stack >= &stacktop[stacksize])
-				{
-					/* Need to enlarge the stack. */
-					int			stackoffset = stack - stacktop;
-
-					stacksize += 32;
-					stacktop = (JsonParseStack *)
-						repalloc(stacktop,
-								 sizeof(JsonParseStack) * stacksize);
-					stack = stacktop + stackoffset;
-				}
-				stack->state = JSON_PARSE_VALUE;
-				if (op == JSON_STACKOP_PUSH_WITH_PUSHBACK)
-					goto redo;
-				break;
-			case JSON_STACKOP_POP:
-				if (stack == stacktop)
-				{
-					/* Expect end of input. */
-					json_lex(&lex);
-					if (lex.token_start != NULL)
-						report_parse_error(NULL, &lex);
-					return;
-				}
-				stack--;
-				break;
-			case JSON_STACKOP_NONE:
-				/* nothing to do */
-				break;
-		}
-	}
+    if (lex_peek(lex) == JSON_TOKEN_OBJECT_START)
+        parse_object(lex, sem);
+    else if (lex_peek(lex) == JSON_TOKEN_ARRAY_START)
+        parse_array(lex,sem);
+    else
+        parse_scalar(lex, sem);
+
+    if (oend != NULL)
+        (*oend) (sem->semstate, fname, isnull);
+}
+
+static void 
+parse_object(JsonLexContext *lex, JsonSemAction sem)
+{
+    
+    json_struct_action ostart = sem->object_start;
+    json_struct_action oend = sem->object_end;
+
+    if (ostart != NULL)
+        (*ostart) (sem->semstate);
+
+	/* we know this will succeeed, just clearing the token */
+    lex_expect(JSON_PARSE_OBJECT_START, lex, JSON_TOKEN_OBJECT_START);
+    if (lex_peek(lex) == JSON_TOKEN_STRING)
+    {
+        parse_object_field(lex, sem);
+
+        while (lex_accept(lex,JSON_TOKEN_COMMA,NULL))
+                parse_object_field(lex, sem);
+        
+    }
+
+    lex_expect(JSON_PARSE_OBJECT_NEXT, lex, JSON_TOKEN_OBJECT_END);
+
+    if (oend != NULL)
+        (*oend) (sem->semstate);
+}
+
+static void
+parse_array_element(JsonLexContext *lex, JsonSemAction sem)
+{
+    json_aelem_action astart = sem->array_element_start;
+    json_aelem_action aend = sem->array_element_end;
+    bool isnull;
+
+    isnull = lex_peek(lex) == JSON_TOKEN_NULL;
+
+    if (astart != NULL)
+        (*astart) (sem->semstate, isnull);
+
+    if (lex_peek(lex) == JSON_TOKEN_OBJECT_START)
+        parse_object(lex, sem);
+    else if (lex_peek(lex) == JSON_TOKEN_ARRAY_START)
+        parse_array(lex, sem);
+    else
+        parse_scalar(lex, sem);
+
+    if (aend != NULL)
+        (*aend) (sem->semstate, isnull);
+}
+
+static void 
+parse_array(JsonLexContext *lex, JsonSemAction sem)
+{
+    json_struct_action astart = sem->array_start;
+    json_struct_action aend = sem->array_end;
+
+    if (astart != NULL)
+        (*astart) (sem->semstate);
+
+    lex_expect(JSON_PARSE_ARRAY_START, lex, JSON_TOKEN_ARRAY_START);
+    if (lex_peek(lex) != JSON_TOKEN_ARRAY_END)
+    {
+
+        parse_array_element(lex, sem);
+
+        while (lex_accept(lex,JSON_TOKEN_COMMA,NULL))
+            parse_array_element(lex, sem);
+    }
+
+    lex_expect(JSON_PARSE_ARRAY_NEXT, lex, JSON_TOKEN_ARRAY_END);
+
+    if (aend != NULL)
+        (*aend) (sem->semstate);
+}
+
+/*
+ * Check whether supplied input is valid JSON.
+ */
+static void
+json_validate_cstring(char *input)
+{
+
+	JsonLexContext lex;
+
+	/* Set up lexing context. */
+	lex.input = input;
+	lex.token_terminator = lex.input;
+	lex.line_number = 1;
+	lex.line_start = input;
+	lex.strval = NULL; /* don't care about de-escaped lexemes */
+	
+	pg_parse_json(&lex, NullSemAction);
 }
 
 /*
 json_lex(JsonLexContext *lex)
 {
 	char	   *s;
-
+	char       buff[1024];
 	/* Skip leading whitespace. */
 	s = lex->token_terminator;
 	while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
-		s++;
+	{
+		if (*s == '\n')
+			++lex->line_number;
+		++s;
+	}
 	lex->token_start = s;
 
 	/* Determine token type. */
-	if (strchr("{}[],:", s[0]) != NULL)
+	if (*s == '\0')
+		lex->token_type =  JSON_TOKEN_END;
+	else if (strchr("{}[],:", s[0]))
 	{
-		/* strchr() is willing to match a zero byte, so test for that. */
+		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->token_terminator = s;
+			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])
+			{
+			    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;
+			}
 		}
-		lex->token_type = JSON_VALUE_INVALID;
 	}
 	else if (*s == '"')
 	{
 		/* String. */
 		json_lex_string(lex);
-		lex->token_type = JSON_VALUE_STRING;
+		lex->token_type = JSON_TOKEN_STRING;
 	}
 	else if (*s == '-')
 	{
 		/* Negative number. */
 		json_lex_number(lex, s + 1);
-		lex->token_type = JSON_VALUE_NUMBER;
+		lex->token_type = JSON_TOKEN_NUMBER;
 	}
 	else if (*s >= '0' && *s <= '9')
 	{
 		/* Positive number. */
 		json_lex_number(lex, s);
-		lex->token_type = JSON_VALUE_NUMBER;
+		lex->token_type = JSON_TOKEN_NUMBER;
 	}
 	else
 	{
-		char	   *p;
+		char   *p;
 
 		/*
-		 * We're not dealing with a string, number, legal punctuation mark, or
-		 * end of string.  The only legal tokens we might find here are true,
-		 * false, and null, but for error reporting purposes we scan until we
-		 * see a non-alphanumeric character.  That way, we can report the
-		 * whole word as an unexpected token, rather than just some
+		 * We're not dealing with a string, number, legal punctuation mark,
+		 * or end of string.  The only legal tokens we might find here are
+		 * true, false, and null, but for error reporting purposes we scan
+		 * until we see a non-alphanumeric character.  That way, we can report
+		 * the whole word as an unexpected token, rather than just some
 		 * unintuitive prefix thereof.
 		 */
-		for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
+ 		for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
 			/* skip */ ;
 
+		/*
+		 * We got some sort of unexpected punctuation or an otherwise
+		 * unexpected character, so just complain about that one character.
+		 */
 		if (p == s)
 		{
-			/*
-			 * 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->prev_token_terminator = lex->token_terminator;
 			lex->token_terminator = s + 1;
 			report_invalid_token(lex);
 		}
 		 * We've got a real alphanumeric token here.  If it happens to be
 		 * true, false, or null, all is well.  If not, error out.
 		 */
+		lex->prev_token_terminator = lex->token_terminator;
 		lex->token_terminator = p;
 		if (p - s == 4)
 		{
 			if (memcmp(s, "true", 4) == 0)
-				lex->token_type = JSON_VALUE_TRUE;
+				lex->token_type = JSON_TOKEN_TRUE;
 			else if (memcmp(s, "null", 4) == 0)
-				lex->token_type = JSON_VALUE_NULL;
+				lex->token_type = JSON_TOKEN_NULL;
 			else
 				report_invalid_token(lex);
 		}
 		else if (p - s == 5 && memcmp(s, "false", 5) == 0)
-			lex->token_type = JSON_VALUE_FALSE;
+			lex->token_type = JSON_TOKEN_FALSE;
 		else
 			report_invalid_token(lex);
+		strncpy(buff, s, p-s);
+		buff[p-s] = '\0';
 	}
 }
 
 {
 	char	   *s;
 
+	if (lex-> strval != NULL)
+		resetStringInfo(lex->strval);
+
 	for (s = lex->token_start + 1; *s != '"'; s++)
 	{
 		/* Per RFC4627, these characters MUST be escaped. */
 								 report_json_context(lex)));
 					}
 				}
+				if (lex->strval != NULL)
+				{
+					unsigned char utf8str[5];
+					int utf8len;
+					char *converted;
+
+					unicode_to_utf8(ch, utf8str);
+					utf8len = pg_utf_mblen(utf8str);
+					utf8str[0] = '\0';
+					converted = pg_any_to_server(utf8str, 1, PG_UTF8);
+					appendStringInfoString(lex->strval, converted);
+					if (converted != utf8str)
+						pfree(converted);
+					
+				}
 			}
-			else if (strchr("\"\\/bfnrt", *s) == NULL)
+			else if (lex->strval != 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("Escape sequence \"\\%s\" is invalid.",
-								   extract_mb_char(s)),
-						 report_json_context(lex)));
+				switch(*s)
+				{
+					case '"':
+					case '\\':
+					case '/':
+						appendStringInfoChar(lex->strval,*s);
+						break;
+					case 'b':
+						appendStringInfoChar(lex->strval,'\b');
+						break;
+					case 'f':
+						appendStringInfoChar(lex->strval,'\f');
+						break;
+					case 'n':
+						appendStringInfoChar(lex->strval,'\n');
+						break;
+					case 'r':
+						appendStringInfoChar(lex->strval,'\r');
+						break;
+					case 't':
+						appendStringInfoChar(lex->strval,'\t');
+						break;
+					default:
+						/* 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("Escape sequence \"\\%s\" is invalid.",
+										   extract_mb_char(s)),
+								 report_json_context(lex)));
+				}
 			}
+			else if (strchr("\"\\/bfnrt", *s) == NULL)
+            {
+				/* 
+				 * Simpler processing if we're not bothered about de-escaping
+				 */
+                lex->token_terminator = s + pg_mblen(s);
+                ereport(ERROR,
+                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                         errmsg("invalid input syntax for type json"),
+                         errdetail("Escape sequence \"\\%s\" is invalid.",
+                                   extract_mb_char(s)),
+                         report_json_context(lex)));
+            }
+
 		}
+		else if (lex->strval != NULL)
+		{
+			appendStringInfoChar(lex->strval,*s);
+		}
+
 	}
 
 	/* Hooray, we found the end of the string! */
+	lex->prev_token_terminator = lex->token_terminator;
 	lex->token_terminator = s + 1;
 }
 
  * (1) An optional minus sign ('-').
  *
  * (2) Either a single '0', or a string of one or more digits that does not
- *	   begin with a '0'.
+ *     begin with a '0'.
  *
  * (3) An optional decimal part, consisting of a period ('.') followed by
- *	   one or more digits.	(Note: While this part can be omitted
- *	   completely, it's not OK to have only the decimal point without
- *	   any digits afterwards.)
+ *     one or more digits.  (Note: While this part can be omitted
+ *     completely, it's not OK to have only the decimal point without
+ *     any digits afterwards.)
  *
  * (4) An optional exponent part, consisting of 'e' or 'E', optionally
- *	   followed by '+' or '-', followed by one or more digits.	(Note:
- *	   As with the decimal part, if 'e' or 'E' is present, it must be
- *	   followed by at least one digit.)
+ *     followed by '+' or '-', followed by one or more digits.  (Note:
+ *     As with the decimal part, if 'e' or 'E' is present, it must be
+ *     followed by at least one digit.)
  *
  * The 's' argument to this function points to the ostensible beginning
  * of part 2 - i.e. the character after any optional minus sign, and the
 static void
 json_lex_number(JsonLexContext *lex, char *s)
 {
-	bool		error = false;
-	char	   *p;
+	bool	error = false;
+	char   *p;
 
 	/* Part (1): leading sign indicator. */
 	/* Caller already did this for us; so do nothing. */
 
 	/* 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.  As in json_lex(), any alphanumeric stuff
-	 * here should be considered part of the token for error-reporting
-	 * purposes.
-	 */
+	/* 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 (error)
+	if (p > s || error)
 		report_invalid_token(lex);
 }
 
  * lex->token_start and lex->token_terminator must identify the current token.
  */
 static void
-report_parse_error(JsonParseStack *stack, JsonLexContext *lex)
+report_parse_error(JsonParseContext ctx, JsonLexContext *lex)
 {
 	char	   *token;
 	int			toklen;
 	token[toklen] = '\0';
 
 	/* Complain, with the appropriate detail message. */
-	if (stack == NULL)
+	if (ctx == JSON_PARSE_END)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type json"),
 				 report_json_context(lex)));
 	else
 	{
-		switch (stack->state)
+		switch (ctx)
 		{
 			case JSON_PARSE_VALUE:
 				ereport(ERROR,
 						 report_json_context(lex)));
 				break;
 			default:
-				elog(ERROR, "unexpected json parse state: %d",
-					 (int) stack->state);
+				elog(ERROR, "unexpected json parse state: %d", ctx);
 		}
 	}
 }

src/backend/utils/adt/jsonfuncs.c

+/*-------------------------------------------------------------------------
+ *
+ * jsonfuncs.c
+ *		Functions to process JSON data type.
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/jsonfuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "funcapi.h"
+#include "lib/stringinfo.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+
+
+static void okeys_object_start(void * state);
+static void okeys_object_end(void *state);
+static void okeys_object_field_start(void * state, char *fname, bool isnull);
+static void okeys_array_start(void * state);
+static void okeys_array_end(void *state);
+static void okeys_scalar(void *state, char *token, JsonTokenType tokentype);
+
+static void get_object_start(void * state);
+static void get_object_end(void *state);
+static void get_object_field_start(void * state, char *fname, bool isnull);
+static void get_object_field_end(void * state, char *fname, bool isnull);
+static void get_array_start(void * state);
+static void get_array_end(void *state);
+static void get_array_element_start(void * state, bool isnull);
+static void get_array_element_end(void * state, bool isnull);
+static void get_scalar(void *state, char *token, JsonTokenType tokentype);
+static text *get_worker(char *json, char * field, int elem_index, bool normalize_results);
+
+typedef enum
+{
+	JSON_SEARCH_OBJECT = 1,
+	JSON_SEARCH_ARRAY
+} JsonSearch;
+
+typedef struct
+{
+	int lex_level;
+	char **result;
+	int result_size;
+	int result_count;
+	int sent_count;
+} okeysState, *OkeysState;
+
+
+typedef struct
+{
+	JsonLexContext *lex;
+	int lex_level;
+	JsonSearch search_type;
+	int search_index;
+	int array_index;
+	char *search_term;
+	char *result_start;
+	text *tresult;
+	bool result_is_null;
+	bool normalize_results ;
+	bool next_scalar;
+} getState, *GetState;
+
+
+
+PG_FUNCTION_INFO_V1(json_object_keys);
+
+Datum
+json_object_keys(PG_FUNCTION_ARGS)
+{
+   FuncCallContext *funcctx;
+   OkeysState state;
+   int i;
+
+   if (SRF_IS_FIRSTCALL())
+   {
+	   text    *json = PG_GETARG_TEXT_P(0);
+	   char    *jsonstr = text_to_cstring(json);
+	   JsonLexContext	lex;
+	   JsonSemAction sem;
+
+	   MemoryContext oldcontext;
+
+	   funcctx = SRF_FIRSTCALL_INIT();
+	   oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+	   state = palloc(sizeof(okeysState));
+	   sem = palloc0(sizeof(jsonSemAction));
+	   
+	   state->result_size = 256;
+	   state->result_count = 0;
+	   state->sent_count = 0;
+	   state->lex_level = 0;
+	   state->result = palloc(256 * sizeof(char *));
+	   
+	   sem->semstate = (void *) state;
+	   sem->object_start = okeys_object_start;
+	   sem->object_end = okeys_object_end;
+	   sem->array_start = okeys_array_start;
+	   sem->array_end = okeys_array_end;
+	   sem->scalar = okeys_scalar;
+	   sem->object_field_start = okeys_object_field_start;
+	   /* remainder are all NULL, courtesy of palloc0 above */
+
+	   /* Set up lexing context. */
+	   lex.input = jsonstr;
+	   lex.token_terminator = lex.input;
+	   lex.line_number = 1;
+	   lex.line_start = jsonstr;
+	   lex.strval = makeStringInfo();
+
+	   pg_parse_json(&lex, sem);
+	   /* keys are now in state->result */
+
+	   pfree(lex.strval->data);
+	   pfree(lex.strval);
+	   pfree(sem);
+
+	   MemoryContextSwitchTo(oldcontext);
+	   funcctx->user_fctx = (void *) state;
+
+   }
+
+   funcctx = SRF_PERCALL_SETUP();
+   state = (OkeysState) funcctx->user_fctx;
+
+   if (state->sent_count < state->result_count)
+   {
+	   char *nxt = state->result[state->sent_count++];
+	   SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+   }
+
+   /* cleanup to reduce or eliminate memory leaks */
+   for (i = 0; i < state->result_count; i++)
+	   pfree(state->result[i]);
+   pfree(state->result);
+   pfree(state);
+   
+   SRF_RETURN_DONE(funcctx);
+}
+
+static void 
+okeys_object_start(void * state)
+{
+	OkeysState _state = (OkeysState) state;
+	_state->lex_level++;
+}
+
+static void 
+okeys_object_end(void *state)
+{
+	OkeysState _state = (OkeysState) state;
+	_state->lex_level--;
+}
+
+static void 
+okeys_object_field_start(void * state, char *fname, bool isnull)
+{
+	OkeysState _state = (OkeysState) state;
+	if (_state->lex_level != 1)
+		return;
+	if (_state->result_count >= _state->result_size)
+	{
+		_state->result_size *= 2;
+		_state->result = 
+			repalloc(_state->result, sizeof(char *) * _state->result_size);
+	}
+	_state->result[_state->result_count++] = pstrdup(fname);
+}
+
+static void 
+okeys_array_start(void * state)
+{
+	OkeysState _state = (OkeysState) state;
+	_state->lex_level++;
+	if (_state->lex_level == 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot call json_object_keys on an array")));		
+}
+
+static void 
+okeys_array_end(void *state)
+{
+	OkeysState _state = (OkeysState) state;
+	_state->lex_level--;
+}
+
+static void 
+okeys_scalar(void * state, char *token, JsonTokenType tokentype)
+{
+	OkeysState _state = (OkeysState) state;
+	if (_state->lex_level == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot call json_object_keys on a scalar")));
+		
+}
+
+/*
+ * json_get* functions
+ * these all use a common worker, just with some slightly
+ * different setup options.
+ */
+
+PG_FUNCTION_INFO_V1(json_get_ofield);
+
+Datum
+json_get_ofield(PG_FUNCTION_ARGS)
+{
+	text    *json = PG_GETARG_TEXT_P(0);
+	text    *fname = PG_GETARG_TEXT_P(1);	
+	char    *jsonstr = text_to_cstring(json);
+	char    *fnamestr = text_to_cstring(fname);
+	text    *result;
+
+	result = get_worker(jsonstr, fnamestr, -1, false);
+
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(json_get_ofield_as_text);
+
+Datum
+json_get_ofield_as_text(PG_FUNCTION_ARGS)
+{
+	text    *json = PG_GETARG_TEXT_P(0);
+	text    *fname = PG_GETARG_TEXT_P(1);	
+	char    *jsonstr = text_to_cstring(json);
+	char    *fnamestr = text_to_cstring(fname);
+	text    *result;
+
+	result = get_worker(jsonstr, fnamestr, -1, true);
+
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(json_get_aelem);
+
+Datum
+json_get_aelem(PG_FUNCTION_ARGS)
+{
+	text    *json = PG_GETARG_TEXT_P(0);
+	int      element  = PG_GETARG_INT32(1);	
+	char    *jsonstr = text_to_cstring(json);
+	text    *result;
+
+	result = get_worker(jsonstr, NULL, element, false);
+
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+}
+
+
+PG_FUNCTION_INFO_V1(json_get_aelem_as_text);
+
+Datum
+json_get_aelem_as_text(PG_FUNCTION_ARGS)
+{
+	text    *json = PG_GETARG_TEXT_P(0);
+	int      element  = PG_GETARG_INT32(1);	
+	char    *jsonstr = text_to_cstring(json);
+	text    *result;
+
+	result = get_worker(jsonstr, NULL, element, true);
+
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+}
+
+
+static text *
+get_worker(char *json, char * field, int elem_index, bool normalize_results)
+{
+	GetState state;
+	JsonLexContext	lex;
+	JsonSemAction sem;
+
+	state = palloc0(sizeof(getState));
+	sem = palloc0(sizeof(jsonSemAction));
+	
+	state->lex_level = 0;
+	state->lex = &lex;
+	state->normalize_results = normalize_results;
+	if (field != NULL)
+	{
+		state->search_type = JSON_SEARCH_OBJECT;
+		state->search_term = field;
+	}
+	else
+	{
+		state->search_type = JSON_SEARCH_ARRAY;
+		state->search_index = elem_index;
+		state->array_index = -1;
+	}
+	
+	sem->semstate = (void *) state;
+	sem->object_start = get_object_start;
+	sem->object_end = get_object_end;
+	sem->array_start = get_array_start;
+	sem->array_end = get_array_end;
+	sem->scalar = get_scalar;
+	if (field != NULL)
+	{
+		sem->object_field_start = get_object_field_start;
+		sem->object_field_end = get_object_field_end;
+	}
+	else
+	{
+		sem->array_element_start = get_array_element_start;
+		sem->array_element_end = get_array_element_end;
+	}
+
+	/* Set up lexing context. */
+	lex.input = json;
+	lex.token_terminator = lex.input;
+	lex.line_number = 1;
+	lex.line_start = json;
+	lex.strval = makeStringInfo();
+	
+	pg_parse_json(&lex, sem);
+
+	return state->tresult;
+}
+
+static void 
+get_object_start(void * state)
+{
+	GetState _state = (GetState) state;
+	_state->lex_level++;
+	if (_state->lex_level == 1 && _state->search_type != JSON_SEARCH_OBJECT)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot call json_get(int) on a non-array")));
+}
+
+static void 
+get_object_end(void *state)
+{
+	GetState _state = (GetState) state;
+	_state->lex_level--;
+}
+
+static void 
+get_object_field_start(void * state, char *fname, bool isnull)
+{
+	GetState _state = (GetState) state;
+
+	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT && 
+		strcmp(fname, _state->search_term) == 0)
+	{
+		if (_state->result_start != NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("field name is not unique in json object")));
+		if (_state->normalize_results && _state->lex->token_type == JSON_TOKEN_STRING)
+		{
+			_state->next_scalar = true; 
+		}
+		else
+		{
+			_state->result_start = _state->lex->token_start;
+		}
+	}
+}
+
+static void 
+get_object_field_end(void * state, char *fname, bool isnull)
+{
+	GetState _state = (GetState) state;
+
+	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT && 
+		_state->result_start != NULL && strcmp(fname, _state->search_term) == 0)
+	{
+		int len = _state->lex->prev_token_terminator - _state->result_start;
+		_state->tresult = cstring_to_text_with_len(_state->result_start, len);
+	}
+}
+
+static void 
+get_array_start(void * state)
+{
+	GetState _state = (GetState) state;
+	_state->lex_level++;
+	if (_state->lex_level == 1	&& _state->search_type != JSON_SEARCH_ARRAY)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot call json_get(fieldname) on a non-object")));
+}
+
+static void 
+get_array_end(void *state)
+{
+	GetState _state = (GetState) state;
+	_state->lex_level--;
+}
+
+static void 
+get_array_element_start(void *state, bool isnull)
+{
+	GetState _state = (GetState) state;
+
+	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_ARRAY)
+	{
+		_state->array_index++;
+		if (_state->array_index == _state->search_index)
+		{
+			if (_state->normalize_results && _state->lex->token_type == JSON_TOKEN_STRING)
+				_state->next_scalar = true; 
+			else
+				_state->result_start = _state->lex->token_start;
+		}
+	}
+}
+
+static void 
+get_array_element_end(void *state, bool isnull)
+{
+	GetState _state = (GetState) state;
+
+	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_ARRAY &&
+		_state->array_index == _state->search_index && _state->result_start != NULL)
+	{
+		int len = _state->lex->prev_token_terminator - _state->result_start;
+		_state->tresult = cstring_to_text_with_len(_state->result_start, len);
+	}
+}
+
+static void 
+get_scalar(void * state, char *token, JsonTokenType tokentype)
+{
+	GetState _state = (GetState) state;
+	if (_state->lex_level == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot call json_get on a scalar")));
+	if (_state->next_scalar)
+	{
+		_state->tresult=cstring_to_text(token);
+		_state->next_scalar = false;
+	}
+		
+}
+
+

src/include/catalog/pg_operator.h

 DATA(insert OID = 3900 (  "*"	   PGNSP PGUID b f f 3831 3831 3831 3900 0 range_intersect - - ));
 DESCR("range intersection");
 
+/* Use function oids here because json_get and json_get_as_text are overloaded */
+DATA(insert OID = 5100 (  "->"	   PGNSP PGUID b f f 114 25 114 0 0 5001 - - ));
+DESCR("get json object field");
+DATA(insert OID = 5101 (  "->>"	   PGNSP PGUID b f f 114 25 25 0 0 5002 - - ));
+DESCR("get json object field as text");
+DATA(insert OID = 5102 (  "->"	   PGNSP PGUID b f f 114 23 114 0 0 5003 - - ));
+DESCR("get json array element");
+DATA(insert OID = 5103 (  "->>"	   PGNSP PGUID b f f 114 23 25 0 0 5004 - - ));
+DESCR("get json array element as text");
+
 
 /*
  * function prototypes

src/include/catalog/pg_proc.h

 DATA(insert OID = 3156 (  row_to_json	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 114 "2249 16" _null_ _null_ _null_ _null_ row_to_json_pretty _null_ _null_ _null_ ));
 DESCR("map row to json with optional pretty printing");
 
+DATA(insert OID = 5001 (  json_get         PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 114 "114 25" _null_ _null_ _null_ _null_ json_get_ofield _null_ _null_ _null_ ));
+DESCR("get json object field");
+DATA(insert OID = 5002 (  json_get_as_text PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 25 "114 25" _null_ _null_ _null_ _null_ json_get_ofield_as_text _null_ _null_ _null_ ));
+DESCR("get json object field as text");
+DATA(insert OID = 5003 (  json_get         PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 114 "114 23" _null_ _null_ _null_ _null_ json_get_aelem _null_ _null_ _null_ ));
+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_ ));
+DESCR("get json object keys");
+
+
 /* uuid */
 DATA(insert OID = 2952 (  uuid_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ ));
 DESCR("I/O");

src/include/utils/json.h

 #include "fmgr.h"
 #include "lib/stringinfo.h"
 
+/* functions in json.c */
 extern Datum json_in(PG_FUNCTION_ARGS);
 extern Datum json_out(PG_FUNCTION_ARGS);
 extern Datum json_recv(PG_FUNCTION_ARGS);
 extern Datum row_to_json_pretty(PG_FUNCTION_ARGS);
 extern void escape_json(StringInfo buf, const char *str);
 
+/* functions in jsonfuncs.c */
+extern Datum json_get_aelem_as_text(PG_FUNCTION_ARGS);
+extern Datum json_get_aelem(PG_FUNCTION_ARGS);
+extern Datum json_get_ofield_as_text(PG_FUNCTION_ARGS);
+extern Datum json_get_ofield(PG_FUNCTION_ARGS);
+extern Datum json_object_keys(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */

src/include/utils/jsonapi.h

+/*-------------------------------------------------------------------------
+ *
+ * json.h
+ *	  Declarations for JSON API support.
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/json.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef JSONAPI_H
+#define JSONAPI_H
+
+#include "lib/stringinfo.h"
+
+typedef enum
+{
+    JSON_TOKEN_INVALID,
+    JSON_TOKEN_STRING,
+    JSON_TOKEN_NUMBER,
+    JSON_TOKEN_OBJECT_START,
+    JSON_TOKEN_OBJECT_END,
+    JSON_TOKEN_ARRAY_START,
+    JSON_TOKEN_ARRAY_END,
+    JSON_TOKEN_COMMA,
+    JSON_TOKEN_COLON,
+    JSON_TOKEN_TRUE,
+    JSON_TOKEN_FALSE,
+    JSON_TOKEN_NULL,
+    JSON_TOKEN_END,
+} JsonTokenType;
+
+typedef struct
+{
+    char       *input;
+    char       *token_start;
+    char       *token_terminator;
+    char       *prev_token_terminator;
+    JsonTokenType   token_type;
+    int         line_number;
+    char       *line_start;
+    StringInfo  strval;
+} JsonLexContext;
+
+typedef void (* json_struct_action) (void * state);
+typedef void (* json_ofield_action) (void * state, char *fname, bool isnull);
+typedef void (* json_aelem_action)  (void * state, bool isnull);
+typedef void (* json_scalar_action) (void * state, char *token, JsonTokenType tokentype);
+
+
+/*
+ * any of these actions can be NULL, in whi9ch case nothig is done.
+ */
+typedef struct
+{
+    void    *semstate;
+    json_struct_action object_start;
+    json_struct_action object_end;
+    json_struct_action array_start;
+    json_struct_action array_end;
+    json_ofield_action object_field_start;
+    json_ofield_action object_field_end;
+    json_aelem_action  array_element_start;
+    json_aelem_action  array_element_end;
+    json_scalar_action scalar;
+} jsonSemAction, *JsonSemAction;
+
+/*
+ * parse_json will parse the string in the lex calling the
+ * action functions in sem at the appropriate points. It is
+ * up to them to keep what state they need  in semstate. If they
+ * need access to the state of the lexer, then its pointer
+ * should be passed to them as a member of whatever semstate
+ * points to. If the action pointers are NULL the parser
+ * does nothing and just continues.
+ */
+extern void pg_parse_json(JsonLexContext *lex, JsonSemAction sem);
+
+#endif   /* JSONAPI_H */

src/test/regress/sql/json.sql

 -- json input
 SELECT row_to_json(q)
 FROM (SELECT '{"a":1,"b": [2,3,4,"d","e","f"],"c":{"p":1,"q":2}}'::json AS "jsonfield") q;
+
+
+-- 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';
+
+SELECT json_get(test_json,'x') 
+FROM test_json
+WHERE json_type = 'array';
+
+SELECT json_get(test_json,'x') 
+FROM test_json
+WHERE json_type = 'object';
+
+SELECT json_get(test_json,'field2') 
+FROM test_json
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_json
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2' 
+FROM test_json
+WHERE json_type = 'object';
+
+SELECT json_get(test_json,2) 
+FROM test_json
+WHERE json_type = 'scalar';
+
+SELECT json_get(test_json,2) 
+FROM test_json
+WHERE json_type = 'array';
+
+SELECT json_get(test_json,2)
+FROM test_json
+WHERE json_type = 'object';
+
+SELECT json_get(test_json,2) 
+FROM test_json
+WHERE json_type = 'array';
+
+SELECT test_json->2 
+FROM test_json
+WHERE json_type = 'array';
+
+SELECT test_json->>2
+FROM test_json
+WHERE json_type = 'array';
+
+SELECT json_object_keys(test_json)
+FROM test_json
+WHERE json_type = 'scalar';
+
+SELECT json_object_keys(test_json)
+FROM test_json
+WHERE json_type = 'array';
+
+SELECT json_object_keys(test_json)
+FROM test_json
+WHERE json_type = 'object';
+
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.