diff options
author | Amit Langote <amitlan@postgresql.org> | 2024-06-28 21:58:13 +0900 |
---|---|---|
committer | Amit Langote <amitlan@postgresql.org> | 2024-06-28 21:58:13 +0900 |
commit | 716bd12d22c53d1943d41309f2dd061ec601dd5e (patch) | |
tree | 93a8030f6b2c690795c0d8bdb405206f476ee7b7 /src/backend/utils/adt/jsonfuncs.c | |
parent | c2d93c3802b205d135d1ae1d7ac167d74e08a274 (diff) | |
download | postgresql-716bd12d22c53d1943d41309f2dd061ec601dd5e.tar.gz postgresql-716bd12d22c53d1943d41309f2dd061ec601dd5e.zip |
SQL/JSON: Always coerce JsonExpr result at runtime
Instead of looking up casts at parse time for converting the result
of JsonPath* query functions to the specified or the default
RETURNING type, always perform the conversion at runtime using either
the target type's input function or the function
json_populate_type().
There are two motivations for this change:
1. json_populate_type() coerces to types with typmod such that any
string values that exceed length limit cause an error instead of
silent truncation, which is necessary to be standard-conforming.
2. It was possible to end up with a cast expression that doesn't
support soft handling of errors causing bugs in the of handling
ON ERROR clause.
JsonExpr.coercion_expr which would store the cast expression is no
longer necessary, so remove.
Bump catversion because stored rules change because of the above
removal.
Reported-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: Jian He <jian.universality@gmail.com>
Discussion: Discussion: https://postgr.es/m/202405271326.5a5rprki64aw%40alvherre.pgsql
Diffstat (limited to 'src/backend/utils/adt/jsonfuncs.c')
-rw-r--r-- | src/backend/utils/adt/jsonfuncs.c | 48 |
1 files changed, 33 insertions, 15 deletions
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index ab5aa0ccb8a..48c3f881403 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -447,12 +447,13 @@ static Datum populate_composite(CompositeIOData *io, Oid typid, HeapTupleHeader defaultval, JsValue *jsv, bool *isnull, Node *escontext); static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv, - bool *isnull, Node *escontext); + bool *isnull, Node *escontext, bool omit_quotes); 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, Node *escontext); + JsValue *jsv, bool *isnull, Node *escontext, + bool omit_quotes); 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); @@ -2622,7 +2623,8 @@ 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, ctx->escontext); + jsv, &element_isnull, ctx->escontext, + false); /* Nothing to do on an error. */ if (SOFT_ERROR_OCCURRED(ctx->escontext)) return false; @@ -3119,7 +3121,7 @@ populate_composite(CompositeIOData *io, */ static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv, - bool *isnull, Node *escontext) + bool *isnull, Node *escontext, bool omit_quotes) { Datum res; char *str = NULL; @@ -3162,7 +3164,9 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv, { JsonbValue *jbv = jsv->val.jsonb; - if (typid == JSONBOID) + if (jbv->type == jbvString && omit_quotes) + str = pnstrdup(jbv->val.string.val, jbv->val.string.len); + else if (typid == JSONBOID) { Jsonb *jsonb = JsonbValueToJsonb(jbv); /* directly use jsonb */ @@ -3225,7 +3229,7 @@ populate_domain(DomainIOData *io, res = populate_record_field(io->base_io, io->base_typid, io->base_typmod, colname, mcxt, PointerGetDatum(NULL), - jsv, isnull, escontext); + jsv, isnull, escontext, false); Assert(!*isnull || SOFT_ERROR_OCCURRED(escontext)); } @@ -3338,7 +3342,7 @@ Datum json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod, void **cache, MemoryContext mcxt, - bool *isnull, + bool *isnull, bool omit_quotes, Node *escontext) { JsValue jsv = {0}; @@ -3368,10 +3372,22 @@ json_populate_type(Datum json_val, Oid json_type, 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 (omit_quotes) + { + char *str = JsonbUnquote(DatumGetJsonbP(json_val)); + + /* fill the quote-stripped string */ + jbv.type = jbvString; + jbv.val.string.len = strlen(str); + jbv.val.string.val = str; + } + else + { + /* 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) @@ -3379,7 +3395,7 @@ json_populate_type(Datum json_val, Oid json_type, return populate_record_field(*cache, typid, typmod, NULL, mcxt, PointerGetDatum(NULL), &jsv, isnull, - escontext); + escontext, omit_quotes); } /* recursively populate a record field or an array element from a json/jsonb value */ @@ -3392,7 +3408,8 @@ populate_record_field(ColumnIOData *col, Datum defaultval, JsValue *jsv, bool *isnull, - Node *escontext) + Node *escontext, + bool omit_scalar_quotes) { TypeCat typcat; @@ -3426,7 +3443,7 @@ populate_record_field(ColumnIOData *col, { case TYPECAT_SCALAR: return populate_scalar(&col->scalar_io, typid, typmod, jsv, - isnull, escontext); + isnull, escontext, omit_scalar_quotes); case TYPECAT_ARRAY: return populate_array(&col->io.array, colname, mcxt, jsv, @@ -3595,7 +3612,8 @@ populate_record(TupleDesc tupdesc, nulls[i] ? (Datum) 0 : values[i], &field, &nulls[i], - escontext); + escontext, + false); } res = heap_form_tuple(tupdesc, values, nulls); |