Re: Error-safe user functions - Mailing list pgsql-hackers
| From | Tom Lane |
|---|---|
| Subject | Re: Error-safe user functions |
| Date | |
| Msg-id | 1436686.1670701118@sss.pgh.pa.us Whole thread Raw |
| In response to | Re: Error-safe user functions (Andrew Dunstan <andrew@dunslane.net>) |
| Responses |
Re: Error-safe user functions
|
| List | pgsql-hackers |
Andrew Dunstan <andrew@dunslane.net> writes:
> OK, json is a fairly easy case, see attached. But jsonb is a different
> kettle of fish. Both the semantic routines called by the parser and the
> subsequent call to JsonbValueToJsonb() can raise errors. These are
> pretty much all about breaking various limits (for strings, objects,
> arrays). There's also a call to numeric_in, but I assume that a string
> that's already parsed as a valid json numeric literal won't upset
> numeric_in.
Um, nope ...
regression=# select '1e1000000'::jsonb;
ERROR: value overflows numeric format
LINE 1: select '1e1000000'::jsonb;
^
> Many of these occur several calls down the stack, so
> adjusting everything to deal with them would be fairly invasive. Perhaps
> we could instead document that this class of input error won't be
> trapped, at least for jsonb.
Seeing that SQL/JSON is one of the major drivers of this whole project,
it seemed a little sad to me that jsonb couldn't manage to implement
what is required. So I spent a bit of time poking at it. Attached
is an extended version of your patch that also covers jsonb.
The main thing I soon realized is that the JsonSemAction API is based
on the assumption that semantic actions will report errors by throwing
them. This is a bit schizophrenic considering the parser itself carefully
hands back error codes instead of throwing anything (excluding palloc
failures of course). What I propose in the attached is that we change
that API so that action functions return JsonParseErrorType, and add
an enum value denoting "I already logged a suitable error, so you don't
have to". It was a little tedious to modify all the existing functions
that way, but not hard. Only the ones used by jsonb_in need to do
anything except "return JSON_SUCCESS", at least for now.
(I wonder if pg_verifybackup's parse_manifest.c could use a second
look at how it's handling errors, given this API. I didn't study it
closely.)
I have not done anything here about errors within JsonbValueToJsonb.
There would need to be another round of API-extension in that area
if we want to be able to trap its errors. As you say, those are mostly
about exceeding implementation size limits, so I suppose one could argue
that they are not so different from palloc failure. It's still annoying.
If people are good with the changes attached, I might take a look at
that.
regards, tom lane
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index fee2ffb55c..e6896eccfe 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -81,9 +81,10 @@ json_in(PG_FUNCTION_ARGS)
/* validate it */
lex = makeJsonLexContext(result, false);
- pg_parse_json_or_ereport(lex, &nullSemAction);
+ if (!pg_parse_json_or_errsave(lex, &nullSemAction, fcinfo->context))
+ PG_RETURN_NULL();
- /* Internal representation is the same as text, for now */
+ /* Internal representation is the same as text */
PG_RETURN_TEXT_P(result);
}
@@ -1337,7 +1338,7 @@ json_typeof(PG_FUNCTION_ARGS)
/* Lex exactly one token from the input and check its type. */
result = json_lex(lex);
if (result != JSON_SUCCESS)
- json_ereport_error(result, lex);
+ json_errsave_error(result, lex, NULL);
tok = lex->token_type;
switch (tok)
{
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 9e14922ec2..7c1e5e6144 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -33,6 +33,7 @@ typedef struct JsonbInState
{
JsonbParseState *parseState;
JsonbValue *res;
+ Node *escontext;
} JsonbInState;
/* unlike with json categories, we need to treat json and jsonb differently */
@@ -61,15 +62,15 @@ typedef struct JsonbAggState
Oid val_output_func;
} JsonbAggState;
-static inline Datum jsonb_from_cstring(char *json, int len);
-static size_t checkStringLen(size_t len);
-static void jsonb_in_object_start(void *pstate);
-static void jsonb_in_object_end(void *pstate);
-static void jsonb_in_array_start(void *pstate);
-static void jsonb_in_array_end(void *pstate);
-static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
+static inline Datum jsonb_from_cstring(char *json, int len, Node *escontext);
+static bool checkStringLen(size_t len, Node *escontext);
+static JsonParseErrorType jsonb_in_object_start(void *pstate);
+static JsonParseErrorType jsonb_in_object_end(void *pstate);
+static JsonParseErrorType jsonb_in_array_start(void *pstate);
+static JsonParseErrorType jsonb_in_array_end(void *pstate);
+static JsonParseErrorType jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
-static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
+static JsonParseErrorType jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
static void jsonb_categorize_type(Oid typoid,
JsonbTypeCategory *tcategory,
Oid *outfuncoid);
@@ -98,7 +99,7 @@ jsonb_in(PG_FUNCTION_ARGS)
{
char *json = PG_GETARG_CSTRING(0);
- return jsonb_from_cstring(json, strlen(json));
+ return jsonb_from_cstring(json, strlen(json), fcinfo->context);
}
/*
@@ -122,7 +123,7 @@ jsonb_recv(PG_FUNCTION_ARGS)
else
elog(ERROR, "unsupported jsonb version number %d", version);
- return jsonb_from_cstring(str, nbytes);
+ return jsonb_from_cstring(str, nbytes, NULL);
}
/*
@@ -251,9 +252,12 @@ jsonb_typeof(PG_FUNCTION_ARGS)
* Turns json string into a jsonb Datum.
*
* Uses the json parser (with hooks) to construct a jsonb.
+ *
+ * If escontext points to an ErrorSaveContext, errors are reported there
+ * instead of being thrown.
*/
static inline Datum
-jsonb_from_cstring(char *json, int len)
+jsonb_from_cstring(char *json, int len, Node *escontext)
{
JsonLexContext *lex;
JsonbInState state;
@@ -263,6 +267,7 @@ jsonb_from_cstring(char *json, int len)
memset(&sem, 0, sizeof(sem));
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
+ state.escontext = escontext;
sem.semstate = (void *) &state;
sem.object_start = jsonb_in_object_start;
@@ -272,58 +277,67 @@ jsonb_from_cstring(char *json, int len)
sem.scalar = jsonb_in_scalar;
sem.object_field_start = jsonb_in_object_field_start;
- pg_parse_json_or_ereport(lex, &sem);
+ if (!pg_parse_json_or_errsave(lex, &sem, escontext))
+ return (Datum) 0;
/* after parsing, the item member has the composed jsonb structure */
PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
}
-static size_t
-checkStringLen(size_t len)
+static bool
+checkStringLen(size_t len, Node *escontext)
{
if (len > JENTRY_OFFLENMASK)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("string too long to represent as jsonb string"),
errdetail("Due to an implementation restriction, jsonb strings cannot exceed %d bytes.",
JENTRY_OFFLENMASK)));
- return len;
+ return true;
}
-static void
+static JsonParseErrorType
jsonb_in_object_start(void *pstate)
{
JsonbInState *_state = (JsonbInState *) pstate;
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
jsonb_in_object_end(void *pstate)
{
JsonbInState *_state = (JsonbInState *) pstate;
_state->res = pushJsonbValue(&_state->parseState, WJB_END_OBJECT, NULL);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
jsonb_in_array_start(void *pstate)
{
JsonbInState *_state = (JsonbInState *) pstate;
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, NULL);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
jsonb_in_array_end(void *pstate)
{
JsonbInState *_state = (JsonbInState *) pstate;
_state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
jsonb_in_object_field_start(void *pstate, char *fname, bool isnull)
{
JsonbInState *_state = (JsonbInState *) pstate;
@@ -331,10 +345,14 @@ jsonb_in_object_field_start(void *pstate, char *fname, bool isnull)
Assert(fname != NULL);
v.type = jbvString;
- v.val.string.len = checkStringLen(strlen(fname));
+ v.val.string.len = strlen(fname);
+ if (!checkStringLen(v.val.string.len, _state->escontext))
+ return JSON_SEM_ACTION_FAILED;
v.val.string.val = fname;
_state->res = pushJsonbValue(&_state->parseState, WJB_KEY, &v);
+
+ return JSON_SUCCESS;
}
static void
@@ -367,7 +385,7 @@ jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal)
/*
* For jsonb we always want the de-escaped value - that's what's in token
*/
-static void
+static JsonParseErrorType
jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
{
JsonbInState *_state = (JsonbInState *) pstate;
@@ -380,7 +398,9 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
case JSON_TOKEN_STRING:
Assert(token != NULL);
v.type = jbvString;
- v.val.string.len = checkStringLen(strlen(token));
+ v.val.string.len = strlen(token);
+ if (!checkStringLen(v.val.string.len, _state->escontext))
+ return JSON_SEM_ACTION_FAILED;
v.val.string.val = token;
break;
case JSON_TOKEN_NUMBER:
@@ -391,10 +411,11 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
*/
Assert(token != NULL);
v.type = jbvNumeric;
- numd = DirectFunctionCall3(numeric_in,
- CStringGetDatum(token),
- ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(-1));
+ if (!DirectInputFunctionCallSafe(numeric_in, token,
+ InvalidOid, -1,
+ _state->escontext,
+ &numd))
+ return JSON_SEM_ACTION_FAILED;
v.val.numeric = DatumGetNumeric(numd);
break;
case JSON_TOKEN_TRUE:
@@ -443,6 +464,8 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
elog(ERROR, "unexpected parent of nested structure");
}
}
+
+ return JSON_SUCCESS;
}
/*
@@ -726,6 +749,9 @@ jsonb_categorize_type(Oid typoid,
*
* If key_scalar is true, the value is stored as a key, so insist
* it's of an acceptable type, and force it to be a jbvString.
+ *
+ * Note: currently, we assume that result->escontext is NULL and errors
+ * will be thrown.
*/
static void
datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
@@ -898,7 +924,8 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
default:
outputstr = OidOutputFunctionCall(outfuncoid, val);
jb.type = jbvString;
- jb.val.string.len = checkStringLen(strlen(outputstr));
+ jb.val.string.len = strlen(outputstr);
+ (void) checkStringLen(jb.val.string.len, NULL);
jb.val.string.val = outputstr;
break;
}
@@ -1636,6 +1663,7 @@ jsonb_agg_finalfn(PG_FUNCTION_ARGS)
* shallow clone is sufficient as we aren't going to change any of the
* values, just add the final array end marker.
*/
+ memset(&result, 0, sizeof(JsonbInState));
result.parseState = clone_parse_state(arg->res->parseState);
@@ -1868,6 +1896,7 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
* going to change any of the values, just add the final object end
* marker.
*/
+ memset(&result, 0, sizeof(JsonbInState));
result.parseState = clone_parse_state(arg->res->parseState);
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index bfc3f02a86..463a8fdf23 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -25,6 +25,7 @@
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/miscnodes.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -336,20 +337,20 @@ typedef struct JsObject
static int report_json_context(JsonLexContext *lex);
/* semantic action functions for json_object_keys */
-static void okeys_object_field_start(void *state, char *fname, bool isnull);
-static void okeys_array_start(void *state);
-static void okeys_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType okeys_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType okeys_array_start(void *state);
+static JsonParseErrorType 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);
+static JsonParseErrorType get_object_start(void *state);
+static JsonParseErrorType get_object_end(void *state);
+static JsonParseErrorType get_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType get_object_field_end(void *state, char *fname, bool isnull);
+static JsonParseErrorType get_array_start(void *state);
+static JsonParseErrorType get_array_end(void *state);
+static JsonParseErrorType get_array_element_start(void *state, bool isnull);
+static JsonParseErrorType get_array_element_end(void *state, bool isnull);
+static JsonParseErrorType get_scalar(void *state, char *token, JsonTokenType tokentype);
/* common worker function for json getter functions */
static Datum get_path_all(FunctionCallInfo fcinfo, bool as_text);
@@ -359,9 +360,9 @@ static Datum get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text);
static text *JsonbValueAsText(JsonbValue *v);
/* semantic action functions for json_array_length */
-static void alen_object_start(void *state);
-static void alen_scalar(void *state, char *token, JsonTokenType tokentype);
-static void alen_array_element_start(void *state, bool isnull);
+static JsonParseErrorType alen_object_start(void *state);
+static JsonParseErrorType alen_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType alen_array_element_start(void *state, bool isnull);
/* common workers for json{b}_each* functions */
static Datum each_worker(FunctionCallInfo fcinfo, bool as_text);
@@ -369,10 +370,10 @@ static Datum each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
bool as_text);
/* semantic action functions for json_each */
-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);
-static void each_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType each_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType each_object_field_end(void *state, char *fname, bool isnull);
+static JsonParseErrorType each_array_start(void *state);
+static JsonParseErrorType each_scalar(void *state, char *token, JsonTokenType tokentype);
/* common workers for json{b}_array_elements_* functions */
static Datum elements_worker(FunctionCallInfo fcinfo, const char *funcname,
@@ -381,44 +382,44 @@ static Datum elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname
bool as_text);
/* semantic action functions for json_array_elements */
-static void elements_object_start(void *state);
-static void elements_array_element_start(void *state, bool isnull);
-static void elements_array_element_end(void *state, bool isnull);
-static void elements_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType elements_object_start(void *state);
+static JsonParseErrorType elements_array_element_start(void *state, bool isnull);
+static JsonParseErrorType elements_array_element_end(void *state, bool isnull);
+static JsonParseErrorType elements_scalar(void *state, char *token, JsonTokenType tokentype);
/* turn a json object into a hash table */
static HTAB *get_json_object_as_hash(char *json, int len, const char *funcname);
/* semantic actions for populate_array_json */
-static void populate_array_object_start(void *_state);
-static void populate_array_array_end(void *_state);
-static void populate_array_element_start(void *_state, bool isnull);
-static void populate_array_element_end(void *_state, bool isnull);
-static void populate_array_scalar(void *_state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType populate_array_object_start(void *_state);
+static JsonParseErrorType populate_array_array_end(void *_state);
+static JsonParseErrorType populate_array_element_start(void *_state, bool isnull);
+static JsonParseErrorType populate_array_element_end(void *_state, bool isnull);
+static JsonParseErrorType populate_array_scalar(void *_state, char *token, JsonTokenType tokentype);
/* semantic action functions for get_json_object_as_hash */
-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);
-static void hash_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType hash_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType hash_object_field_end(void *state, char *fname, bool isnull);
+static JsonParseErrorType hash_array_start(void *state);
+static JsonParseErrorType hash_scalar(void *state, char *token, JsonTokenType tokentype);
/* semantic action functions for populate_recordset */
-static void populate_recordset_object_field_start(void *state, char *fname, bool isnull);
-static void populate_recordset_object_field_end(void *state, char *fname, bool isnull);
-static void populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype);
-static void populate_recordset_object_start(void *state);
-static void populate_recordset_object_end(void *state);
-static void populate_recordset_array_start(void *state);
-static void populate_recordset_array_element_start(void *state, bool isnull);
+static JsonParseErrorType populate_recordset_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType populate_recordset_object_field_end(void *state, char *fname, bool isnull);
+static JsonParseErrorType populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType populate_recordset_object_start(void *state);
+static JsonParseErrorType populate_recordset_object_end(void *state);
+static JsonParseErrorType populate_recordset_array_start(void *state);
+static JsonParseErrorType populate_recordset_array_element_start(void *state, bool isnull);
/* semantic action functions for json_strip_nulls */
-static void sn_object_start(void *state);
-static void sn_object_end(void *state);
-static void sn_array_start(void *state);
-static void sn_array_end(void *state);
-static void sn_object_field_start(void *state, char *fname, bool isnull);
-static void sn_array_element_start(void *state, bool isnull);
-static void sn_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType sn_object_start(void *state);
+static JsonParseErrorType sn_object_end(void *state);
+static JsonParseErrorType sn_array_start(void *state);
+static JsonParseErrorType sn_array_end(void *state);
+static JsonParseErrorType sn_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType sn_array_element_start(void *state, bool isnull);
+static JsonParseErrorType sn_scalar(void *state, char *token, JsonTokenType tokentype);
/* worker functions for populate_record, to_record, populate_recordset and to_recordset */
static Datum populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
@@ -478,33 +479,43 @@ static void setPathArray(JsonbIterator **it, Datum *path_elems,
JsonbValue *newval, uint32 nelems, int op_type);
/* function supporting iterate_json_values */
-static void iterate_values_scalar(void *state, char *token, JsonTokenType tokentype);
-static void iterate_values_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType iterate_values_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType iterate_values_object_field_start(void *state, char *fname, bool isnull);
/* functions supporting transform_json_string_values */
-static void transform_string_values_object_start(void *state);
-static void transform_string_values_object_end(void *state);
-static void transform_string_values_array_start(void *state);
-static void transform_string_values_array_end(void *state);
-static void transform_string_values_object_field_start(void *state, char *fname, bool isnull);
-static void transform_string_values_array_element_start(void *state, bool isnull);
-static void transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
+static JsonParseErrorType transform_string_values_object_start(void *state);
+static JsonParseErrorType transform_string_values_object_end(void *state);
+static JsonParseErrorType transform_string_values_array_start(void *state);
+static JsonParseErrorType transform_string_values_array_end(void *state);
+static JsonParseErrorType transform_string_values_object_field_start(void *state, char *fname, bool isnull);
+static JsonParseErrorType transform_string_values_array_element_start(void *state, bool isnull);
+static JsonParseErrorType transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
+
/*
- * pg_parse_json_or_ereport
+ * pg_parse_json_or_errsave
*
* This function is like pg_parse_json, except that it does not return a
* JsonParseErrorType. Instead, in case of any failure, this function will
+ * save error data into *escontext if that's an ErrorSaveContext, otherwise
* ereport(ERROR).
+ *
+ * Returns a boolean indicating success or failure (failure will only be
+ * returned when escontext is an ErrorSaveContext).
*/
-void
-pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem)
+bool
+pg_parse_json_or_errsave(JsonLexContext *lex, JsonSemAction *sem,
+ Node *escontext)
{
JsonParseErrorType result;
result = pg_parse_json(lex, sem);
if (result != JSON_SUCCESS)
- json_ereport_error(result, lex);
+ {
+ json_errsave_error(result, lex, escontext);
+ return false;
+ }
+ return true;
}
/*
@@ -608,17 +619,24 @@ jsonb_object_keys(PG_FUNCTION_ARGS)
* Report a JSON error.
*/
void
-json_ereport_error(JsonParseErrorType error, JsonLexContext *lex)
+json_errsave_error(JsonParseErrorType error, JsonLexContext *lex,
+ Node *escontext)
{
if (error == JSON_UNICODE_HIGH_ESCAPE ||
error == JSON_UNICODE_CODE_POINT_ZERO)
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
errmsg("unsupported Unicode escape sequence"),
errdetail_internal("%s", json_errdetail(error, lex)),
report_json_context(lex)));
+ else if (error == JSON_SEM_ACTION_FAILED)
+ {
+ /* semantic action function had better have reported something */
+ if (!SOFT_ERROR_OCCURRED(escontext))
+ elog(ERROR, "JSON semantic action function did not provide error information");
+ }
else
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s", "json"),
errdetail_internal("%s", json_errdetail(error, lex)),
@@ -745,14 +763,14 @@ json_object_keys(PG_FUNCTION_ARGS)
SRF_RETURN_DONE(funcctx);
}
-static void
+static JsonParseErrorType
okeys_object_field_start(void *state, char *fname, bool isnull)
{
OkeysState *_state = (OkeysState *) state;
/* only collecting keys for the top level object */
if (_state->lex->lex_level != 1)
- return;
+ return JSON_SUCCESS;
/* enlarge result array if necessary */
if (_state->result_count >= _state->result_size)
@@ -764,9 +782,11 @@ okeys_object_field_start(void *state, char *fname, bool isnull)
/* save a copy of the field name */
_state->result[_state->result_count++] = pstrdup(fname);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
okeys_array_start(void *state)
{
OkeysState *_state = (OkeysState *) state;
@@ -777,9 +797,11 @@ okeys_array_start(void *state)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot call %s on an array",
"json_object_keys")));
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
okeys_scalar(void *state, char *token, JsonTokenType tokentype)
{
OkeysState *_state = (OkeysState *) state;
@@ -790,6 +812,8 @@ okeys_scalar(void *state, char *token, JsonTokenType tokentype)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot call %s on a scalar",
"json_object_keys")));
+
+ return JSON_SUCCESS;
}
/*
@@ -1112,7 +1136,7 @@ get_worker(text *json,
return state->tresult;
}
-static void
+static JsonParseErrorType
get_object_start(void *state)
{
GetState *_state = (GetState *) state;
@@ -1127,9 +1151,11 @@ get_object_start(void *state)
*/
_state->result_start = _state->lex->token_start;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_object_end(void *state)
{
GetState *_state = (GetState *) state;
@@ -1143,9 +1169,11 @@ get_object_end(void *state)
_state->tresult = cstring_to_text_with_len(start, len);
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_object_field_start(void *state, char *fname, bool isnull)
{
GetState *_state = (GetState *) state;
@@ -1188,9 +1216,11 @@ get_object_field_start(void *state, char *fname, bool isnull)
_state->result_start = _state->lex->token_start;
}
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_object_field_end(void *state, char *fname, bool isnull)
{
GetState *_state = (GetState *) state;
@@ -1237,9 +1267,11 @@ get_object_field_end(void *state, char *fname, bool isnull)
/* this should be unnecessary but let's do it for cleanliness: */
_state->result_start = NULL;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_array_start(void *state)
{
GetState *_state = (GetState *) state;
@@ -1260,7 +1292,7 @@ get_array_start(void *state)
error = json_count_array_elements(_state->lex, &nelements);
if (error != JSON_SUCCESS)
- json_ereport_error(error, _state->lex);
+ json_errsave_error(error, _state->lex, NULL);
if (-_state->path_indexes[lex_level] <= nelements)
_state->path_indexes[lex_level] += nelements;
@@ -1275,9 +1307,11 @@ get_array_start(void *state)
*/
_state->result_start = _state->lex->token_start;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_array_end(void *state)
{
GetState *_state = (GetState *) state;
@@ -1291,9 +1325,11 @@ get_array_end(void *state)
_state->tresult = cstring_to_text_with_len(start, len);
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_array_element_start(void *state, bool isnull)
{
GetState *_state = (GetState *) state;
@@ -1337,9 +1373,11 @@ get_array_element_start(void *state, bool isnull)
_state->result_start = _state->lex->token_start;
}
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_array_element_end(void *state, bool isnull)
{
GetState *_state = (GetState *) state;
@@ -1379,9 +1417,11 @@ get_array_element_end(void *state, bool isnull)
_state->result_start = NULL;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
get_scalar(void *state, char *token, JsonTokenType tokentype)
{
GetState *_state = (GetState *) state;
@@ -1420,6 +1460,8 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
/* make sure the next call to get_scalar doesn't overwrite it */
_state->next_scalar = false;
}
+
+ return JSON_SUCCESS;
}
Datum
@@ -1834,7 +1876,7 @@ jsonb_array_length(PG_FUNCTION_ARGS)
* a scalar or an object).
*/
-static void
+static JsonParseErrorType
alen_object_start(void *state)
{
AlenState *_state = (AlenState *) state;
@@ -1844,9 +1886,11 @@ alen_object_start(void *state)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot get array length of a non-array")));
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
alen_scalar(void *state, char *token, JsonTokenType tokentype)
{
AlenState *_state = (AlenState *) state;
@@ -1856,9 +1900,11 @@ alen_scalar(void *state, char *token, JsonTokenType tokentype)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot get array length of a scalar")));
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
alen_array_element_start(void *state, bool isnull)
{
AlenState *_state = (AlenState *) state;
@@ -1866,6 +1912,8 @@ alen_array_element_start(void *state, bool isnull)
/* just count up all the level 1 elements */
if (_state->lex->lex_level == 1)
_state->count++;
+
+ return JSON_SUCCESS;
}
/*
@@ -2026,7 +2074,7 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
}
-static void
+static JsonParseErrorType
each_object_field_start(void *state, char *fname, bool isnull)
{
EachState *_state = (EachState *) state;
@@ -2044,9 +2092,11 @@ each_object_field_start(void *state, char *fname, bool isnull)
else
_state->result_start = _state->lex->token_start;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
each_object_field_end(void *state, char *fname, bool isnull)
{
EachState *_state = (EachState *) state;
@@ -2059,7 +2109,7 @@ each_object_field_end(void *state, char *fname, bool isnull)
/* skip over nested objects */
if (_state->lex->lex_level != 1)
- return;
+ return JSON_SUCCESS;
/* use the tmp context so we can clean up after each tuple is done */
old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
@@ -2090,9 +2140,11 @@ each_object_field_end(void *state, char *fname, bool isnull)
/* clean up and switch back */
MemoryContextSwitchTo(old_cxt);
MemoryContextReset(_state->tmp_cxt);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
each_array_start(void *state)
{
EachState *_state = (EachState *) state;
@@ -2102,9 +2154,11 @@ each_array_start(void *state)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot deconstruct an array as an object")));
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
each_scalar(void *state, char *token, JsonTokenType tokentype)
{
EachState *_state = (EachState *) state;
@@ -2118,6 +2172,8 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
/* supply de-escaped value if required */
if (_state->next_scalar)
_state->normalized_scalar = token;
+
+ return JSON_SUCCESS;
}
/*
@@ -2268,7 +2324,7 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
PG_RETURN_NULL();
}
-static void
+static JsonParseErrorType
elements_array_element_start(void *state, bool isnull)
{
ElementsState *_state = (ElementsState *) state;
@@ -2286,9 +2342,11 @@ elements_array_element_start(void *state, bool isnull)
else
_state->result_start = _state->lex->token_start;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
elements_array_element_end(void *state, bool isnull)
{
ElementsState *_state = (ElementsState *) state;
@@ -2301,7 +2359,7 @@ elements_array_element_end(void *state, bool isnull)
/* skip over nested objects */
if (_state->lex->lex_level != 1)
- return;
+ return JSON_SUCCESS;
/* use the tmp context so we can clean up after each tuple is done */
old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
@@ -2330,9 +2388,11 @@ elements_array_element_end(void *state, bool isnull)
/* clean up and switch back */
MemoryContextSwitchTo(old_cxt);
MemoryContextReset(_state->tmp_cxt);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
elements_object_start(void *state)
{
ElementsState *_state = (ElementsState *) state;
@@ -2343,9 +2403,11 @@ elements_object_start(void *state)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot call %s on a non-array",
_state->function_name)));
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
elements_scalar(void *state, char *token, JsonTokenType tokentype)
{
ElementsState *_state = (ElementsState *) state;
@@ -2360,6 +2422,8 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
/* supply de-escaped value if required */
if (_state->next_scalar)
_state->normalized_scalar = token;
+
+ return JSON_SUCCESS;
}
/*
@@ -2508,7 +2572,7 @@ populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv)
}
/* json object start handler for populate_array_json() */
-static void
+static JsonParseErrorType
populate_array_object_start(void *_state)
{
PopulateArrayState *state = (PopulateArrayState *) _state;
@@ -2518,10 +2582,12 @@ populate_array_object_start(void *_state)
populate_array_assign_ndims(state->ctx, ndim);
else if (ndim < state->ctx->ndims)
populate_array_report_expected_array(state->ctx, ndim);
+
+ return JSON_SUCCESS;
}
/* json array end handler for populate_array_json() */
-static void
+static JsonParseErrorType
populate_array_array_end(void *_state)
{
PopulateArrayState *state = (PopulateArrayState *) _state;
@@ -2533,10 +2599,12 @@ populate_array_array_end(void *_state)
if (ndim < ctx->ndims)
populate_array_check_dimension(ctx, ndim);
+
+ return JSON_SUCCESS;
}
/* json array element start handler for populate_array_json() */
-static void
+static JsonParseErrorType
populate_array_element_start(void *_state, bool isnull)
{
PopulateArrayState *state = (PopulateArrayState *) _state;
@@ -2549,10 +2617,12 @@ populate_array_element_start(void *_state, bool isnull)
state->element_type = state->lex->token_type;
state->element_scalar = NULL;
}
+
+ return JSON_SUCCESS;
}
/* json array element end handler for populate_array_json() */
-static void
+static JsonParseErrorType
populate_array_element_end(void *_state, bool isnull)
{
PopulateArrayState *state = (PopulateArrayState *) _state;
@@ -2588,10 +2658,12 @@ populate_array_element_end(void *_state, bool isnull)
populate_array_element(ctx, ndim, &jsv);
}
+
+ return JSON_SUCCESS;
}
/* json scalar handler for populate_array_json() */
-static void
+static JsonParseErrorType
populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
{
PopulateArrayState *state = (PopulateArrayState *) _state;
@@ -2610,6 +2682,8 @@ populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
/* element_type must already be set in populate_array_element_start() */
Assert(state->element_type == tokentype);
}
+
+ return JSON_SUCCESS;
}
/* parse a json array and populate array */
@@ -3491,13 +3565,13 @@ get_json_object_as_hash(char *json, int len, const char *funcname)
return tab;
}
-static void
+static JsonParseErrorType
hash_object_field_start(void *state, char *fname, bool isnull)
{
JHashState *_state = (JHashState *) state;
if (_state->lex->lex_level > 1)
- return;
+ return JSON_SUCCESS;
/* remember token type */
_state->saved_token_type = _state->lex->token_type;
@@ -3513,9 +3587,11 @@ hash_object_field_start(void *state, char *fname, bool isnull)
/* must be a scalar */
_state->save_json_start = NULL;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
hash_object_field_end(void *state, char *fname, bool isnull)
{
JHashState *_state = (JHashState *) state;
@@ -3526,7 +3602,7 @@ hash_object_field_end(void *state, char *fname, bool isnull)
* Ignore nested fields.
*/
if (_state->lex->lex_level > 1)
- return;
+ return JSON_SUCCESS;
/*
* Ignore field names >= NAMEDATALEN - they can't match a record field.
@@ -3536,7 +3612,7 @@ hash_object_field_end(void *state, char *fname, bool isnull)
* has previously insisted on exact equality, so we keep this behavior.)
*/
if (strlen(fname) >= NAMEDATALEN)
- return;
+ return JSON_SUCCESS;
hashentry = hash_search(_state->hash, fname, HASH_ENTER, &found);
@@ -3562,9 +3638,11 @@ hash_object_field_end(void *state, char *fname, bool isnull)
/* must have had a scalar instead */
hashentry->val = _state->saved_scalar;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
hash_array_start(void *state)
{
JHashState *_state = (JHashState *) state;
@@ -3573,9 +3651,11 @@ hash_array_start(void *state)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot call %s on an array", _state->function_name)));
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
hash_scalar(void *state, char *token, JsonTokenType tokentype)
{
JHashState *_state = (JHashState *) state;
@@ -3591,6 +3671,8 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
/* saved_token_type must already be set in hash_object_field_start() */
Assert(_state->saved_token_type == tokentype);
}
+
+ return JSON_SUCCESS;
}
@@ -3840,7 +3922,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
PG_RETURN_NULL();
}
-static void
+static JsonParseErrorType
populate_recordset_object_start(void *state)
{
PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
@@ -3856,7 +3938,7 @@ populate_recordset_object_start(void *state)
/* Nested objects require no special processing */
if (lex_level > 1)
- return;
+ return JSON_SUCCESS;
/* Object at level 1: set up a new hash table for this object */
ctl.keysize = NAMEDATALEN;
@@ -3866,9 +3948,11 @@ populate_recordset_object_start(void *state)
100,
&ctl,
HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
populate_recordset_object_end(void *state)
{
PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
@@ -3876,7 +3960,7 @@ populate_recordset_object_end(void *state)
/* Nested objects require no special processing */
if (_state->lex->lex_level > 1)
- return;
+ return JSON_SUCCESS;
obj.is_json = true;
obj.val.json_hash = _state->json_hash;
@@ -3887,9 +3971,11 @@ populate_recordset_object_end(void *state)
/* Done with hash for this object */
hash_destroy(_state->json_hash);
_state->json_hash = NULL;
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
populate_recordset_array_element_start(void *state, bool isnull)
{
PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
@@ -3900,15 +3986,18 @@ populate_recordset_array_element_start(void *state, bool isnull)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("argument of %s must be an array of objects",
_state->function_name)));
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
populate_recordset_array_start(void *state)
{
/* nothing to do */
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype)
{
PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
@@ -3921,15 +4010,17 @@ populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype)
if (_state->lex->lex_level == 2)
_state->saved_scalar = token;
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
populate_recordset_object_field_start(void *state, char *fname, bool isnull)
{
PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
if (_state->lex->lex_level > 2)
- return;
+ return JSON_SUCCESS;
_state->saved_token_type = _state->lex->token_type;
@@ -3942,9 +4033,11 @@ populate_recordset_object_field_start(void *state, char *fname, bool isnull)
{
_state->save_json_start = NULL;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
populate_recordset_object_field_end(void *state, char *fname, bool isnull)
{
PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
@@ -3955,7 +4048,7 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
* Ignore nested fields.
*/
if (_state->lex->lex_level > 2)
- return;
+ return JSON_SUCCESS;
/*
* Ignore field names >= NAMEDATALEN - they can't match a record field.
@@ -3965,7 +4058,7 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
* has previously insisted on exact equality, so we keep this behavior.)
*/
if (strlen(fname) >= NAMEDATALEN)
- return;
+ return JSON_SUCCESS;
hashentry = hash_search(_state->json_hash, fname, HASH_ENTER, &found);
@@ -3991,6 +4084,8 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
/* must have had a scalar instead */
hashentry->val = _state->saved_scalar;
}
+
+ return JSON_SUCCESS;
}
/*
@@ -4002,39 +4097,47 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
* is called.
*/
-static void
+static JsonParseErrorType
sn_object_start(void *state)
{
StripnullState *_state = (StripnullState *) state;
appendStringInfoCharMacro(_state->strval, '{');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
sn_object_end(void *state)
{
StripnullState *_state = (StripnullState *) state;
appendStringInfoCharMacro(_state->strval, '}');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
sn_array_start(void *state)
{
StripnullState *_state = (StripnullState *) state;
appendStringInfoCharMacro(_state->strval, '[');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
sn_array_end(void *state)
{
StripnullState *_state = (StripnullState *) state;
appendStringInfoCharMacro(_state->strval, ']');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
sn_object_field_start(void *state, char *fname, bool isnull)
{
StripnullState *_state = (StripnullState *) state;
@@ -4047,7 +4150,7 @@ sn_object_field_start(void *state, char *fname, bool isnull)
* object or array. The flag will be reset in the scalar action.
*/
_state->skip_next_null = true;
- return;
+ return JSON_SUCCESS;
}
if (_state->strval->data[_state->strval->len - 1] != '{')
@@ -4060,18 +4163,22 @@ sn_object_field_start(void *state, char *fname, bool isnull)
escape_json(_state->strval, fname);
appendStringInfoCharMacro(_state->strval, ':');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
sn_array_element_start(void *state, bool isnull)
{
StripnullState *_state = (StripnullState *) state;
if (_state->strval->data[_state->strval->len - 1] != '[')
appendStringInfoCharMacro(_state->strval, ',');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
sn_scalar(void *state, char *token, JsonTokenType tokentype)
{
StripnullState *_state = (StripnullState *) state;
@@ -4080,13 +4187,15 @@ sn_scalar(void *state, char *token, JsonTokenType tokentype)
{
Assert(tokentype == JSON_TOKEN_NULL);
_state->skip_next_null = false;
- return;
+ return JSON_SUCCESS;
}
if (tokentype == JSON_TOKEN_STRING)
escape_json(_state->strval, token);
else
appendStringInfoString(_state->strval, token);
+
+ return JSON_SUCCESS;
}
/*
@@ -5326,7 +5435,7 @@ iterate_json_values(text *json, uint32 flags, void *action_state,
* An auxiliary function for iterate_json_values to invoke a specified
* JsonIterateStringValuesAction for specified values.
*/
-static void
+static JsonParseErrorType
iterate_values_scalar(void *state, char *token, JsonTokenType tokentype)
{
IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
@@ -5350,9 +5459,11 @@ iterate_values_scalar(void *state, char *token, JsonTokenType tokentype)
/* do not call callback for any other token */
break;
}
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
iterate_values_object_field_start(void *state, char *fname, bool isnull)
{
IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
@@ -5363,6 +5474,8 @@ iterate_values_object_field_start(void *state, char *fname, bool isnull)
_state->action(_state->action_state, val, strlen(val));
}
+
+ return JSON_SUCCESS;
}
/*
@@ -5430,7 +5543,6 @@ transform_json_string_values(text *json, void *action_state,
state->action_state = action_state;
sem->semstate = (void *) state;
- sem->scalar = transform_string_values_scalar;
sem->object_start = transform_string_values_object_start;
sem->object_end = transform_string_values_object_end;
sem->array_start = transform_string_values_array_start;
@@ -5449,39 +5561,47 @@ transform_json_string_values(text *json, void *action_state,
* specified JsonTransformStringValuesAction for all values and left everything
* else untouched.
*/
-static void
+static JsonParseErrorType
transform_string_values_object_start(void *state)
{
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
appendStringInfoCharMacro(_state->strval, '{');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
transform_string_values_object_end(void *state)
{
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
appendStringInfoCharMacro(_state->strval, '}');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
transform_string_values_array_start(void *state)
{
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
appendStringInfoCharMacro(_state->strval, '[');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
transform_string_values_array_end(void *state)
{
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
appendStringInfoCharMacro(_state->strval, ']');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
transform_string_values_object_field_start(void *state, char *fname, bool isnull)
{
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
@@ -5495,18 +5615,22 @@ transform_string_values_object_field_start(void *state, char *fname, bool isnull
*/
escape_json(_state->strval, fname);
appendStringInfoCharMacro(_state->strval, ':');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
transform_string_values_array_element_start(void *state, bool isnull)
{
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
if (_state->strval->data[_state->strval->len - 1] != '[')
appendStringInfoCharMacro(_state->strval, ',');
+
+ return JSON_SUCCESS;
}
-static void
+static JsonParseErrorType
transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
{
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
@@ -5519,4 +5643,6 @@ transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype
}
else
appendStringInfoString(_state->strval, token);
+
+ return JSON_SUCCESS;
}
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 0d37f69298..7b28a266ce 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -1614,6 +1614,51 @@ InputFunctionCallSafe(FmgrInfo *flinfo, char *str,
return true;
}
+/*
+ * Call a directly-named datatype input function, with non-exception
+ * handling of "soft" errors.
+ *
+ * This is like InputFunctionCallSafe, except that it is given a direct
+ * pointer to the C function to call. We assume that that function is
+ * strict. Also, the function cannot be one that needs to
+ * look at FmgrInfo, since there won't be any.
+ */
+bool
+DirectInputFunctionCallSafe(PGFunction func, char *str,
+ Oid typioparam, int32 typmod,
+ fmNodePtr escontext,
+ Datum *result)
+{
+ LOCAL_FCINFO(fcinfo, 3);
+
+ if (str == NULL)
+ {
+ *result = (Datum) 0; /* just return null result */
+ return true;
+ }
+
+ InitFunctionCallInfoData(*fcinfo, NULL, 3, InvalidOid, escontext, NULL);
+
+ fcinfo->args[0].value = CStringGetDatum(str);
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].value = ObjectIdGetDatum(typioparam);
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].value = Int32GetDatum(typmod);
+ fcinfo->args[2].isnull = false;
+
+ *result = (*func) (fcinfo);
+
+ /* Result value is garbage, and could be null, if an error was reported */
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ /* Otherwise, shouldn't get null result */
+ if (fcinfo->isnull)
+ elog(ERROR, "input function %p returned NULL", (void *) func);
+
+ return true;
+}
+
/*
* Call a previously-looked-up datatype output function.
*
diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c
index 6364b01282..beff018e18 100644
--- a/src/bin/pg_verifybackup/parse_manifest.c
+++ b/src/bin/pg_verifybackup/parse_manifest.c
@@ -88,14 +88,14 @@ typedef struct
char *manifest_checksum;
} JsonManifestParseState;
-static void json_manifest_object_start(void *state);
-static void json_manifest_object_end(void *state);
-static void json_manifest_array_start(void *state);
-static void json_manifest_array_end(void *state);
-static void json_manifest_object_field_start(void *state, char *fname,
- bool isnull);
-static void json_manifest_scalar(void *state, char *token,
- JsonTokenType tokentype);
+static JsonParseErrorType json_manifest_object_start(void *state);
+static JsonParseErrorType json_manifest_object_end(void *state);
+static JsonParseErrorType json_manifest_array_start(void *state);
+static JsonParseErrorType json_manifest_array_end(void *state);
+static JsonParseErrorType json_manifest_object_field_start(void *state, char *fname,
+ bool isnull);
+static JsonParseErrorType json_manifest_scalar(void *state, char *token,
+ JsonTokenType tokentype);
static void json_manifest_finalize_file(JsonManifestParseState *parse);
static void json_manifest_finalize_wal_range(JsonManifestParseState *parse);
static void verify_manifest_checksum(JsonManifestParseState *parse,
@@ -162,7 +162,7 @@ json_parse_manifest(JsonManifestParseContext *context, char *buffer,
* WAL range is also expected to be an object. If we're anywhere else in the
* document, it's an error.
*/
-static void
+static JsonParseErrorType
json_manifest_object_start(void *state)
{
JsonManifestParseState *parse = state;
@@ -191,6 +191,8 @@ json_manifest_object_start(void *state)
"unexpected object start");
break;
}
+
+ return JSON_SUCCESS;
}
/*
@@ -201,7 +203,7 @@ json_manifest_object_start(void *state)
* reach the end of an object representing a particular file or WAL range,
* we must call json_manifest_finalize_file() to save the associated details.
*/
-static void
+static JsonParseErrorType
json_manifest_object_end(void *state)
{
JsonManifestParseState *parse = state;
@@ -224,6 +226,8 @@ json_manifest_object_end(void *state)
"unexpected object end");
break;
}
+
+ return JSON_SUCCESS;
}
/*
@@ -233,7 +237,7 @@ json_manifest_object_end(void *state)
* should be an array. Similarly for the "WAL-Ranges" key. No other arrays
* are expected.
*/
-static void
+static JsonParseErrorType
json_manifest_array_start(void *state)
{
JsonManifestParseState *parse = state;
@@ -251,6 +255,8 @@ json_manifest_array_start(void *state)
"unexpected array start");
break;
}
+
+ return JSON_SUCCESS;
}
/*
@@ -258,7 +264,7 @@ json_manifest_array_start(void *state)
*
* The cases here are analogous to those in json_manifest_array_start.
*/
-static void
+static JsonParseErrorType
json_manifest_array_end(void *state)
{
JsonManifestParseState *parse = state;
@@ -274,12 +280,14 @@ json_manifest_array_end(void *state)
"unexpected array end");
break;
}
+
+ return JSON_SUCCESS;
}
/*
* Invoked at the start of each object field in the JSON document.
*/
-static void
+static JsonParseErrorType
json_manifest_object_field_start(void *state, char *fname, bool isnull)
{
JsonManifestParseState *parse = state;
@@ -367,6 +375,8 @@ json_manifest_object_field_start(void *state, char *fname, bool isnull)
"unexpected object field");
break;
}
+
+ return JSON_SUCCESS;
}
/*
@@ -384,7 +394,7 @@ json_manifest_object_field_start(void *state, char *fname, bool isnull)
* reach either the end of the object representing this file, or the end
* of the manifest, as the case may be.
*/
-static void
+static JsonParseErrorType
json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
{
JsonManifestParseState *parse = state;
@@ -448,6 +458,8 @@ json_manifest_scalar(void *state, char *token, JsonTokenType tokentype)
json_manifest_parse_failure(parse->context, "unexpected scalar");
break;
}
+
+ return JSON_SUCCESS;
}
/*
diff --git a/src/common/jsonapi.c b/src/common/jsonapi.c
index 873357aa02..83c286b89b 100644
--- a/src/common/jsonapi.c
+++ b/src/common/jsonapi.c
@@ -298,9 +298,9 @@ parse_scalar(JsonLexContext *lex, JsonSemAction *sem)
return result;
/* invoke the callback */
- (*sfunc) (sem->semstate, val, tok);
+ result = (*sfunc) (sem->semstate, val, tok);
- return JSON_SUCCESS;
+ return result;
}
static JsonParseErrorType
@@ -335,7 +335,11 @@ parse_object_field(JsonLexContext *lex, JsonSemAction *sem)
isnull = tok == JSON_TOKEN_NULL;
if (ostart != NULL)
- (*ostart) (sem->semstate, fname, isnull);
+ {
+ result = (*ostart) (sem->semstate, fname, isnull);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
switch (tok)
{
@@ -352,7 +356,12 @@ parse_object_field(JsonLexContext *lex, JsonSemAction *sem)
return result;
if (oend != NULL)
- (*oend) (sem->semstate, fname, isnull);
+ {
+ result = (*oend) (sem->semstate, fname, isnull);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
+
return JSON_SUCCESS;
}
@@ -373,7 +382,11 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem)
#endif
if (ostart != NULL)
- (*ostart) (sem->semstate);
+ {
+ result = (*ostart) (sem->semstate);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
/*
* Data inside an object is at a higher nesting level than the object
@@ -417,7 +430,11 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem)
lex->lex_level--;
if (oend != NULL)
- (*oend) (sem->semstate);
+ {
+ result = (*oend) (sem->semstate);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
return JSON_SUCCESS;
}
@@ -429,13 +446,16 @@ parse_array_element(JsonLexContext *lex, JsonSemAction *sem)
json_aelem_action aend = sem->array_element_end;
JsonTokenType tok = lex_peek(lex);
JsonParseErrorType result;
-
bool isnull;
isnull = tok == JSON_TOKEN_NULL;
if (astart != NULL)
- (*astart) (sem->semstate, isnull);
+ {
+ result = (*astart) (sem->semstate, isnull);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
/* an array element is any object, array or scalar */
switch (tok)
@@ -454,7 +474,11 @@ parse_array_element(JsonLexContext *lex, JsonSemAction *sem)
return result;
if (aend != NULL)
- (*aend) (sem->semstate, isnull);
+ {
+ result = (*aend) (sem->semstate, isnull);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
return JSON_SUCCESS;
}
@@ -475,7 +499,11 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem)
#endif
if (astart != NULL)
- (*astart) (sem->semstate);
+ {
+ result = (*astart) (sem->semstate);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
/*
* Data inside an array is at a higher nesting level than the array
@@ -508,7 +536,11 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem)
lex->lex_level--;
if (aend != NULL)
- (*aend) (sem->semstate);
+ {
+ result = (*aend) (sem->semstate);
+ if (result != JSON_SUCCESS)
+ return result;
+ }
return JSON_SUCCESS;
}
@@ -1139,6 +1171,9 @@ json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
return _("Unicode high surrogate must not follow a high surrogate.");
case JSON_UNICODE_LOW_SURROGATE:
return _("Unicode low surrogate must follow a high surrogate.");
+ case JSON_SEM_ACTION_FAILED:
+ /* fall through to the error code after switch */
+ break;
}
/*
diff --git a/src/include/common/jsonapi.h b/src/include/common/jsonapi.h
index 8d31630e5c..4590ff2476 100644
--- a/src/include/common/jsonapi.h
+++ b/src/include/common/jsonapi.h
@@ -52,7 +52,8 @@ typedef enum
JSON_UNICODE_ESCAPE_FORMAT,
JSON_UNICODE_HIGH_ESCAPE,
JSON_UNICODE_HIGH_SURROGATE,
- JSON_UNICODE_LOW_SURROGATE
+ JSON_UNICODE_LOW_SURROGATE,
+ JSON_SEM_ACTION_FAILED /* error should already be reported */
} JsonParseErrorType;
@@ -84,14 +85,15 @@ typedef struct JsonLexContext
StringInfo strval;
} JsonLexContext;
-typedef void (*json_struct_action) (void *state);
-typedef void (*json_ofield_action) (void *state, char *fname, bool isnull);
-typedef void (*json_aelem_action) (void *state, bool isnull);
-typedef void (*json_scalar_action) (void *state, char *token, JsonTokenType tokentype);
+typedef JsonParseErrorType (*json_struct_action) (void *state);
+typedef JsonParseErrorType (*json_ofield_action) (void *state, char *fname, bool isnull);
+typedef JsonParseErrorType (*json_aelem_action) (void *state, bool isnull);
+typedef JsonParseErrorType (*json_scalar_action) (void *state, char *token, JsonTokenType tokentype);
/*
* Semantic Action structure for use in parsing json.
+ *
* Any of these actions can be NULL, in which case nothing is done at that
* point, Likewise, semstate can be NULL. Using an all-NULL structure amounts
* to doing a pure parse with no side-effects, and is therefore exactly
@@ -100,6 +102,11 @@ typedef void (*json_scalar_action) (void *state, char *token, JsonTokenType toke
* The 'fname' and 'token' strings passed to these actions are palloc'd.
* They are not free'd or used further by the parser, so the action function
* is free to do what it wishes with them.
+ *
+ * All action functions return JsonParseErrorType. If the result isn't
+ * JSON_SUCCESS, the parse is abandoned and that error code is returned.
+ * If it is JSON_SEM_ACTION_FAILED, the action function is responsible
+ * for having reported the error in some appropriate way.
*/
typedef struct JsonSemAction
{
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index b7832d0aa2..972afe3aff 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -704,6 +704,10 @@ extern bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str,
Oid typioparam, int32 typmod,
fmNodePtr escontext,
Datum *result);
+extern bool DirectInputFunctionCallSafe(PGFunction func, char *str,
+ Oid typioparam, int32 typmod,
+ fmNodePtr escontext,
+ Datum *result);
extern Datum OidInputFunctionCall(Oid functionId, char *str,
Oid typioparam, int32 typmod);
extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val);
diff --git a/src/include/utils/jsonfuncs.h b/src/include/utils/jsonfuncs.h
index 865b2ff7c1..7fad0269f6 100644
--- a/src/include/utils/jsonfuncs.h
+++ b/src/include/utils/jsonfuncs.h
@@ -39,11 +39,16 @@ typedef text *(*JsonTransformStringValuesAction) (void *state, char *elem_value,
/* build a JsonLexContext from a text datum */
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);
+/* try to parse json, and errsave(escontext) on failure */
+extern bool pg_parse_json_or_errsave(JsonLexContext *lex, JsonSemAction *sem,
+ struct Node *escontext);
-/* report an error during json lexing or parsing */
-extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
+#define pg_parse_json_or_ereport(lex, sem) \
+ (void) pg_parse_json_or_errsave(lex, sem, NULL)
+
+/* save an error during json lexing or parsing */
+extern void json_errsave_error(JsonParseErrorType error, JsonLexContext *lex,
+ struct Node *escontext);
extern uint32 parse_jsonb_index_flags(Jsonb *jb);
extern void iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index cb181226e9..af96ce4180 100644
--- a/src/test/regress/expected/json.out
+++ b/src/test/regress/expected/json.out
@@ -320,6 +320,25 @@ LINE 1: SELECT '{
DETAIL: Expected JSON value, but found "}".
CONTEXT: JSON data, line 4: ...yveryveryveryveryveryveryveryverylongfieldname":}
-- ERROR missing value for last field
+-- test non-error-throwing input
+select pg_input_is_valid('{"a":true}', 'json');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+select pg_input_is_valid('{"a":true', 'json');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('{"a":true', 'json');
+ pg_input_error_message
+------------------------------------
+ invalid input syntax for type json
+(1 row)
+
--constructors
-- array_to_json
SELECT array_to_json(array(select 1 as a));
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index b2b3677482..be85676b5b 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -310,6 +310,31 @@ LINE 1: SELECT '{
DETAIL: Expected JSON value, but found "}".
CONTEXT: JSON data, line 4: ...yveryveryveryveryveryveryveryverylongfieldname":}
-- ERROR missing value for last field
+-- test non-error-throwing input
+select pg_input_is_valid('{"a":true}', 'jsonb');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+select pg_input_is_valid('{"a":true', 'jsonb');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('{"a":true', 'jsonb');
+ pg_input_error_message
+------------------------------------
+ invalid input syntax for type json
+(1 row)
+
+select pg_input_error_message('{"a":1e1000000}', 'jsonb');
+ pg_input_error_message
+--------------------------------
+ value overflows numeric format
+(1 row)
+
-- make sure jsonb is passed through json generators without being escaped
SELECT array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
array_to_json
diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql
index 589e0cea36..21534ed959 100644
--- a/src/test/regress/sql/json.sql
+++ b/src/test/regress/sql/json.sql
@@ -81,6 +81,11 @@ SELECT '{
"averyveryveryveryveryveryveryveryveryverylongfieldname":}'::json;
-- ERROR missing value for last field
+-- test non-error-throwing input
+select pg_input_is_valid('{"a":true}', 'json');
+select pg_input_is_valid('{"a":true', 'json');
+select pg_input_error_message('{"a":true', 'json');
+
--constructors
-- array_to_json
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 8d25966267..bc44ad1518 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -86,6 +86,12 @@ SELECT '{
"averyveryveryveryveryveryveryveryveryverylongfieldname":}'::jsonb;
-- ERROR missing value for last field
+-- test non-error-throwing input
+select pg_input_is_valid('{"a":true}', 'jsonb');
+select pg_input_is_valid('{"a":true', 'jsonb');
+select pg_input_error_message('{"a":true', 'jsonb');
+select pg_input_error_message('{"a":1e1000000}', 'jsonb');
+
-- make sure jsonb is passed through json generators without being escaped
SELECT array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
pgsql-hackers by date: