Commit 1f3a0217 authored by Robert Haas's avatar Robert Haas

Adjust pg_parse_json() so that it does not directly ereport().

Instead, it now returns a value indicating either success or the
type of error which occurred. The old behavior is still available
by calling pg_parse_json_or_ereport(). If the new interface is
used, an error can be thrown by passing the return value of
pg_parse_json() to json_ereport_error().

pg_parse_json() can still elog() in can't-happen cases, but it
seems like that issue is best handled separately.

Adjust json_lex() and json_count_array_elements() to return an
error code, too.

This is all in preparation for making the backend's json parser
available to frontend code.

Reviewed and/or tested by Mark Dilger and Andrew Dunstan.

Discussion: http://postgr.es/m/CA+TgmoYfOXhd27MUDGioVh6QtpD0C1K-f6ObSA10AWiHBAL5bA@mail.gmail.com
parent 3e4818e9
...@@ -81,7 +81,7 @@ json_in(PG_FUNCTION_ARGS) ...@@ -81,7 +81,7 @@ json_in(PG_FUNCTION_ARGS)
/* validate it */ /* validate it */
lex = makeJsonLexContext(result, false); lex = makeJsonLexContext(result, false);
pg_parse_json(lex, &nullSemAction); pg_parse_json_or_ereport(lex, &nullSemAction);
/* Internal representation is the same as text, for now */ /* Internal representation is the same as text, for now */
PG_RETURN_TEXT_P(result); PG_RETURN_TEXT_P(result);
...@@ -128,7 +128,7 @@ json_recv(PG_FUNCTION_ARGS) ...@@ -128,7 +128,7 @@ json_recv(PG_FUNCTION_ARGS)
/* Validate it. */ /* Validate it. */
lex = makeJsonLexContextCstringLen(str, nbytes, false); lex = makeJsonLexContextCstringLen(str, nbytes, false);
pg_parse_json(lex, &nullSemAction); pg_parse_json_or_ereport(lex, &nullSemAction);
PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes)); PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
} }
...@@ -1337,12 +1337,15 @@ json_typeof(PG_FUNCTION_ARGS) ...@@ -1337,12 +1337,15 @@ json_typeof(PG_FUNCTION_ARGS)
JsonLexContext *lex; JsonLexContext *lex;
JsonTokenType tok; JsonTokenType tok;
char *type; char *type;
JsonParseErrorType result;
json = PG_GETARG_TEXT_PP(0); json = PG_GETARG_TEXT_PP(0);
lex = makeJsonLexContext(json, false); lex = makeJsonLexContext(json, false);
/* Lex exactly one token from the input and check its type. */ /* Lex exactly one token from the input and check its type. */
json_lex(lex); result = json_lex(lex);
if (result != JSON_SUCCESS)
json_ereport_error(result, lex);
tok = lex->token_type; tok = lex->token_type;
switch (tok) switch (tok)
{ {
......
This diff is collapsed.
...@@ -272,7 +272,7 @@ jsonb_from_cstring(char *json, int len) ...@@ -272,7 +272,7 @@ jsonb_from_cstring(char *json, int len)
sem.scalar = jsonb_in_scalar; sem.scalar = jsonb_in_scalar;
sem.object_field_start = jsonb_in_object_field_start; sem.object_field_start = jsonb_in_object_field_start;
pg_parse_json(lex, &sem); pg_parse_json_or_ereport(lex, &sem);
/* after parsing, the item member has the composed jsonb structure */ /* after parsing, the item member has the composed jsonb structure */
PG_RETURN_POINTER(JsonbValueToJsonb(state.res)); PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
...@@ -860,7 +860,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, ...@@ -860,7 +860,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
sem.scalar = jsonb_in_scalar; sem.scalar = jsonb_in_scalar;
sem.object_field_start = jsonb_in_object_field_start; sem.object_field_start = jsonb_in_object_field_start;
pg_parse_json(lex, &sem); pg_parse_json_or_ereport(lex, &sem);
} }
break; break;
......
...@@ -606,7 +606,7 @@ json_object_keys(PG_FUNCTION_ARGS) ...@@ -606,7 +606,7 @@ json_object_keys(PG_FUNCTION_ARGS)
sem->object_field_start = okeys_object_field_start; sem->object_field_start = okeys_object_field_start;
/* remainder are all NULL, courtesy of palloc0 above */ /* remainder are all NULL, courtesy of palloc0 above */
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
/* keys are now in state->result */ /* keys are now in state->result */
pfree(lex->strval->data); pfree(lex->strval->data);
...@@ -1000,7 +1000,7 @@ get_worker(text *json, ...@@ -1000,7 +1000,7 @@ get_worker(text *json,
sem->array_element_end = get_array_element_end; sem->array_element_end = get_array_element_end;
} }
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
return state->tresult; return state->tresult;
} }
...@@ -1148,7 +1148,12 @@ get_array_start(void *state) ...@@ -1148,7 +1148,12 @@ get_array_start(void *state)
_state->path_indexes[lex_level] != INT_MIN) _state->path_indexes[lex_level] != INT_MIN)
{ {
/* Negative subscript -- convert to positive-wise subscript */ /* Negative subscript -- convert to positive-wise subscript */
int nelements = json_count_array_elements(_state->lex); JsonParseErrorType error;
int nelements;
error = json_count_array_elements(_state->lex, &nelements);
if (error != JSON_SUCCESS)
json_ereport_error(error, _state->lex);
if (-_state->path_indexes[lex_level] <= nelements) if (-_state->path_indexes[lex_level] <= nelements)
_state->path_indexes[lex_level] += nelements; _state->path_indexes[lex_level] += nelements;
...@@ -1548,7 +1553,7 @@ json_array_length(PG_FUNCTION_ARGS) ...@@ -1548,7 +1553,7 @@ json_array_length(PG_FUNCTION_ARGS)
sem->scalar = alen_scalar; sem->scalar = alen_scalar;
sem->array_element_start = alen_array_element_start; sem->array_element_start = alen_array_element_start;
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
PG_RETURN_INT32(state->count); PG_RETURN_INT32(state->count);
} }
...@@ -1814,7 +1819,7 @@ each_worker(FunctionCallInfo fcinfo, bool as_text) ...@@ -1814,7 +1819,7 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
"json_each temporary cxt", "json_each temporary cxt",
ALLOCSET_DEFAULT_SIZES); ALLOCSET_DEFAULT_SIZES);
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
MemoryContextDelete(state->tmp_cxt); MemoryContextDelete(state->tmp_cxt);
...@@ -2113,7 +2118,7 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text) ...@@ -2113,7 +2118,7 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
"json_array_elements temporary cxt", "json_array_elements temporary cxt",
ALLOCSET_DEFAULT_SIZES); ALLOCSET_DEFAULT_SIZES);
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
MemoryContextDelete(state->tmp_cxt); MemoryContextDelete(state->tmp_cxt);
...@@ -2485,7 +2490,7 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len) ...@@ -2485,7 +2490,7 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len)
sem.array_element_end = populate_array_element_end; sem.array_element_end = populate_array_element_end;
sem.scalar = populate_array_scalar; sem.scalar = populate_array_scalar;
pg_parse_json(state.lex, &sem); pg_parse_json_or_ereport(state.lex, &sem);
/* number of dimensions should be already known */ /* number of dimensions should be already known */
Assert(ctx->ndims > 0 && ctx->dims); Assert(ctx->ndims > 0 && ctx->dims);
...@@ -3342,7 +3347,7 @@ get_json_object_as_hash(char *json, int len, const char *funcname) ...@@ -3342,7 +3347,7 @@ get_json_object_as_hash(char *json, int len, const char *funcname)
sem->object_field_start = hash_object_field_start; sem->object_field_start = hash_object_field_start;
sem->object_field_end = hash_object_field_end; sem->object_field_end = hash_object_field_end;
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
return tab; return tab;
} }
...@@ -3641,7 +3646,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname, ...@@ -3641,7 +3646,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
state->lex = lex; state->lex = lex;
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
} }
else else
{ {
...@@ -3971,7 +3976,7 @@ json_strip_nulls(PG_FUNCTION_ARGS) ...@@ -3971,7 +3976,7 @@ json_strip_nulls(PG_FUNCTION_ARGS)
sem->array_element_start = sn_array_element_start; sem->array_element_start = sn_array_element_start;
sem->object_field_start = sn_object_field_start; sem->object_field_start = sn_object_field_start;
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
PG_RETURN_TEXT_P(cstring_to_text_with_len(state->strval->data, PG_RETURN_TEXT_P(cstring_to_text_with_len(state->strval->data,
state->strval->len)); state->strval->len));
...@@ -5110,7 +5115,7 @@ iterate_json_values(text *json, uint32 flags, void *action_state, ...@@ -5110,7 +5115,7 @@ iterate_json_values(text *json, uint32 flags, void *action_state,
sem->scalar = iterate_values_scalar; sem->scalar = iterate_values_scalar;
sem->object_field_start = iterate_values_object_field_start; sem->object_field_start = iterate_values_object_field_start;
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
} }
/* /*
...@@ -5230,7 +5235,7 @@ transform_json_string_values(text *json, void *action_state, ...@@ -5230,7 +5235,7 @@ transform_json_string_values(text *json, void *action_state,
sem->array_element_start = transform_string_values_array_element_start; sem->array_element_start = transform_string_values_array_element_start;
sem->object_field_start = transform_string_values_object_field_start; sem->object_field_start = transform_string_values_object_field_start;
pg_parse_json(lex, sem); pg_parse_json_or_ereport(lex, sem);
return cstring_to_text_with_len(state->strval->data, state->strval->len); return cstring_to_text_with_len(state->strval->data, state->strval->len);
} }
......
...@@ -33,6 +33,28 @@ typedef enum ...@@ -33,6 +33,28 @@ typedef enum
JSON_TOKEN_END JSON_TOKEN_END
} JsonTokenType; } JsonTokenType;
typedef enum
{
JSON_SUCCESS,
JSON_ESCAPING_INVALID,
JSON_ESCAPING_REQUIRED,
JSON_EXPECTED_ARRAY_FIRST,
JSON_EXPECTED_ARRAY_NEXT,
JSON_EXPECTED_COLON,
JSON_EXPECTED_END,
JSON_EXPECTED_JSON,
JSON_EXPECTED_MORE,
JSON_EXPECTED_OBJECT_FIRST,
JSON_EXPECTED_OBJECT_NEXT,
JSON_EXPECTED_STRING,
JSON_INVALID_TOKEN,
JSON_UNICODE_CODE_POINT_ZERO,
JSON_UNICODE_ESCAPE_FORMAT,
JSON_UNICODE_HIGH_ESCAPE,
JSON_UNICODE_HIGH_SURROGATE,
JSON_UNICODE_LOW_SURROGATE
} JsonParseErrorType;
/* /*
* All the fields in this structure should be treated as read-only. * All the fields in this structure should be treated as read-only.
...@@ -101,7 +123,14 @@ typedef struct JsonSemAction ...@@ -101,7 +123,14 @@ typedef struct JsonSemAction
* points to. If the action pointers are NULL the parser * points to. If the action pointers are NULL the parser
* does nothing and just continues. * does nothing and just continues.
*/ */
extern void pg_parse_json(JsonLexContext *lex, JsonSemAction *sem); extern JsonParseErrorType pg_parse_json(JsonLexContext *lex,
JsonSemAction *sem);
/*
* Same thing, but signal errors via ereport(ERROR) instead of returning
* a result code.
*/
extern void pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem);
/* the null action object used for pure validation */ /* the null action object used for pure validation */
extern JsonSemAction nullSemAction; extern JsonSemAction nullSemAction;
...@@ -110,8 +139,13 @@ extern JsonSemAction nullSemAction; ...@@ -110,8 +139,13 @@ extern JsonSemAction nullSemAction;
* json_count_array_elements performs a fast secondary parse to determine the * json_count_array_elements performs a fast secondary parse to determine the
* number of elements in passed array lex context. It should be called from an * number of elements in passed array lex context. It should be called from an
* array_start action. * array_start action.
*
* The return value indicates whether any error occurred, while the number
* of elements is stored into *elements (but only if the return value is
* JSON_SUCCESS).
*/ */
extern int json_count_array_elements(JsonLexContext *lex); extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex,
int *elements);
/* /*
* constructors for JsonLexContext, with or without strval element. * constructors for JsonLexContext, with or without strval element.
...@@ -128,7 +162,13 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json, ...@@ -128,7 +162,13 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
bool need_escapes); bool need_escapes);
/* lex one token */ /* lex one token */
extern void json_lex(JsonLexContext *lex); extern JsonParseErrorType json_lex(JsonLexContext *lex);
/* report an error during json lexing or parsing */
extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
/* construct an error detail string for a json error */
extern char *json_errdetail(JsonParseErrorType error, JsonLexContext *lex);
/* /*
* Utility function to check if a string is a valid JSON number. * Utility function to check if a string is a valid JSON number.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment