From a2c7d0e798004fc5c22ef8eef204a8eefc19e4a5 Mon Sep 17 00:00:00 2001 From: Mark Dilger 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, ©lex, - JSON_TOKEN_ARRAY_START); - if (result != JSON_SUCCESS) - return result; + INSIST(lex_expect(JSON_PARSE_ARRAY_START, ©lex, JSON_TOKEN_ARRAY_START)); if (lex_peek(©lex) != JSON_TOKEN_ARRAY_END) { while (1) { count++; - result = parse_array_element(©lex, &nullSemAction); - if (result != JSON_SUCCESS) - return result; + if (JSON_SUCCESS != parse_array_element(©lex, &nullSemAction)) + break; if (copylex.token_type != JSON_TOKEN_COMMA) break; - result = json_lex(©lex); - if (result != JSON_SUCCESS) - return result; + INSIST(json_lex(©lex)); } } - 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)); *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)