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)
/* validate it */
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 */
PG_RETURN_TEXT_P(result);
......@@ -128,7 +128,7 @@ json_recv(PG_FUNCTION_ARGS)
/* Validate it. */
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));
}
......@@ -1337,12 +1337,15 @@ json_typeof(PG_FUNCTION_ARGS)
JsonLexContext *lex;
JsonTokenType tok;
char *type;
JsonParseErrorType result;
json = PG_GETARG_TEXT_PP(0);
lex = makeJsonLexContext(json, false);
/* 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;
switch (tok)
{
......
This diff is collapsed.
......@@ -272,7 +272,7 @@ jsonb_from_cstring(char *json, int len)
sem.scalar = jsonb_in_scalar;
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 */
PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
......@@ -860,7 +860,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
sem.scalar = jsonb_in_scalar;
sem.object_field_start = jsonb_in_object_field_start;
pg_parse_json(lex, &sem);
pg_parse_json_or_ereport(lex, &sem);
}
break;
......
......@@ -606,7 +606,7 @@ json_object_keys(PG_FUNCTION_ARGS)
sem->object_field_start = okeys_object_field_start;
/* 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 */
pfree(lex->strval->data);
......@@ -1000,7 +1000,7 @@ get_worker(text *json,
sem->array_element_end = get_array_element_end;
}
pg_parse_json(lex, sem);
pg_parse_json_or_ereport(lex, sem);
return state->tresult;
}
......@@ -1148,7 +1148,12 @@ get_array_start(void *state)
_state->path_indexes[lex_level] != INT_MIN)
{
/* 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)
_state->path_indexes[lex_level] += nelements;
......@@ -1548,7 +1553,7 @@ json_array_length(PG_FUNCTION_ARGS)
sem->scalar = alen_scalar;
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);
}
......@@ -1814,7 +1819,7 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
"json_each temporary cxt",
ALLOCSET_DEFAULT_SIZES);
pg_parse_json(lex, sem);
pg_parse_json_or_ereport(lex, sem);
MemoryContextDelete(state->tmp_cxt);
......@@ -2113,7 +2118,7 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
"json_array_elements temporary cxt",
ALLOCSET_DEFAULT_SIZES);
pg_parse_json(lex, sem);
pg_parse_json_or_ereport(lex, sem);
MemoryContextDelete(state->tmp_cxt);
......@@ -2485,7 +2490,7 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len)
sem.array_element_end = populate_array_element_end;
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 */
Assert(ctx->ndims > 0 && ctx->dims);
......@@ -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_end = hash_object_field_end;
pg_parse_json(lex, sem);
pg_parse_json_or_ereport(lex, sem);
return tab;
}
......@@ -3641,7 +3646,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
state->lex = lex;
pg_parse_json(lex, sem);
pg_parse_json_or_ereport(lex, sem);
}
else
{
......@@ -3971,7 +3976,7 @@ json_strip_nulls(PG_FUNCTION_ARGS)
sem->array_element_start = sn_array_element_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,
state->strval->len));
......@@ -5110,7 +5115,7 @@ iterate_json_values(text *json, uint32 flags, void *action_state,
sem->scalar = iterate_values_scalar;
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,
sem->array_element_start = transform_string_values_array_element_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);
}
......
......@@ -33,6 +33,28 @@ typedef enum
JSON_TOKEN_END
} 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.
......@@ -101,7 +123,14 @@ typedef struct JsonSemAction
* points to. If the action pointers are NULL the parser
* 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 */
extern JsonSemAction nullSemAction;
......@@ -110,8 +139,13 @@ extern JsonSemAction nullSemAction;
* 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
* 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.
......@@ -128,7 +162,13 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
bool need_escapes);
/* 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.
......
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