mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
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
This commit is contained in:
@ -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);
|
||||
|
Reference in New Issue
Block a user