0010-Relocating-jsonapi.c.patch

application/octet-stream

Filename: 0010-Relocating-jsonapi.c.patch
Type: application/octet-stream
Part: 4
Message: Re: making the backend's json parser work in frontend code

Patch

Format: format-patch
Series: patch 0010
Subject: Relocating jsonapi.c
File+
src/backend/utils/adt/jsonb.c 1 1
src/backend/utils/adt/json.c 1 1
src/backend/utils/adt/jsonfuncs.c 4 3
src/backend/utils/adt/Makefile 0 1
src/common/jsonapi.c 122 151
src/common/Makefile 1 0
src/include/common/jsonapi.h 16 3
src/include/utils/jsonfuncs.h 1 1
From a2c7d0e798004fc5c22ef8eef204a8eefc19e4a5 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Thu, 23 Jan 2020 11:39:55 -0800
Subject: [PATCH 10/11] Relocating jsonapi.c

Moving src/backend/utils/adt/jsonapi.c to src/common/jsonapi.c
and reworking the code to not include elog, pg_mblen and similar
backend-only functions.

Along the way, simplifying Robert's implementation with macros
to make the json parsing code more compact and (to my eyes)
easier to read.
---
 src/backend/utils/adt/Makefile              |   1 -
 src/backend/utils/adt/json.c                |   2 +-
 src/backend/utils/adt/jsonb.c               |   2 +-
 src/backend/utils/adt/jsonfuncs.c           |   7 +-
 src/common/Makefile                         |   1 +
 src/{backend/utils/adt => common}/jsonapi.c | 273 +++++++++-----------
 src/include/common/jsonapi.h                |  19 +-
 src/include/utils/jsonfuncs.h               |   2 +-
 8 files changed, 146 insertions(+), 161 deletions(-)
 rename src/{backend/utils/adt => common}/jsonapi.c (84%)

diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 790d7a24fb..13efa9338c 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -44,7 +44,6 @@ OBJS = \
 	int.o \
 	int8.o \
 	json.o \
-	jsonapi.o \
 	jsonb.o \
 	jsonb_gin.o \
 	jsonb_op.o \
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 8333711988..3f9a3c9e6e 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -129,7 +129,7 @@ json_recv(PG_FUNCTION_ARGS)
 	str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
 
 	/* Validate it. */
-	lex = makeJsonLexContextCstringLen(str, nbytes, false);
+	lex = makeJsonLexContextCstringLen(str, nbytes, GetDatabaseEncoding(), false);
 	pg_parse_json_or_ereport(lex, &nullSemAction);
 
 	PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index d1d4542f74..1f85f68e30 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -262,7 +262,7 @@ jsonb_from_cstring(char *json, int len)
 
 	memset(&state, 0, sizeof(state));
 	memset(&sem, 0, sizeof(sem));
-	lex = makeJsonLexContextCstringLen(json, len, true);
+	lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
 
 	sem.semstate = (void *) &state;
 
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index d5e2c56f14..2b403ea7da 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -495,7 +495,7 @@ static void transform_string_values_scalar(void *state, char *token, JsonTokenTy
  * ereport(ERROR).
  */
 void
-pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem)
+pg_parse_json_or_ereport(JsonLexContext *lex, const JsonSemAction *sem)
 {
 	JsonParseErrorType	result;
 
@@ -515,6 +515,7 @@ makeJsonLexContext(text *json, bool need_escapes)
 {
 	return makeJsonLexContextCstringLen(VARDATA_ANY(json),
 										VARSIZE_ANY_EXHDR(json),
+										GetDatabaseEncoding(),
 										need_escapes);
 }
 
@@ -2606,7 +2607,7 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len)
 	PopulateArrayState state;
 	JsonSemAction sem;
 
-	state.lex = makeJsonLexContextCstringLen(json, len, true);
+	state.lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
 	state.ctx = ctx;
 
 	memset(&sem, 0, sizeof(sem));
@@ -3449,7 +3450,7 @@ get_json_object_as_hash(char *json, int len, const char *funcname)
 	HASHCTL		ctl;
 	HTAB	   *tab;
 	JHashState *state;
-	JsonLexContext *lex = makeJsonLexContextCstringLen(json, len, true);
+	JsonLexContext *lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
 	JsonSemAction *sem;
 
 	memset(&ctl, 0, sizeof(ctl));
diff --git a/src/common/Makefile b/src/common/Makefile
index 3882cd7a3d..e9caa08418 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -56,6 +56,7 @@ OBJS_COMMON = \
 	f2s.o \
 	file_perm.o \
 	ip.o \
+	jsonapi.o \
 	keywords.o \
 	kwlookup.o \
 	link-canary.o \
diff --git a/src/backend/utils/adt/jsonapi.c b/src/common/jsonapi.c
similarity index 84%
rename from src/backend/utils/adt/jsonapi.c
rename to src/common/jsonapi.c
index ee26dc324f..49e1bb44c9 100644
--- a/src/backend/utils/adt/jsonapi.c
+++ b/src/common/jsonapi.c
@@ -11,12 +11,25 @@
  *
  *-------------------------------------------------------------------------
  */
+#ifndef FRONTEND
 #include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
 
 #include "common/jsonapi.h"
 #include "common/pg_wchar.h"
+
+#ifndef FRONTEND
 #include "miscadmin.h"
-#include "utils/mbutils.h"
+#endif
+
+#define INSIST(x) \
+do { \
+	JsonParseErrorType	parse_result; \
+	if((parse_result = (x)) != JSON_SUCCESS) \
+		return parse_result; \
+} while (0)
 
 /*
  * The context of the parser is maintained by the recursive descent
@@ -36,19 +49,20 @@ typedef enum					/* contexts of JSON parser */
 	JSON_PARSE_END				/* saw the end of a document, expect nothing */
 } JsonParseContext;
 
+static inline JsonParseErrorType lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token);
 static inline JsonParseErrorType json_lex_string(JsonLexContext *lex);
 static inline JsonParseErrorType json_lex_number(JsonLexContext *lex, char *s,
 								   bool *num_err, int *total_len);
-static inline JsonParseErrorType parse_scalar(JsonLexContext *lex, JsonSemAction *sem);
-static JsonParseErrorType parse_object_field(JsonLexContext *lex, JsonSemAction *sem);
-static JsonParseErrorType parse_object(JsonLexContext *lex, JsonSemAction *sem);
-static JsonParseErrorType parse_array_element(JsonLexContext *lex, JsonSemAction *sem);
-static JsonParseErrorType parse_array(JsonLexContext *lex, JsonSemAction *sem);
+static inline JsonParseErrorType parse_scalar(JsonLexContext *lex, const JsonSemAction *sem);
+static JsonParseErrorType parse_object_field(JsonLexContext *lex, const JsonSemAction *sem);
+static JsonParseErrorType parse_object(JsonLexContext *lex, const JsonSemAction *sem);
+static JsonParseErrorType parse_array_element(JsonLexContext *lex, const JsonSemAction *sem);
+static JsonParseErrorType parse_array(JsonLexContext *lex, const JsonSemAction *sem);
 static JsonParseErrorType report_parse_error(JsonParseContext ctx, JsonLexContext *lex);
 static char *extract_token(JsonLexContext *lex);
 
 /* the null action object used for pure validation */
-JsonSemAction nullSemAction =
+const JsonSemAction nullSemAction =
 {
 	NULL, NULL, NULL, NULL, NULL,
 	NULL, NULL, NULL, NULL, NULL
@@ -56,32 +70,6 @@ JsonSemAction nullSemAction =
 
 /* Recursive Descent parser support routines */
 
-/*
- * lex_peek
- *
- * what is the current look_ahead token?
-*/
-static inline JsonTokenType
-lex_peek(JsonLexContext *lex)
-{
-	return lex->token_type;
-}
-
-/*
- * lex_expect
- *
- * move the lexer to the next token if the current look_ahead token matches
- * the parameter token. Otherwise, report an error.
- */
-static inline JsonParseErrorType
-lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
-{
-	if (lex_peek(lex) == token)
-		return json_lex(lex);
-	else
-		return report_parse_error(ctx, lex);
-}
-
 /* chars to consider as part of an alphanumeric token */
 #define JSON_ALPHANUMERIC_CHAR(c)  \
 	(((c) >= 'a' && (c) <= 'z') || \
@@ -122,7 +110,8 @@ IsValidJsonNumber(const char *str, int len)
 		dummy_lex.input_length = len;
 	}
 
-	json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error, &total_len);
+	if (JSON_SUCCESS != json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error, &total_len))
+		return false;
 
 	return (!numeric_error) && (total_len == dummy_lex.input_length);
 }
@@ -136,13 +125,21 @@ IsValidJsonNumber(const char *str, int len)
  * if really required.
  */
 JsonLexContext *
-makeJsonLexContextCstringLen(char *json, int len, bool need_escapes)
+makeJsonLexContextCstringLen(char *json, int len, pg_enc encoding, bool need_escapes)
 {
-	JsonLexContext *lex = palloc0(sizeof(JsonLexContext));
+	JsonLexContext *lex;
+
+#ifndef FRONTEND
+	lex = (JsonLexContext*) palloc0fast(sizeof(JsonLexContext));
+#else
+	lex = (JsonLexContext*) malloc(sizeof(JsonLexContext));
+	memset(lex, 0, sizeof(JsonLexContext));
+#endif
 
 	lex->input = lex->token_terminator = lex->line_start = json;
 	lex->line_number = 1;
 	lex->input_length = len;
+	lex->input_encoding = encoding;
 	if (need_escapes)
 		lex->strval = makeStringInfo();
 	return lex;
@@ -159,15 +156,12 @@ makeJsonLexContextCstringLen(char *json, int len, bool need_escapes)
  * pointer to a state object to be passed to those routines.
  */
 JsonParseErrorType
-pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
+pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)
 {
 	JsonTokenType tok;
-	JsonParseErrorType	result;
 
 	/* get the initial token */
-	result = json_lex(lex);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(json_lex(lex));
 
 	tok = lex_peek(lex);
 
@@ -175,19 +169,16 @@ pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
 	switch (tok)
 	{
 		case JSON_TOKEN_OBJECT_START:
-			result = parse_object(lex, sem);
+			INSIST(parse_object(lex, sem));
 			break;
 		case JSON_TOKEN_ARRAY_START:
-			result = parse_array(lex, sem);
+			INSIST(parse_array(lex, sem));
 			break;
 		default:
-			result = parse_scalar(lex, sem); /* json can be a bare scalar */
+			INSIST(parse_scalar(lex, sem)); /* json can be a bare scalar */
 	}
 
-	if (result == JSON_SUCCESS)
-		result = lex_expect(JSON_PARSE_END, lex, JSON_TOKEN_END);
-
-	return result;
+	return lex_expect(JSON_PARSE_END, lex, JSON_TOKEN_END);
 }
 
 /*
@@ -203,7 +194,6 @@ json_count_array_elements(JsonLexContext *lex, int *elements)
 {
 	JsonLexContext copylex;
 	int			count;
-	JsonParseErrorType	result;
 
 	/*
 	 * It's safe to do this with a shallow copy because the lexical routines
@@ -215,29 +205,20 @@ json_count_array_elements(JsonLexContext *lex, int *elements)
 	copylex.lex_level++;
 
 	count = 0;
-	result = lex_expect(JSON_PARSE_ARRAY_START, &copylex,
-						JSON_TOKEN_ARRAY_START);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(lex_expect(JSON_PARSE_ARRAY_START, &copylex, JSON_TOKEN_ARRAY_START));
 	if (lex_peek(&copylex) != JSON_TOKEN_ARRAY_END)
 	{
 		while (1)
 		{
 			count++;
-			result = parse_array_element(&copylex, &nullSemAction);
-			if (result != JSON_SUCCESS)
-				return result;
+			if (JSON_SUCCESS != parse_array_element(&copylex, &nullSemAction))
+				break;
 			if (copylex.token_type != JSON_TOKEN_COMMA)
 				break;
-			result = json_lex(&copylex);
-			if (result != JSON_SUCCESS)
-				return result;
+			INSIST(json_lex(&copylex));
 		}
 	}
-	result = lex_expect(JSON_PARSE_ARRAY_NEXT, &copylex,
-							JSON_TOKEN_ARRAY_END);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(lex_expect(JSON_PARSE_ARRAY_NEXT, &copylex, JSON_TOKEN_ARRAY_END));
 
 	*elements = count;
 	return JSON_SUCCESS;
@@ -253,22 +234,30 @@ json_count_array_elements(JsonLexContext *lex, int *elements)
  *	  - object field
  */
 static inline JsonParseErrorType
-parse_scalar(JsonLexContext *lex, JsonSemAction *sem)
+parse_scalar(JsonLexContext *lex, const JsonSemAction *sem)
 {
 	char	   *val = NULL;
 	json_scalar_action sfunc = sem->scalar;
 	JsonTokenType tok = lex_peek(lex);
-	JsonParseErrorType result;
 
 	/* a scalar must be a string, a number, true, false, or null */
-	if (tok != JSON_TOKEN_STRING && tok != JSON_TOKEN_NUMBER &&
-		tok != JSON_TOKEN_TRUE && tok != JSON_TOKEN_FALSE &&
-		tok != JSON_TOKEN_NULL)
-		return report_parse_error(JSON_PARSE_VALUE, lex);
+	switch (tok)
+	{
+		case JSON_TOKEN_STRING:
+		case JSON_TOKEN_NUMBER:
+		case JSON_TOKEN_TRUE:
+		case JSON_TOKEN_FALSE:
+		case JSON_TOKEN_NULL:
+			break;
+		default:
+			return report_parse_error(JSON_PARSE_VALUE, lex);
+	}
 
 	/* if no semantic function, just consume the token */
 	if (sfunc == NULL)
+	{
 		return json_lex(lex);
+	}
 
 	/* extract the de-escaped string value, or the raw lexeme */
 	if (lex_peek(lex) == JSON_TOKEN_STRING)
@@ -278,7 +267,7 @@ parse_scalar(JsonLexContext *lex, JsonSemAction *sem)
 	}
 	else
 	{
-		int			len = (lex->token_terminator - lex->token_start);
+		int		len = (lex->token_terminator - lex->token_start);
 
 		val = palloc(len + 1);
 		memcpy(val, lex->token_start, len);
@@ -286,18 +275,15 @@ parse_scalar(JsonLexContext *lex, JsonSemAction *sem)
 	}
 
 	/* consume the token */
-	result = json_lex(lex);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(json_lex(lex));
 
 	/* invoke the callback */
 	(*sfunc) (sem->semstate, val, tok);
-
 	return JSON_SUCCESS;
 }
 
 static JsonParseErrorType
-parse_object_field(JsonLexContext *lex, JsonSemAction *sem)
+parse_object_field(JsonLexContext *lex, const JsonSemAction *sem)
 {
 	/*
 	 * An object field is "fieldname" : value where value can be a scalar,
@@ -310,19 +296,14 @@ parse_object_field(JsonLexContext *lex, JsonSemAction *sem)
 	json_ofield_action oend = sem->object_field_end;
 	bool		isnull;
 	JsonTokenType tok;
-	JsonParseErrorType result;
 
 	if (lex_peek(lex) != JSON_TOKEN_STRING)
 		return report_parse_error(JSON_PARSE_STRING, lex);
 	if ((ostart != NULL || oend != NULL) && lex->strval != NULL)
 		fname = pstrdup(lex->strval->data);
-	result = json_lex(lex);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(json_lex(lex));
 
-	result = lex_expect(JSON_PARSE_OBJECT_LABEL, lex, JSON_TOKEN_COLON);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(lex_expect(JSON_PARSE_OBJECT_LABEL, lex, JSON_TOKEN_COLON));
 
 	tok = lex_peek(lex);
 	isnull = tok == JSON_TOKEN_NULL;
@@ -333,16 +314,14 @@ parse_object_field(JsonLexContext *lex, JsonSemAction *sem)
 	switch (tok)
 	{
 		case JSON_TOKEN_OBJECT_START:
-			result = parse_object(lex, sem);
+			INSIST(parse_object(lex, sem));
 			break;
 		case JSON_TOKEN_ARRAY_START:
-			result = parse_array(lex, sem);
+			INSIST(parse_array(lex, sem));
 			break;
 		default:
-			result = parse_scalar(lex, sem);
+			INSIST(parse_scalar(lex, sem));
 	}
-	if (result != JSON_SUCCESS)
-		return result;
 
 	if (oend != NULL)
 		(*oend) (sem->semstate, fname, isnull);
@@ -350,7 +329,7 @@ parse_object_field(JsonLexContext *lex, JsonSemAction *sem)
 }
 
 static JsonParseErrorType
-parse_object(JsonLexContext *lex, JsonSemAction *sem)
+parse_object(JsonLexContext *lex, const JsonSemAction *sem)
 {
 	/*
 	 * an object is a possibly empty sequence of object fields, separated by
@@ -359,9 +338,12 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem)
 	json_struct_action ostart = sem->object_start;
 	json_struct_action oend = sem->object_end;
 	JsonTokenType tok;
-	JsonParseErrorType result;
 
+#ifndef FRONTEND
 	check_stack_depth();
+#else
+	/* TODO: What do we do in frontend code? */
+#endif
 
 	if (ostart != NULL)
 		(*ostart) (sem->semstate);
@@ -375,51 +357,41 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem)
 	lex->lex_level++;
 
 	Assert(lex_peek(lex) == JSON_TOKEN_OBJECT_START);
-	result = json_lex(lex);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(json_lex(lex));
 
 	tok = lex_peek(lex);
 	switch (tok)
 	{
 		case JSON_TOKEN_STRING:
-			result = parse_object_field(lex, sem);
-			while (result == JSON_SUCCESS && lex_peek(lex) == JSON_TOKEN_COMMA)
+			INSIST(parse_object_field(lex, sem));
+			while (lex_peek(lex) == JSON_TOKEN_COMMA)
 			{
-				result = json_lex(lex);
-				if (result != JSON_SUCCESS)
-					break;
-				result = parse_object_field(lex, sem);
+				INSIST(json_lex(lex));
+				INSIST(parse_object_field(lex, sem));
 			}
 			break;
 		case JSON_TOKEN_OBJECT_END:
 			break;
 		default:
 			/* case of an invalid initial token inside the object */
-			result = report_parse_error(JSON_PARSE_OBJECT_START, lex);
+			return report_parse_error(JSON_PARSE_OBJECT_START, lex);
 	}
-	if (result != JSON_SUCCESS)
-		return result;
 
-	result = lex_expect(JSON_PARSE_OBJECT_NEXT, lex, JSON_TOKEN_OBJECT_END);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(lex_expect(JSON_PARSE_OBJECT_NEXT, lex, JSON_TOKEN_OBJECT_END));
 
 	lex->lex_level--;
 
 	if (oend != NULL)
 		(*oend) (sem->semstate);
-
 	return JSON_SUCCESS;
 }
 
 static JsonParseErrorType
-parse_array_element(JsonLexContext *lex, JsonSemAction *sem)
+parse_array_element(JsonLexContext *lex, const JsonSemAction *sem)
 {
 	json_aelem_action astart = sem->array_element_start;
 	json_aelem_action aend = sem->array_element_end;
 	JsonTokenType tok = lex_peek(lex);
-	JsonParseErrorType result;
 
 	bool		isnull;
 
@@ -432,26 +404,22 @@ parse_array_element(JsonLexContext *lex, JsonSemAction *sem)
 	switch (tok)
 	{
 		case JSON_TOKEN_OBJECT_START:
-			result = parse_object(lex, sem);
+			INSIST(parse_object(lex, sem));
 			break;
 		case JSON_TOKEN_ARRAY_START:
-			result = parse_array(lex, sem);
+			INSIST(parse_array(lex, sem));
 			break;
 		default:
-			result = parse_scalar(lex, sem);
+			INSIST(parse_scalar(lex, sem));
 	}
 
-	if (result != JSON_SUCCESS)
-		return result;
-
 	if (aend != NULL)
 		(*aend) (sem->semstate, isnull);
-
 	return JSON_SUCCESS;
 }
 
 static JsonParseErrorType
-parse_array(JsonLexContext *lex, JsonSemAction *sem)
+parse_array(JsonLexContext *lex, const JsonSemAction *sem)
 {
 	/*
 	 * an array is a possibly empty sequence of array elements, separated by
@@ -459,9 +427,12 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem)
 	 */
 	json_struct_action astart = sem->array_start;
 	json_struct_action aend = sem->array_end;
-	JsonParseErrorType result;
 
+#ifndef FRONTEND
 	check_stack_depth();
+#else
+	/* TODO: What do we do in frontend code? */
+#endif
 
 	if (astart != NULL)
 		(*astart) (sem->semstate);
@@ -474,31 +445,25 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem)
 	 */
 	lex->lex_level++;
 
-	result = lex_expect(JSON_PARSE_ARRAY_START, lex, JSON_TOKEN_ARRAY_START);
-	if (result == JSON_SUCCESS && lex_peek(lex) != JSON_TOKEN_ARRAY_END)
+	INSIST(lex_expect(JSON_PARSE_ARRAY_START, lex, JSON_TOKEN_ARRAY_START));
+	if (lex_peek(lex) != JSON_TOKEN_ARRAY_END)
 	{
-		result = parse_array_element(lex, sem);
 
-		while (result == JSON_SUCCESS && lex_peek(lex) == JSON_TOKEN_COMMA)
+		INSIST(parse_array_element(lex, sem));
+
+		while (lex_peek(lex) == JSON_TOKEN_COMMA)
 		{
-			result = json_lex(lex);
-			if (result != JSON_SUCCESS)
-				break;
-			result = parse_array_element(lex, sem);
+			INSIST(json_lex(lex));
+			INSIST(parse_array_element(lex, sem));
 		}
 	}
-	if (result != JSON_SUCCESS)
-		return result;
 
-	result = lex_expect(JSON_PARSE_ARRAY_NEXT, lex, JSON_TOKEN_ARRAY_END);
-	if (result != JSON_SUCCESS)
-		return result;
+	INSIST(lex_expect(JSON_PARSE_ARRAY_NEXT, lex, JSON_TOKEN_ARRAY_END));
 
 	lex->lex_level--;
 
 	if (aend != NULL)
 		(*aend) (sem->semstate);
-
 	return JSON_SUCCESS;
 }
 
@@ -510,7 +475,6 @@ json_lex(JsonLexContext *lex)
 {
 	char	   *s;
 	int			len;
-	JsonParseErrorType	result;
 
 	/* Skip leading whitespace. */
 	s = lex->token_terminator;
@@ -534,7 +498,6 @@ json_lex(JsonLexContext *lex)
 		lex->token_type = JSON_TOKEN_END;
 	}
 	else
-	{
 		switch (*s)
 		{
 				/* Single-character token, some kind of punctuation mark. */
@@ -570,16 +533,12 @@ json_lex(JsonLexContext *lex)
 				break;
 			case '"':
 				/* string */
-				result = json_lex_string(lex);
-				if (result != JSON_SUCCESS)
-					return result;
+				INSIST(json_lex_string(lex));
 				lex->token_type = JSON_TOKEN_STRING;
 				break;
 			case '-':
 				/* Negative number. */
-				result = json_lex_number(lex, s + 1, NULL, NULL);
-				if (result != JSON_SUCCESS)
-					return result;
+				INSIST(json_lex_number(lex, s + 1, NULL, NULL));
 				lex->token_type = JSON_TOKEN_NUMBER;
 				break;
 			case '0':
@@ -593,9 +552,7 @@ json_lex(JsonLexContext *lex)
 			case '8':
 			case '9':
 				/* Positive number. */
-				result = json_lex_number(lex, s, NULL, NULL);
-				if (result != JSON_SUCCESS)
-					return result;
+				INSIST(json_lex_number(lex, s, NULL, NULL));
 				lex->token_type = JSON_TOKEN_NUMBER;
 				break;
 			default:
@@ -649,11 +606,23 @@ json_lex(JsonLexContext *lex)
 
 				}
 		}						/* end of switch */
-	}
-
 	return JSON_SUCCESS;
 }
 
+/*
+ * lex_expect
+ *
+ * move the lexer to the next token if the current look_ahead token matches
+ * the parameter token. Otherwise, report an error.
+ */
+static inline JsonParseErrorType
+lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
+{
+	if (lex_peek(lex) == token)
+		return json_lex(lex);
+	return report_parse_error(ctx, lex);
+}
+
 /*
  * The next token in the input stream is known to be a string; lex it.
  */
@@ -721,7 +690,7 @@ json_lex_string(JsonLexContext *lex)
 						ch = (ch * 16) + (*s - 'A') + 10;
 					else
 					{
-						lex->token_terminator = s + pg_mblen(s);
+						lex->token_terminator = s + pg_wchar_table[lex->input_encoding].mblen((const unsigned char *) s);
 						return JSON_UNICODE_ESCAPE_FORMAT;
 					}
 				}
@@ -760,7 +729,7 @@ json_lex_string(JsonLexContext *lex)
 						/* We can't allow this, since our TEXT type doesn't */
 						return JSON_UNICODE_CODE_POINT_ZERO;
 					}
-					else if (GetDatabaseEncoding() == PG_UTF8)
+					else if (lex->input_encoding == PG_UTF8)
 					{
 						unicode_to_utf8(ch, (unsigned char *) utf8str);
 						utf8len = pg_utf_mblen((unsigned char *) utf8str);
@@ -810,7 +779,7 @@ json_lex_string(JsonLexContext *lex)
 					default:
 						/* Not a valid string escape, so signal error. */
 						lex->token_start = s;
-						lex->token_terminator = s + pg_mblen(s);
+						lex->token_terminator = s + pg_wchar_table[lex->input_encoding].mblen((const unsigned char *) s);
 						return JSON_ESCAPING_INVALID;
 				}
 			}
@@ -824,7 +793,7 @@ json_lex_string(JsonLexContext *lex)
 				 * shown it's not a performance win.
 				 */
 				lex->token_start = s;
-				lex->token_terminator = s + pg_mblen(s);
+				lex->token_terminator = s + pg_wchar_table[lex->input_encoding].mblen((const unsigned char *) s);
 				return JSON_ESCAPING_INVALID;
 			}
 
@@ -833,7 +802,6 @@ json_lex_string(JsonLexContext *lex)
 		{
 			if (hi_surrogate != -1)
 				return JSON_UNICODE_LOW_SURROGATE;
-
 			appendStringInfoChar(lex->strval, *s);
 		}
 
@@ -967,7 +935,6 @@ json_lex_number(JsonLexContext *lex, char *s,
 		if (error)
 			return JSON_INVALID_TOKEN;
 	}
-
 	return JSON_SUCCESS;
 }
 
@@ -983,7 +950,6 @@ report_parse_error(JsonParseContext ctx, JsonLexContext *lex)
 	if (lex->token_start == NULL || lex->token_type == JSON_TOKEN_END)
 		return JSON_EXPECTED_MORE;
 
-	/* Otherwise choose the error type based on the parsing context. */
 	switch (ctx)
 	{
 		case JSON_PARSE_END:
@@ -1005,7 +971,7 @@ report_parse_error(JsonParseContext ctx, JsonLexContext *lex)
 		case JSON_PARSE_OBJECT_COMMA:
 			return JSON_EXPECTED_STRING;
 		default:
-			elog(ERROR, "unexpected json parse state: %d", ctx);
+			return JSON_BAD_PARSER_STATE;;
 	}
 }
 
@@ -1018,7 +984,12 @@ json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
 	switch (error)
 	{
 		case JSON_SUCCESS:
+		case JSON_BAD_PARSER_STATE:
+#ifdef FRONTEND
+			return _("internal error in json parser");
+#else
 			elog(ERROR, "internal error in json parser");
+#endif
 			break;
 		case JSON_ESCAPING_INVALID:
 			return psprintf(_("Escape sequence \"\\%s\" is invalid."),
diff --git a/src/include/common/jsonapi.h b/src/include/common/jsonapi.h
index da20ffce64..1836b5e591 100644
--- a/src/include/common/jsonapi.h
+++ b/src/include/common/jsonapi.h
@@ -14,6 +14,7 @@
 #ifndef JSONAPI_H
 #define JSONAPI_H
 
+#include "common/pg_wchar.h"
 #include "lib/stringinfo.h"
 
 typedef enum
@@ -52,7 +53,8 @@ typedef enum
 	JSON_UNICODE_ESCAPE_FORMAT,
 	JSON_UNICODE_HIGH_ESCAPE,
 	JSON_UNICODE_HIGH_SURROGATE,
-	JSON_UNICODE_LOW_SURROGATE
+	JSON_UNICODE_LOW_SURROGATE,
+	JSON_BAD_PARSER_STATE
 } JsonParseErrorType;
 
 
@@ -73,6 +75,7 @@ typedef struct JsonLexContext
 {
 	char	   *input;
 	int			input_length;
+	pg_enc		input_encoding;
 	char	   *token_start;
 	char	   *token_terminator;
 	char	   *prev_token_terminator;
@@ -114,6 +117,9 @@ typedef struct JsonSemAction
 	json_scalar_action scalar;
 } JsonSemAction;
 
+/* the null action object used for pure validation */
+extern const JsonSemAction nullSemAction;
+
 /*
  * pg_parse_json will parse the string in the lex calling the
  * action functions in sem at the appropriate points. It is
@@ -124,10 +130,10 @@ typedef struct JsonSemAction
  * does nothing and just continues.
  */
 extern JsonParseErrorType pg_parse_json(JsonLexContext *lex,
-										JsonSemAction *sem);
+										const JsonSemAction *sem);
 
 /* the null action object used for pure validation */
-extern JsonSemAction nullSemAction;
+extern const JsonSemAction nullSemAction;
 
 /*
  * json_count_array_elements performs a fast secondary parse to determine the
@@ -149,11 +155,18 @@ extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex,
  */
 extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
 													int len,
+													pg_enc encoding,
 													bool need_escapes);
 
 /* lex one token */
 extern JsonParseErrorType json_lex(JsonLexContext *lex);
 
+/* Get the current look_ahead token. */
+static inline JsonTokenType lex_peek(JsonLexContext *lex)
+{
+	return lex->token_type;
+}
+
 /* construct an error detail string for a json error */
 extern char *json_errdetail(JsonParseErrorType error, JsonLexContext *lex);
 
diff --git a/src/include/utils/jsonfuncs.h b/src/include/utils/jsonfuncs.h
index 1f1b4029cb..f29437d29e 100644
--- a/src/include/utils/jsonfuncs.h
+++ b/src/include/utils/jsonfuncs.h
@@ -40,7 +40,7 @@ typedef text *(*JsonTransformStringValuesAction) (void *state, char *elem_value,
 extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
 
 /* try to parse json, and ereport(ERROR) on failure */
-extern void pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem);
+extern void pg_parse_json_or_ereport(JsonLexContext *lex, const JsonSemAction *sem);
 
 /* report an error during json lexing or parsing */
 extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
-- 
2.21.1 (Apple Git-122.3)