Make cast functions from jsonb error safe

This adjusts cast functions from jsonb to other types to support soft
errors.  This just involves some refactoring of the underlying helper
functions to use ereturn.

This is in preparation for a future feature where conversion errors in
casts can be caught.

Author: jian he <jian.universality@gmail.com>
Reviewed-by: Amul Sul <sulamul@gmail.com>
Reviewed-by: Corey Huinker <corey.huinker@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CADkLM%3Dfv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ%40mail.gmail.com
This commit is contained in:
Peter Eisentraut 2026-03-28 15:44:13 +01:00
parent 999dec9ec6
commit 10e4d8aaf4

View file

@ -1785,8 +1785,8 @@ JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
/*
* Emit correct, translatable cast error message
*/
static void
cannotCastJsonbValue(enum jbvType type, const char *sqltype)
static Datum
cannotCastJsonbValue(enum jbvType type, const char *sqltype, Node *escontext)
{
static const struct
{
@ -1807,12 +1807,13 @@ cannotCastJsonbValue(enum jbvType type, const char *sqltype)
for (i = 0; i < lengthof(messages); i++)
if (messages[i].type == type)
ereport(ERROR,
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg(messages[i].msg, sqltype)));
/* should be unreachable */
elog(ERROR, "unknown jsonb type: %d", (int) type);
return (Datum) 0;
}
Datum
@ -1822,7 +1823,7 @@ jsonb_bool(PG_FUNCTION_ARGS)
JsonbValue v;
if (!JsonbExtractScalar(&in->root, &v))
cannotCastJsonbValue(v.type, "boolean");
return cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
if (v.type == jbvNull)
{
@ -1831,7 +1832,7 @@ jsonb_bool(PG_FUNCTION_ARGS)
}
if (v.type != jbvBool)
cannotCastJsonbValue(v.type, "boolean");
return cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
PG_FREE_IF_COPY(in, 0);
@ -1846,7 +1847,7 @@ jsonb_numeric(PG_FUNCTION_ARGS)
Numeric retValue;
if (!JsonbExtractScalar(&in->root, &v))
cannotCastJsonbValue(v.type, "numeric");
return cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
if (v.type == jbvNull)
{
@ -1855,7 +1856,7 @@ jsonb_numeric(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
cannotCastJsonbValue(v.type, "numeric");
return cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
/*
* v.val.numeric points into jsonb body, so we need to make a copy to
@ -1876,7 +1877,7 @@ jsonb_int2(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
cannotCastJsonbValue(v.type, "smallint");
return cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
if (v.type == jbvNull)
{
@ -1885,7 +1886,7 @@ jsonb_int2(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
cannotCastJsonbValue(v.type, "smallint");
return cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
retValue = DirectFunctionCall1(numeric_int2,
NumericGetDatum(v.val.numeric));
@ -1903,7 +1904,7 @@ jsonb_int4(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
cannotCastJsonbValue(v.type, "integer");
return cannotCastJsonbValue(v.type, "integer", fcinfo->context);
if (v.type == jbvNull)
{
@ -1912,7 +1913,7 @@ jsonb_int4(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
cannotCastJsonbValue(v.type, "integer");
return cannotCastJsonbValue(v.type, "integer", fcinfo->context);
retValue = DirectFunctionCall1(numeric_int4,
NumericGetDatum(v.val.numeric));
@ -1930,7 +1931,7 @@ jsonb_int8(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
cannotCastJsonbValue(v.type, "bigint");
return cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
if (v.type == jbvNull)
{
@ -1939,7 +1940,7 @@ jsonb_int8(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
cannotCastJsonbValue(v.type, "bigint");
return cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
retValue = DirectFunctionCall1(numeric_int8,
NumericGetDatum(v.val.numeric));
@ -1957,7 +1958,7 @@ jsonb_float4(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
cannotCastJsonbValue(v.type, "real");
return cannotCastJsonbValue(v.type, "real", fcinfo->context);
if (v.type == jbvNull)
{
@ -1966,7 +1967,7 @@ jsonb_float4(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
cannotCastJsonbValue(v.type, "real");
return cannotCastJsonbValue(v.type, "real", fcinfo->context);
retValue = DirectFunctionCall1(numeric_float4,
NumericGetDatum(v.val.numeric));
@ -1984,7 +1985,7 @@ jsonb_float8(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
cannotCastJsonbValue(v.type, "double precision");
return cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
if (v.type == jbvNull)
{
@ -1993,7 +1994,7 @@ jsonb_float8(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
cannotCastJsonbValue(v.type, "double precision");
return cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
retValue = DirectFunctionCall1(numeric_float8,
NumericGetDatum(v.val.numeric));