From a2e6d6442d982125ecc9121bc8f37d738bc7b15a Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Thu, 18 Jan 2024 16:16:56 +0900 Subject: [PATCH v34 2/8] Add json_populate_type() with support for soft error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new function is intended to extract a value from a given jsonb value passed in as a Datum and return as a Datum of the specified type. Its implementation uses the existing populate_record_field(), which has been modified here to add soft handling of errors. The changes here are only intended to suppress errors in the functions in jsonfuncs.c, but not those in any external functions that the functions in jsonfuncs.c may in turn call, such as those in arrayfuncs.c, etc. The assumption is that the various checks in populate_* functions should ensure that only values that are structurally valid get passed to the external functions. Reviewed-by: Álvaro Herrera Reviewed-by: Andres Freund Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com --- src/backend/utils/adt/jsonfuncs.c | 373 ++++++++++++++++++++++++------ src/include/utils/jsonfuncs.h | 6 + 2 files changed, 302 insertions(+), 77 deletions(-) diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index caaafb72c0..87599530d1 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -265,6 +265,7 @@ typedef struct PopulateArrayContext int *dims; /* dimensions */ int *sizes; /* current dimension counters */ int ndims; /* number of dimensions */ + Node *escontext; /* For soft-error handling */ } PopulateArrayContext; /* state for populate_array_json() */ @@ -389,7 +390,8 @@ 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); +static HTAB *get_json_object_as_hash(char *json, int len, const char *funcname, + Node *escontext); /* semantic actions for populate_array_json */ static JsonParseErrorType populate_array_object_start(void *_state); @@ -431,37 +433,42 @@ static Datum populate_record_worker(FunctionCallInfo fcinfo, const char *funcnam /* helper functions for populate_record[set] */ static HeapTupleHeader populate_record(TupleDesc tupdesc, RecordIOData **record_p, HeapTupleHeader defaultval, MemoryContext mcxt, - JsObject *obj); + JsObject *obj, Node *escontext); static void get_record_type_from_argument(FunctionCallInfo fcinfo, const char *funcname, PopulateRecordCache *cache); static void get_record_type_from_query(FunctionCallInfo fcinfo, const char *funcname, PopulateRecordCache *cache); -static void JsValueToJsObject(JsValue *jsv, JsObject *jso); +static bool JsValueToJsObject(JsValue *jsv, JsObject *jso, Node *escontext); static Datum populate_composite(CompositeIOData *io, Oid typid, const char *colname, MemoryContext mcxt, - HeapTupleHeader defaultval, JsValue *jsv, bool isnull); -static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv); + HeapTupleHeader defaultval, JsValue *jsv, bool *isnull, + Node *escontext); +static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv, + bool *isnull, Node *escontext); static void prepare_column_cache(ColumnIOData *column, Oid typid, int32 typmod, MemoryContext mcxt, bool need_scalar); static Datum populate_record_field(ColumnIOData *col, Oid typid, int32 typmod, const char *colname, MemoryContext mcxt, Datum defaultval, - JsValue *jsv, bool *isnull); + JsValue *jsv, bool *isnull, Node *escontext); static RecordIOData *allocate_record_info(MemoryContext mcxt, int ncolumns); static bool JsObjectGetField(JsObject *obj, char *field, JsValue *jsv); static void populate_recordset_record(PopulateRecordsetState *state, JsObject *obj); -static void populate_array_json(PopulateArrayContext *ctx, char *json, int len); -static void populate_array_dim_jsonb(PopulateArrayContext *ctx, JsonbValue *jbv, +static bool populate_array_json(PopulateArrayContext *ctx, char *json, int len); +static bool populate_array_dim_jsonb(PopulateArrayContext *ctx, JsonbValue *jbv, int ndim); static void populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim); -static void populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims); -static void populate_array_check_dimension(PopulateArrayContext *ctx, int ndim); -static void populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv); +static bool populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims); +static bool populate_array_check_dimension(PopulateArrayContext *ctx, int ndim); +static bool populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv); static Datum populate_array(ArrayIOData *aio, const char *colname, - MemoryContext mcxt, JsValue *jsv); + MemoryContext mcxt, JsValue *jsv, + bool *isnull, + Node *escontext); static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname, - MemoryContext mcxt, JsValue *jsv, bool isnull); + MemoryContext mcxt, JsValue *jsv, bool isnull, + Node *escontext); /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */ static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2, @@ -2484,14 +2491,15 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim) if (ndim <= 0) { if (ctx->colname) - ereport(ERROR, + errsave(ctx->escontext, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("expected JSON array"), errhint("See the value of key \"%s\".", ctx->colname))); else - ereport(ERROR, + errsave(ctx->escontext, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("expected JSON array"))); + return; } else { @@ -2506,22 +2514,28 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim) appendStringInfo(&indices, "[%d]", ctx->sizes[i]); if (ctx->colname) - ereport(ERROR, + errsave(ctx->escontext, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("expected JSON array"), errhint("See the array element %s of key \"%s\".", indices.data, ctx->colname))); else - ereport(ERROR, + errsave(ctx->escontext, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("expected JSON array"), errhint("See the array element %s.", indices.data))); + return; } } -/* set the number of dimensions of the populated array when it becomes known */ -static void +/* + * Validate and set ndims for populating an array with some + * populate_array_*() function. + * + * Returns false if the input (ndims) is erroneous. + */ +static bool populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims) { int i; @@ -2529,7 +2543,12 @@ populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims) Assert(ctx->ndims <= 0); if (ndims <= 0) + { populate_array_report_expected_array(ctx, ndims); + /* Getting here means the error was reported softly. */ + Assert(SOFT_ERROR_OCCURRED(ctx->escontext)); + return false; + } ctx->ndims = ndims; ctx->dims = palloc(sizeof(int) * ndims); @@ -2537,10 +2556,16 @@ populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims) for (i = 0; i < ndims; i++) ctx->dims[i] = -1; /* dimensions are unknown yet */ + + return true; } -/* check the populated subarray dimension */ -static void +/* + * Check the populated subarray dimension + * + * Returns false if the input (ndims) is erroneous. + */ +static bool populate_array_check_dimension(PopulateArrayContext *ctx, int ndim) { int dim = ctx->sizes[ndim]; /* current dimension counter */ @@ -2548,21 +2573,31 @@ populate_array_check_dimension(PopulateArrayContext *ctx, int ndim) if (ctx->dims[ndim] == -1) ctx->dims[ndim] = dim; /* assign dimension if not yet known */ else if (ctx->dims[ndim] != dim) - ereport(ERROR, + errsave(ctx->escontext, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed JSON array"), errdetail("Multidimensional arrays must have " "sub-arrays with matching dimensions."))); + /* Nothing to do on an error. */ + if (SOFT_ERROR_OCCURRED(ctx->escontext)) + return false; + /* reset the current array dimension size counter */ ctx->sizes[ndim] = 0; /* increment the parent dimension counter if it is a nested sub-array */ if (ndim > 0) ctx->sizes[ndim - 1]++; + + return true; } -static void +/* + * Returns true if the array element value was successfully extracted from jsv + * and added to ctx->astate. False if an error occurred when doing so. + */ +static bool populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv) { Datum element; @@ -2573,13 +2608,18 @@ populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv) ctx->aio->element_type, ctx->aio->element_typmod, NULL, ctx->mcxt, PointerGetDatum(NULL), - jsv, &element_isnull); + jsv, &element_isnull, ctx->escontext); + /* Nothing to do on an error. */ + if (SOFT_ERROR_OCCURRED(ctx->escontext)) + return false; accumArrayResult(ctx->astate, element, element_isnull, ctx->aio->element_type, ctx->acxt); Assert(ndim > 0); ctx->sizes[ndim - 1]++; /* increment current dimension counter */ + + return true; } /* json object start handler for populate_array_json() */ @@ -2590,9 +2630,17 @@ populate_array_object_start(void *_state) int ndim = state->lex->lex_level; if (state->ctx->ndims <= 0) - populate_array_assign_ndims(state->ctx, ndim); + { + if (!populate_array_assign_ndims(state->ctx, ndim)) + return JSON_SEM_ACTION_FAILED; + } else if (ndim < state->ctx->ndims) + { populate_array_report_expected_array(state->ctx, ndim); + /* Getting here means the error was reported softly. */ + Assert(SOFT_ERROR_OCCURRED(state->ctx->escontext)); + return JSON_SEM_ACTION_FAILED; + } return JSON_SUCCESS; } @@ -2606,10 +2654,17 @@ populate_array_array_end(void *_state) int ndim = state->lex->lex_level; if (ctx->ndims <= 0) - populate_array_assign_ndims(ctx, ndim + 1); + { + if (!populate_array_assign_ndims(ctx, ndim + 1)) + return JSON_SEM_ACTION_FAILED; + } if (ndim < ctx->ndims) - populate_array_check_dimension(ctx, ndim); + { + /* Report if an error occurred. */ + if (!populate_array_check_dimension(ctx, ndim)) + return JSON_SEM_ACTION_FAILED; + } return JSON_SUCCESS; } @@ -2667,7 +2722,9 @@ populate_array_element_end(void *_state, bool isnull) state->element_start) * sizeof(char); } - populate_array_element(ctx, ndim, &jsv); + /* Report if an error occurred. */ + if (!populate_array_element(ctx, ndim, &jsv)) + return JSON_SEM_ACTION_FAILED; } return JSON_SUCCESS; @@ -2682,9 +2739,17 @@ populate_array_scalar(void *_state, char *token, JsonTokenType tokentype) int ndim = state->lex->lex_level; if (ctx->ndims <= 0) - populate_array_assign_ndims(ctx, ndim); + { + if (!populate_array_assign_ndims(ctx, ndim)) + return JSON_SEM_ACTION_FAILED; + } else if (ndim < ctx->ndims) + { populate_array_report_expected_array(ctx, ndim); + /* Getting here means the error was reported softly. */ + Assert(SOFT_ERROR_OCCURRED(ctx->escontext)); + return JSON_SEM_ACTION_FAILED; + } if (ndim == ctx->ndims) { @@ -2697,8 +2762,12 @@ populate_array_scalar(void *_state, char *token, JsonTokenType tokentype) return JSON_SUCCESS; } -/* parse a json array and populate array */ -static void +/* + * Parse a json array and populate array + * + * Returns false if an error occurs when parsing. + */ +static bool populate_array_json(PopulateArrayContext *ctx, char *json, int len) { PopulateArrayState state; @@ -2716,19 +2785,25 @@ 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_or_ereport(state.lex, &sem); - - /* number of dimensions should be already known */ - Assert(ctx->ndims > 0 && ctx->dims); + if (pg_parse_json_or_errsave(state.lex, &sem, ctx->escontext)) + { + /* number of dimensions should be already known */ + Assert(ctx->ndims > 0 && ctx->dims); + } freeJsonLexContext(state.lex); + + return !SOFT_ERROR_OCCURRED(ctx->escontext); } /* * populate_array_dim_jsonb() -- Iterate recursively through jsonb sub-array * elements and accumulate result using given ArrayBuildState. + * + * Returns false if we return partway through because of an error in a + * subroutine. */ -static void +static bool populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */ JsonbValue *jbv, /* jsonb sub-array */ int ndim) /* current dimension */ @@ -2741,10 +2816,14 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */ check_stack_depth(); - if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc)) + if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc) || + JsonContainerIsScalar(jbc)) + { populate_array_report_expected_array(ctx, ndim - 1); - - Assert(!JsonContainerIsScalar(jbc)); + /* Getting here means the error was reported softly. */ + Assert(SOFT_ERROR_OCCURRED(ctx->escontext)); + return false; + } it = JsonbIteratorInit(jbc); @@ -2763,7 +2842,10 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */ (tok == WJB_ELEM && (val.type != jbvBinary || !JsonContainerIsArray(val.val.binary.data))))) - populate_array_assign_ndims(ctx, ndim); + { + if (!populate_array_assign_ndims(ctx, ndim)) + return false; + } jsv.is_json = false; jsv.val.jsonb = &val; @@ -2776,16 +2858,21 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */ * it is not the innermost dimension. */ if (ctx->ndims > 0 && ndim >= ctx->ndims) - populate_array_element(ctx, ndim, &jsv); + { + if (!populate_array_element(ctx, ndim, &jsv)) + return false; + } else { /* populate child sub-array */ - populate_array_dim_jsonb(ctx, &val, ndim + 1); + if (!populate_array_dim_jsonb(ctx, &val, ndim + 1)) + return false; /* number of dimensions should be already known */ Assert(ctx->ndims > 0 && ctx->dims); - populate_array_check_dimension(ctx, ndim); + if (!populate_array_check_dimension(ctx, ndim)) + return false; } tok = JsonbIteratorNext(&it, &val, true); @@ -2796,14 +2883,22 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */ /* free iterator, iterating until WJB_DONE */ tok = JsonbIteratorNext(&it, &val, true); Assert(tok == WJB_DONE && !it); + + return true; } -/* recursively populate an array from json/jsonb */ +/* + * Recursively populate an array from json/jsonb + * + * *isnull is set to true if an error is reported during parsing. + */ static Datum populate_array(ArrayIOData *aio, const char *colname, MemoryContext mcxt, - JsValue *jsv) + JsValue *jsv, + bool *isnull, + Node *escontext) { PopulateArrayContext ctx; Datum result; @@ -2818,14 +2913,27 @@ populate_array(ArrayIOData *aio, ctx.ndims = 0; /* unknown yet */ ctx.dims = NULL; ctx.sizes = NULL; + ctx.escontext = escontext; if (jsv->is_json) - populate_array_json(&ctx, jsv->val.json.str, - jsv->val.json.len >= 0 ? jsv->val.json.len - : strlen(jsv->val.json.str)); + { + /* Return null if an error was found. */ + if (!populate_array_json(&ctx, jsv->val.json.str, + jsv->val.json.len >= 0 ? jsv->val.json.len + : strlen(jsv->val.json.str))) + { + *isnull = true; + return (Datum) 0; + } + } else { - populate_array_dim_jsonb(&ctx, jsv->val.jsonb, 1); + /* Return null if an error was found. */ + if (!populate_array_dim_jsonb(&ctx, jsv->val.jsonb, 1)) + { + *isnull = true; + return (Datum) 0; + } ctx.dims[0] = ctx.sizes[0]; } @@ -2843,11 +2951,16 @@ populate_array(ArrayIOData *aio, pfree(ctx.sizes); pfree(lbs); + *isnull = false; return result; } -static void -JsValueToJsObject(JsValue *jsv, JsObject *jso) +/* + * Returns false if an error occurs, provided escontext points to an + * ErrorSaveContext. + */ +static bool +JsValueToJsObject(JsValue *jsv, JsObject *jso, Node *escontext) { jso->is_json = jsv->is_json; @@ -2859,7 +2972,9 @@ JsValueToJsObject(JsValue *jsv, JsObject *jso) jsv->val.json.len >= 0 ? jsv->val.json.len : strlen(jsv->val.json.str), - "populate_composite"); + "populate_composite", + escontext); + Assert(jso->val.json_hash != NULL || SOFT_ERROR_OCCURRED(escontext)); } else { @@ -2877,7 +2992,7 @@ JsValueToJsObject(JsValue *jsv, JsObject *jso) is_scalar = IsAJsonbScalar(jbv) || (jbv->type == jbvBinary && JsonContainerIsScalar(jbv->val.binary.data)); - ereport(ERROR, + errsave(escontext, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), is_scalar ? errmsg("cannot call %s on a scalar", @@ -2886,6 +3001,8 @@ JsValueToJsObject(JsValue *jsv, JsObject *jso) "populate_composite"))); } } + + return !SOFT_ERROR_OCCURRED(escontext); } /* acquire or update cached tuple descriptor for a composite type */ @@ -2912,7 +3029,12 @@ update_cached_tupdesc(CompositeIOData *io, MemoryContext mcxt) } } -/* recursively populate a composite (row type) value from json/jsonb */ +/* + * Recursively populate a composite (row type) value from json/jsonb + * + * Returns null if an error occurs in a subroutine, provided escontext points + * to an ErrorSaveContext. + */ static Datum populate_composite(CompositeIOData *io, Oid typid, @@ -2920,14 +3042,15 @@ populate_composite(CompositeIOData *io, MemoryContext mcxt, HeapTupleHeader defaultval, JsValue *jsv, - bool isnull) + bool *isnull, + Node *escontext) { Datum result; /* acquire/update cached tuple descriptor */ update_cached_tupdesc(io, mcxt); - if (isnull) + if (*isnull) result = (Datum) 0; else { @@ -2935,11 +3058,21 @@ populate_composite(CompositeIOData *io, JsObject jso; /* prepare input value */ - JsValueToJsObject(jsv, &jso); + if (!JsValueToJsObject(jsv, &jso, escontext)) + { + *isnull = true; + return (Datum) 0; + } /* populate resulting record tuple */ tuple = populate_record(io->tupdesc, &io->record_io, - defaultval, mcxt, &jso); + defaultval, mcxt, &jso, escontext); + + if (SOFT_ERROR_OCCURRED(escontext)) + { + *isnull = true; + return (Datum) 0; + } result = HeapTupleHeaderGetDatum(tuple); JsObjectFree(&jso); @@ -2951,14 +3084,20 @@ populate_composite(CompositeIOData *io, * now, we can tell by comparing typid to base_typid.) */ if (typid != io->base_typid && typid != RECORDOID) - domain_check(result, isnull, typid, &io->domain_info, mcxt); + domain_check(result, *isnull, typid, &io->domain_info, mcxt); return result; } -/* populate non-null scalar value from json/jsonb value */ +/* + * Populate non-null scalar value from json/jsonb value. + * + * Returns null if an error occurs during the call to type input function, + * provided escontext is valid. + */ static Datum -populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv) +populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv, + bool *isnull, Node *escontext) { Datum res; char *str = NULL; @@ -3029,7 +3168,12 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv) elog(ERROR, "unrecognized jsonb type: %d", (int) jbv->type); } - res = InputFunctionCall(&io->typiofunc, str, io->typioparam, typmod); + if (!InputFunctionCallSafe(&io->typiofunc, str, io->typioparam, typmod, + escontext, &res)) + { + res = (Datum) 0; + *isnull = true; + } /* free temporary buffer */ if (str != json) @@ -3044,7 +3188,8 @@ populate_domain(DomainIOData *io, const char *colname, MemoryContext mcxt, JsValue *jsv, - bool isnull) + bool isnull, + Node *escontext) { Datum res; @@ -3055,8 +3200,8 @@ populate_domain(DomainIOData *io, res = populate_record_field(io->base_io, io->base_typid, io->base_typmod, colname, mcxt, PointerGetDatum(NULL), - jsv, &isnull); - Assert(!isnull); + jsv, &isnull, escontext); + Assert(!isnull || SOFT_ERROR_OCCURRED(escontext)); } domain_check(res, isnull, typid, &io->domain_info, mcxt); @@ -3160,7 +3305,8 @@ populate_record_field(ColumnIOData *col, MemoryContext mcxt, Datum defaultval, JsValue *jsv, - bool *isnull) + bool *isnull, + Node *escontext) { TypeCat typcat; @@ -3193,10 +3339,12 @@ populate_record_field(ColumnIOData *col, switch (typcat) { case TYPECAT_SCALAR: - return populate_scalar(&col->scalar_io, typid, typmod, jsv); + return populate_scalar(&col->scalar_io, typid, typmod, jsv, + isnull, escontext); case TYPECAT_ARRAY: - return populate_array(&col->io.array, colname, mcxt, jsv); + return populate_array(&col->io.array, colname, mcxt, jsv, + isnull, escontext); case TYPECAT_COMPOSITE: case TYPECAT_COMPOSITE_DOMAIN: @@ -3205,11 +3353,12 @@ populate_record_field(ColumnIOData *col, DatumGetPointer(defaultval) ? DatumGetHeapTupleHeader(defaultval) : NULL, - jsv, *isnull); + jsv, isnull, + escontext); case TYPECAT_DOMAIN: return populate_domain(&col->io.domain, typid, colname, mcxt, - jsv, *isnull); + jsv, *isnull, escontext); default: elog(ERROR, "unrecognized type category '%c'", typcat); @@ -3217,6 +3366,62 @@ populate_record_field(ColumnIOData *col, } } +/* + * Populate and return the value of specified type from a given json/jsonb + * value 'json_val'. 'cache' is caller-specified pointer to save the + * ColumnIOData that will be initialized on the 1st call and then reused + * during any subsequent calls. 'mcxt' gives the memory context to allocate + * the ColumnIOData and any other subsidiary memory in. 'escontext', + * if not NULL, tells that any errors that occur should be handled softly. + */ +Datum +json_populate_type(Datum json_val, Oid json_type, + Oid typid, int32 typmod, + void **cache, MemoryContext mcxt, + bool *isnull, + Node *escontext) +{ + JsValue jsv = {0}; + JsonbValue jbv; + + jsv.is_json = json_type == JSONOID; + + if (*isnull) + { + if (jsv.is_json) + jsv.val.json.str = NULL; + else + jsv.val.jsonb = NULL; + } + else if (jsv.is_json) + { + text *json = DatumGetTextPP(json_val); + + jsv.val.json.str = VARDATA_ANY(json); + jsv.val.json.len = VARSIZE_ANY_EXHDR(json); + jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in + * populate_composite() */ + } + else + { + Jsonb *jsonb = DatumGetJsonbP(json_val); + + jsv.val.jsonb = &jbv; + + /* fill binary jsonb value pointing to jb */ + jbv.type = jbvBinary; + jbv.val.binary.data = &jsonb->root; + jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ; + } + + if (*cache == NULL) + *cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData)); + + return populate_record_field(*cache, typid, typmod, NULL, mcxt, + PointerGetDatum(NULL), &jsv, isnull, + escontext); +} + static RecordIOData * allocate_record_info(MemoryContext mcxt, int ncolumns) { @@ -3266,7 +3471,8 @@ populate_record(TupleDesc tupdesc, RecordIOData **record_p, HeapTupleHeader defaultval, MemoryContext mcxt, - JsObject *obj) + JsObject *obj, + Node *escontext) { RecordIOData *record = *record_p; Datum *values; @@ -3358,7 +3564,8 @@ populate_record(TupleDesc tupdesc, mcxt, nulls[i] ? (Datum) 0 : values[i], &field, - &nulls[i]); + &nulls[i], + escontext); } res = heap_form_tuple(tupdesc, values, nulls); @@ -3445,6 +3652,7 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname, JsValue jsv = {0}; HeapTupleHeader rec; Datum rettuple; + bool isnull; JsonbValue jbv; MemoryContext fnmcxt = fcinfo->flinfo->fn_mcxt; PopulateRecordCache *cache = fcinfo->flinfo->fn_extra; @@ -3531,8 +3739,11 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname, jbv.val.binary.len = VARSIZE(jb) - VARHDRSZ; } + isnull = false; rettuple = populate_composite(&cache->c.io.composite, cache->argtype, - NULL, fnmcxt, rec, &jsv, false); + NULL, fnmcxt, rec, &jsv, &isnull, + NULL); + Assert(!isnull); PG_RETURN_DATUM(rettuple); } @@ -3540,10 +3751,13 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname, /* * get_json_object_as_hash * - * decompose a json object into a hash table. + * Decomposes a json object into a hash table. + * + * Returns the hash table if the json is parsed successfully, NULL otherwise. */ static HTAB * -get_json_object_as_hash(char *json, int len, const char *funcname) +get_json_object_as_hash(char *json, int len, const char *funcname, + Node *escontext) { HASHCTL ctl; HTAB *tab; @@ -3572,7 +3786,11 @@ 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_or_ereport(state->lex, sem); + if (!pg_parse_json_or_errsave(state->lex, sem, escontext)) + { + hash_destroy(state->hash); + tab = NULL; + } freeJsonLexContext(state->lex); @@ -3743,7 +3961,8 @@ populate_recordset_record(PopulateRecordsetState *state, JsObject *obj) &cache->c.io.composite.record_io, state->rec, cache->fn_mcxt, - obj); + obj, + NULL); /* if it's domain over composite, check domain constraints */ if (cache->c.typcat == TYPECAT_COMPOSITE_DOMAIN) diff --git a/src/include/utils/jsonfuncs.h b/src/include/utils/jsonfuncs.h index 31c1ae4767..9bb9eb73b4 100644 --- a/src/include/utils/jsonfuncs.h +++ b/src/include/utils/jsonfuncs.h @@ -15,6 +15,7 @@ #define JSONFUNCS_H #include "common/jsonapi.h" +#include "nodes/nodes.h" #include "utils/jsonb.h" /* @@ -87,5 +88,10 @@ extern Datum datum_to_json(Datum val, JsonTypeCategory tcategory, extern Datum datum_to_jsonb(Datum val, JsonTypeCategory tcategory, Oid outfuncoid); extern Datum jsonb_from_text(text *js, bool unique_keys); +extern Datum json_populate_type(Datum json_val, Oid json_type, + Oid typid, int32 typmod, + void **cache, MemoryContext mcxt, + bool *isnull, + Node *escontext); #endif -- 2.35.3