Commits

Andrew Dunstan  committed cdddd17

Improve Json API somewhat.

Add a constructor for a JsonLexContext, and add a lex_level
value to the object, which is maintained by the parser.

The means that there is less work to do in setting up and less
semantic actions are required. All this means there are less
chances to go wrong.

  • Participants
  • Parent commits c71a6b8

Comments (0)

Files changed (3)

File src/backend/utils/adt/json.c

 }
 
 /*
+ * lex constructor, with or without StringInfo object
+ * for de-escaped lexemes.
+ */
+
+JsonLexContext *
+makeJsonLexContext(char * json, bool need_escapes)
+{
+	JsonLexContext * lex = palloc0(sizeof(JsonLexContext));
+	
+	lex->input = lex->token_terminator = lex->line_start = json;
+	lex->line_number = 1;
+	if (need_escapes)
+		lex->strval = makeStringInfo();
+	return lex;
+}
+
+/*
  * parse routines
  */
 void
     if (ostart != NULL)
         (*ostart) (sem->semstate);
 
+	lex->lex_level++;
+
 	/* 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)
 
     lex_expect(JSON_PARSE_OBJECT_NEXT, lex, JSON_TOKEN_OBJECT_END);
 
+	lex->lex_level--;
+
     if (oend != NULL)
         (*oend) (sem->semstate);
 }
     if (astart != NULL)
         (*astart) (sem->semstate);
 
+	lex->lex_level++;
+
     lex_expect(JSON_PARSE_ARRAY_START, lex, JSON_TOKEN_ARRAY_START);
     if (lex_peek(lex) != JSON_TOKEN_ARRAY_END)
     {
 
     lex_expect(JSON_PARSE_ARRAY_NEXT, lex, JSON_TOKEN_ARRAY_END);
 
+	lex->lex_level--;
+
     if (aend != NULL)
         (*aend) (sem->semstate);
 }
 json_validate_cstring(char *input)
 {
 
-	JsonLexContext lex;
+	JsonLexContext *lex = makeJsonLexContext(input, false);
 
-	/* 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);
+	pg_parse_json(lex, NullSemAction);
 }
 
 /*

File src/backend/utils/adt/jsonfuncs.c

 #include "utils/typcache.h"
 
 /* semantic action functions for json_object_keys */
-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);
 
 /* semantic action functions for json_get* functions */
 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);
 
 /* semantic action functions for json_array_length */
 static void alen_object_start(void *state);
-static void alen_array_start(void *state);
-static void alen_array_end(void *state);
 static void alen_scalar(void *state, char *token, JsonTokenType tokentype);
 static void alen_array_element_start(void *state, bool isnull);
 
 /* semantic action functions for json_each */
-static void each_object_start(void *state);
-static void each_object_end(void *state);
 static void each_object_field_start(void *state, char *fname, bool isnull);
 static void each_object_field_end(void *state, char *fname, bool isnull);
 static void each_array_start(void *state);
 
 /* semantic action functions for json_unnest */
 static void unnest_object_start(void *state);
-static void unnest_array_start(void *state);
-static void unnest_array_end(void *state);
 static void unnest_array_element_start(void *state, bool isnull);
 static void unnest_array_element_end(void *state, bool isnull);
 static void unnest_scalar(void *state, char *token, JsonTokenType tokentype);
 static HTAB *get_json_object_as_hash(char *jsonstr, char *funcname);
 
 /* semantic action functions for get_json_object_as_hash */
-static void hash_object_start(void *state);
 static void hash_object_field_start(void *state, char *fname, bool isnull);
 static void hash_object_field_end(void *state, char *fname, bool isnull);
 static void hash_array_start(void *state);
 /* state for json_object_keys */
 typedef struct
 {
-	int			lex_level;
+	JsonLexContext *lex;
 	char	  **result;
 	int			result_size;
 	int			result_count;
 typedef struct
 {
 	JsonLexContext *lex;
-	int			lex_level;
 	JsonSearch	search_type;
 	int			search_index;
 	int			array_index;
 /* state for json_array_length */
 typedef struct
 {
-	int			lex_level;
+	JsonLexContext *lex;
 	int			count;
 }	alenState, *AlenState;
 
 typedef struct
 {
 	JsonLexContext *lex;
-	int			lex_level;
 	Tuplestorestate *tuple_store;
 	TupleDesc	ret_tdesc;
 	MemoryContext tmp_cxt;
 typedef struct
 {
 	JsonLexContext *lex;
-	int			lex_level;
 	Tuplestorestate *tuple_store;
 	TupleDesc	ret_tdesc;
 	MemoryContext tmp_cxt;
 typedef struct
 {
 	JsonLexContext *lex;
-	int			lex_level;
 	HTAB	   *hash;
 	char	   *saved_scalar;
 	char	   *function_name;
 	{
 		text	   *json = PG_GETARG_TEXT_P(0);
 		char	   *jsonstr = text_to_cstring(json);
-		JsonLexContext lex;
+		JsonLexContext *lex = makeJsonLexContext(jsonstr,true);
 		JsonSemAction sem;
 
 		MemoryContext oldcontext;
 		state = palloc(sizeof(okeysState));
 		sem = palloc0(sizeof(jsonSemAction));
 
+		state->lex = lex;
 		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);
+		pg_parse_json(lex, sem);
 		/* keys are now in state->result */
 
-		pfree(lex.strval->data);
-		pfree(lex.strval);
+		pfree(lex->strval->data);
+		pfree(lex->strval);
+		pfree(lex);
 		pfree(sem);
 
 		MemoryContextSwitchTo(oldcontext);
 }
 
 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)
+	if (_state->lex->lex_level != 1)
 		return;
 	if (_state->result_count >= _state->result_size)
 	{
 {
 	OkeysState	_state = (OkeysState) state;
 
-	_state->lex_level++;
-	if (_state->lex_level == 1)
+	if (_state->lex->lex_level == 0)
 		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)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_object_keys on a scalar")));
-
 }
 
 /*
 		   bool normalize_results)
 {
 	GetState	state;
-	JsonLexContext lex;
+	JsonLexContext *lex = makeJsonLexContext(json, true);
 	JsonSemAction sem;
 
 	state = palloc0(sizeof(getState));
 	sem = palloc0(sizeof(jsonSemAction));
 
-	state->lex_level = 0;
-	state->lex = &lex;
+	state->lex = lex;
 	state->normalize_results = normalize_results;
 	if (field != NULL)
 	{
 
 	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 || path != NULL)
 	{
 		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);
+	pg_parse_json(lex, sem);
 
 	return state->tresult;
 }
 {
 	GetState	_state = (GetState) state;
 
-	_state->lex_level++;
-	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_ARRAY)
+	if (_state->lex->lex_level == 0 && _state->search_type == JSON_SEARCH_ARRAY)
 		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;
 	bool		get_next = false;
+	int lex_level = _state->lex->lex_level;
 
-	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT &&
+	if ( lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT &&
 		strcmp(fname, _state->search_term) == 0)
 	{
 		get_next = true;
 	}
 	else if (_state->search_type == JSON_SEARCH_PATH &&
-			 _state->lex_level <= _state->npath &&
-			 _state->pathok[_state->lex_level - 1] &&
-			 strcmp(fname, _state->path[_state->lex_level - 1]) == 0)
+			 lex_level <= _state->npath &&
+			 _state->pathok[_state->lex->lex_level - 1] &&
+			 strcmp(fname, _state->path[lex_level - 1]) == 0)
 	{
-		if (_state->lex_level < _state->npath)
-			_state->pathok[_state->lex_level] = true;
+		if (lex_level < _state->npath)
+			_state->pathok[lex_level] = true;
 
-		if (_state->lex_level == _state->npath)
+		if (lex_level == _state->npath)
 			get_next = true;
 	}
 
 {
 	GetState	_state = (GetState) state;
 	bool		get_last = false;
+	int         lex_level = _state->lex->lex_level;
 
-	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT &&
+	if (lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT &&
 		strcmp(fname, _state->search_term) == 0)
 	{
 		get_last = true;
 	}
 	else if (_state->search_type == JSON_SEARCH_PATH &&
-			 _state->lex_level <= _state->npath &&
-			 _state->pathok[_state->lex_level - 1] &&
-			 strcmp(fname, _state->path[_state->lex_level - 1]) == 0)
+			 lex_level <= _state->npath &&
+			 _state->pathok[lex_level - 1] &&
+			 strcmp(fname, _state->path[lex_level - 1]) == 0)
 	{
 		/* done with this field so reset pathok */
-		if (_state->lex_level < _state->npath)
-			_state->pathok[_state->lex_level] = false;
+		if (lex_level < _state->npath)
+			_state->pathok[lex_level] = false;
 
-		if (_state->lex_level == _state->npath)
+		if (lex_level == _state->npath)
 			get_last = true;
 	}
 
 get_array_start(void *state)
 {
 	GetState	_state = (GetState) state;
+	int lex_level = _state->lex->lex_level;
 
-	_state->lex_level++;
-	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT)
+	if (lex_level == 0 && _state->search_type == JSON_SEARCH_OBJECT)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_get(fieldname) on a non-object")));
-	else if (_state->search_type == JSON_SEARCH_PATH &&
-			 _state->lex_level <= _state->npath)
-		_state->array_level_index[_state->lex_level - 1] = -1;
-}
-
-static void
-get_array_end(void *state)
-{
-	GetState	_state = (GetState) state;
-
-	_state->lex_level--;
+	else if (_state->search_type == JSON_SEARCH_PATH && 
+			 lex_level <= _state->npath)
+		_state->array_level_index[lex_level] = -1;
 }
 
 static void
 {
 	GetState	_state = (GetState) state;
 	bool		get_next = false;
+	int         lex_level = _state->lex->lex_level;
 
-	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_ARRAY)
+	if (lex_level == 1 && _state->search_type == JSON_SEARCH_ARRAY)
 	{
 		_state->array_index++;
 		if (_state->array_index == _state->search_index)
 			get_next = true;
 	}
 	else if (_state->search_type == JSON_SEARCH_PATH &&
-			 _state->lex_level <= _state->npath &&
-			 _state->pathok[_state->lex_level - 1])
+			 lex_level <= _state->npath &&
+			 _state->pathok[lex_level - 1])
 	{
-		if (++_state->array_level_index[_state->lex_level - 1] ==
-			_state->path_level_index[_state->lex_level - 1])
+		if (++_state->array_level_index[lex_level - 1] ==
+			_state->path_level_index[lex_level - 1])
 		{
-			if (_state->lex_level == _state->npath)
+			if (lex_level == _state->npath)
 				get_next = true;
 			else
-				_state->pathok[_state->lex_level] = true;
+				_state->pathok[lex_level] = true;
 		}
 
 	}
 {
 	GetState	_state = (GetState) state;
 	bool		get_last = false;
+	int         lex_level = _state->lex->lex_level;
 
-	if (_state->lex_level == 1 && _state->search_type == JSON_SEARCH_ARRAY &&
+	if (lex_level == 1 && _state->search_type == JSON_SEARCH_ARRAY &&
 		_state->array_index == _state->search_index)
 	{
 		get_last = true;
 	}
 	else if (_state->search_type == JSON_SEARCH_PATH &&
-			 _state->lex_level <= _state->npath &&
-			 _state->pathok[_state->lex_level - 1] &&
-			 _state->array_level_index[_state->lex_level - 1] ==
-			 _state->path_level_index[_state->lex_level - 1])
+			 lex_level <= _state->npath &&
+			 _state->pathok[lex_level - 1] &&
+			 _state->array_level_index[lex_level - 1] ==
+			 _state->path_level_index[lex_level - 1])
 	{
 		/* done with this element so reset pathok */
-		if (_state->lex_level < _state->npath)
-			_state->pathok[_state->lex_level] = false;
+		if (lex_level < _state->npath)
+			_state->pathok[lex_level] = false;
 
-		if (_state->lex_level == _state->npath)
+		if (lex_level == _state->npath)
 			get_last = true;
 	}
 	if (get_last && _state->result_start != NULL)
 {
 	GetState	_state = (GetState) state;
 
-	if (_state->lex_level == 0 && _state->search_type != JSON_SEARCH_PATH)
+	if (_state->lex->lex_level == 0 && _state->search_type != JSON_SEARCH_PATH)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_get on a scalar")));
 
 }
 
-
 /*
  * SQL function json_array_length
  */
 	char	   *jsonstr = text_to_cstring(json);
 
 	AlenState	state;
-	JsonLexContext lex;
+	JsonLexContext *lex = makeJsonLexContext(jsonstr,false);
 	JsonSemAction sem;
 
 	state = palloc0(sizeof(alenState));
 
 	/* palloc0 does this for us */
 #if 0
-	state->lex_level = 0;
 	state->count = 0;
 #endif
+	state->lex = lex;
 
 	sem->semstate = (void *) state;
 	sem->object_start = alen_object_start;
-	sem->array_start = alen_array_start;
-	sem->array_end = alen_array_end;
 	sem->scalar = alen_scalar;
 	sem->array_element_start = alen_array_element_start;
 
-	/* Set up lexing context. */
-	lex.input = jsonstr;
-	lex.token_terminator = lex.input;
-	lex.line_number = 1;
-	lex.line_start = jsonstr;
-	lex.strval = NULL;			/* just counting, so this is faster */
-
-	pg_parse_json(&lex, sem);
+	pg_parse_json(lex, sem);
 
 	PG_RETURN_INT32(state->count);
 }
 
-
-static void
-alen_array_start(void *state)
-{
-	AlenState	_state = (AlenState) state;
-
-	_state->lex_level++;
-}
-
-static void
-alen_array_end(void *state)
-{
-	AlenState	_state = (AlenState) state;
-
-	_state->lex_level--;
-}
-
 static void
 alen_object_start(void *state)
 {
 	AlenState	_state = (AlenState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_array_length on an object")));
 {
 	AlenState	_state = (AlenState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_array_length on a scalar")));
 {
 	AlenState	_state = (AlenState) state;
 
-	if (_state->lex_level == 1)
+	if (_state->lex->lex_level == 1)
 		_state->count++;
 }
 
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
 	char	   *jsonstr = text_to_cstring(json);
-	JsonLexContext lex;
+	JsonLexContext *lex = makeJsonLexContext(jsonstr, true);
 	JsonSemAction sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	MemoryContextSwitchTo(old_cxt);
 
 	sem->semstate = (void *) state;
-	sem->object_start = each_object_start;
-	sem->object_end = each_object_end;
 	sem->array_start = each_array_start;
 	sem->scalar = each_scalar;
 	sem->object_field_start = each_object_field_start;
 	sem->object_field_end = each_object_field_end;
 
-	/* Set up lexing context. */
-	lex.input = jsonstr;
-	lex.token_terminator = lex.input;
-	lex.line_number = 1;
-	lex.line_start = jsonstr;
-	lex.strval = makeStringInfo();
-
 	state->normalize_results = false;
 	state->next_scalar = false;
 
-	state->lex = &lex;
-	state->lex_level = 0;
+	state->lex = lex;
 	state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
 										   "json_each temporary cxt",
 										   ALLOCSET_DEFAULT_MINSIZE,
 										   ALLOCSET_DEFAULT_INITSIZE,
 										   ALLOCSET_DEFAULT_MAXSIZE);
 
-	pg_parse_json(&lex, sem);
+	pg_parse_json(lex, sem);
 
 	rsi->setResult = state->tuple_store;
 	rsi->setDesc = state->ret_tdesc;
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
 	char	   *jsonstr = text_to_cstring(json);
-	JsonLexContext lex;
+	JsonLexContext *lex = makeJsonLexContext(jsonstr, true);
 	JsonSemAction sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	MemoryContextSwitchTo(old_cxt);
 
 	sem->semstate = (void *) state;
-	sem->object_start = each_object_start;
-	sem->object_end = each_object_end;
 	sem->array_start = each_array_start;
 	sem->scalar = each_scalar;
 	sem->object_field_start = each_object_field_start;
 	sem->object_field_end = each_object_field_end;
 
-	/* Set up lexing context. */
-	lex.input = jsonstr;
-	lex.token_terminator = lex.input;
-	lex.line_number = 1;
-	lex.line_start = jsonstr;
-	lex.strval = makeStringInfo();
-
 	/* next line is what's different from json_each */
 	state->normalize_results = true;
 	state->next_scalar = false;
 
-	state->lex = &lex;
-	state->lex_level = 0;
+	state->lex = lex;
 	state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
 										   "json_each temporary cxt",
 										   ALLOCSET_DEFAULT_MINSIZE,
 										   ALLOCSET_DEFAULT_INITSIZE,
 										   ALLOCSET_DEFAULT_MAXSIZE);
 
-	pg_parse_json(&lex, sem);
+	pg_parse_json(lex, sem);
 
 	rsi->setResult = state->tuple_store;
 	rsi->setDesc = state->ret_tdesc;
 }
 
 static void
-each_object_start(void *state)
-{
-	EachState	_state = (EachState) state;
-
-	_state->lex_level++;
-}
-static void
-each_object_end(void *state)
-{
-	EachState	_state = (EachState) state;
-
-	_state->lex_level--;
-}
-static void
 each_object_field_start(void *state, char *fname, bool isnull)
 {
 	EachState	_state = (EachState) state;
 
 	/* save a pointer to where the value starts */
-	if (_state->lex_level == 1)
+	if (_state->lex->lex_level == 1)
 	{
 		/*
 		 * next_scalar will be reset in the object_field_end handler, and
 	static bool nulls[2] = {false, false};
 
 	/* skip over nested objects */
-	if (_state->lex_level != 1)
+	if (_state->lex->lex_level != 1)
 		return;
 
 	/* use the tmp context so we can clean up after each tuple is done */
 {
 	EachState	_state = (EachState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_each on an array")));
 {
 	EachState	_state = (EachState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_each on a scalar")));
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
 	char	   *jsonstr = text_to_cstring(json);
-	JsonLexContext lex;
+	JsonLexContext *lex = makeJsonLexContext(jsonstr, true);
 	JsonSemAction sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 
 	sem->semstate = (void *) state;
 	sem->object_start = unnest_object_start;
-	sem->array_start = unnest_array_start;
-	sem->array_end = unnest_array_end;
 	sem->scalar = unnest_scalar;
 	sem->array_element_start = unnest_array_element_start;
 	sem->array_element_end = unnest_array_element_end;
 
-	/* Set up lexing context. */
-	lex.input = jsonstr;
-	lex.token_terminator = lex.input;
-	lex.line_number = 1;
-	lex.line_start = jsonstr;
-	lex.strval = makeStringInfo();
-
-	state->lex = &lex;
-	state->lex_level = 0;
+	state->lex = lex;
 	state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
 										   "json_unnest temporary cxt",
 										   ALLOCSET_DEFAULT_MINSIZE,
 										   ALLOCSET_DEFAULT_INITSIZE,
 										   ALLOCSET_DEFAULT_MAXSIZE);
 
-	pg_parse_json(&lex, sem);
+	pg_parse_json(lex, sem);
 
 	rsi->setResult = state->tuple_store;
 	rsi->setDesc = state->ret_tdesc;
 }
 
 static void
-unnest_array_start(void *state)
-{
-	UnnestState _state = (UnnestState) state;
-
-	_state->lex_level++;
-}
-
-static void
-unnest_array_end(void *state)
-{
-	UnnestState _state = (UnnestState) state;
-
-	_state->lex_level--;
-}
-
-static void
 unnest_array_element_start(void *state, bool isnull)
 {
 	UnnestState _state = (UnnestState) state;
 
 	/* save a pointer to where the value starts */
-	if (_state->lex_level == 1)
+	if (_state->lex->lex_level == 1)
 		_state->result_start = _state->lex->token_start;
 }
 
 	static bool nulls[1] = {false};
 
 	/* skip over nested objects */
-	if (_state->lex_level != 1)
+	if (_state->lex->lex_level != 1)
 		return;
 
 	/* use the tmp context so we can clean up after each tuple is done */
 {
 	UnnestState _state = (UnnestState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_unnest on an object")));
 {
 	UnnestState _state = (UnnestState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("cannot call json_unnest on a scalar")));
 	HASHCTL		ctl;
 	HTAB	   *tab;
 	JHashState	state;
-	JsonLexContext lex;
+	JsonLexContext *lex = makeJsonLexContext(jsonstr, true);
 	JsonSemAction sem;
 
 	memset(&ctl, 0, sizeof(ctl));
 	state = palloc0(sizeof(jhashState));
 	sem = palloc0(sizeof(jsonSemAction));
 
-	/* palloc0 does this for us */
-#if 0
-	state->lex_level = 0;
-#endif
-
 	state->function_name = funcname;
 	state->hash = tab;
-	state->lex = &lex;
+	state->lex = lex;
 
 	sem->semstate = (void *) state;
-	sem->object_start = hash_object_start;
 	sem->array_start = hash_array_start;
 	sem->scalar = hash_scalar;
 	sem->object_field_start = hash_object_field_start;
 	sem->object_field_end = hash_object_field_end;
 
-	/* 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);
+	pg_parse_json(lex, sem);
 
 	return tab;
 }
 
 static void
-hash_object_start(void *state)
-{
-	JHashState	_state = (JHashState) state;
-
-	_state->lex_level++;
-}
-
-static void
 hash_object_field_start(void *state, char *fname, bool isnull)
 {
 	JHashState	_state = (JHashState) state;
 {
 	JHashState	_state = (JHashState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 			   errmsg("cannot call %s on an array", _state->function_name)));
 {
 	JHashState	_state = (JHashState) state;
 
-	if (_state->lex_level == 0)
+	if (_state->lex->lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 			   errmsg("cannot call %s on an array", _state->function_name)));

File src/include/utils/jsonapi.h

 	char	   *token_terminator;
 	char	   *prev_token_terminator;
 	JsonTokenType token_type;
+	int         lex_level;
 	int			line_number;
 	char	   *line_start;
 	StringInfo	strval;
  */
 extern void pg_parse_json(JsonLexContext * lex, JsonSemAction sem);
 
+/* constructor for JsonLexContext, with or without strval element */
+extern JsonLexContext *makeJsonLexContext(char * json, bool need_escapes);
+
 #endif   /* JSONAPI_H */