aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/jsonfuncs.c
diff options
context:
space:
mode:
authorAmit Langote <amitlan@postgresql.org>2024-06-28 21:58:13 +0900
committerAmit Langote <amitlan@postgresql.org>2024-06-28 21:58:13 +0900
commit716bd12d22c53d1943d41309f2dd061ec601dd5e (patch)
tree93a8030f6b2c690795c0d8bdb405206f476ee7b7 /src/backend/utils/adt/jsonfuncs.c
parentc2d93c3802b205d135d1ae1d7ac167d74e08a274 (diff)
downloadpostgresql-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.c48
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);