SQL/JSON: Rethink c2d93c3802

This essentially reverts c2d93c3802 except tests. The problem with
c2d93c3802 was that it only changed the casting behavior for types
with typmod, and had coding issues noted in the post-commit review.

This commit changes coerceJsonFuncExpr() to use assignment-level casts
instead of explicit casts to coerce the result of JSON constructor
functions to the specified or the default RETURNING type.  Using
assignment-level casts fixes the problem that using explicit casts was
leading to the wrong typmod / length coercion behavior -- truncating
results longer than the specified length instead of erroring out --
which c2d93c3802 aimed to solve.

That restricts the set of allowed target types to string types, the
same set that's currently allowed.

Discussion: https://postgr.es/m/202406291824.reofujy7xdj3@alvherre.pgsql
This commit is contained in:
Amit Langote 2024-07-17 17:10:57 +09:00
parent ec678692f6
commit 86d33987e8
1 changed files with 10 additions and 24 deletions

View File

@ -3582,7 +3582,6 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr,
Node *res; Node *res;
int location; int location;
Oid exprtype = exprType(expr); Oid exprtype = exprType(expr);
int32 baseTypmod = returning->typmod;
/* if output type is not specified or equals to function type, return */ /* if output type is not specified or equals to function type, return */
if (!OidIsValid(returning->typid) || returning->typid == exprtype) if (!OidIsValid(returning->typid) || returning->typid == exprtype)
@ -3612,19 +3611,18 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr,
} }
/* /*
* For domains, consider the base type's typmod to decide whether to setup * For other cases, try to coerce expression to the output type using
* an implicit or explicit cast. * assignment-level casts, erroring out if none available. This basically
* allows coercing the jsonb value to any string type (typcategory = 'S').
*
* Requesting assignment-level here means that typmod / length coercion
* assumes implicit coercion which is the behavior we want; see
* build_coercion_expression().
*/ */
if (get_typtype(returning->typid) == TYPTYPE_DOMAIN)
(void) getBaseTypeAndTypmod(returning->typid, &baseTypmod);
/* try to coerce expression to the output type */
res = coerce_to_target_type(pstate, expr, exprtype, res = coerce_to_target_type(pstate, expr, exprtype,
returning->typid, baseTypmod, returning->typid, returning->typmod,
baseTypmod > 0 ? COERCION_IMPLICIT : COERCION_ASSIGNMENT,
COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
baseTypmod > 0 ? COERCE_IMPLICIT_CAST :
COERCE_EXPLICIT_CAST,
location); location);
if (!res && report_error) if (!res && report_error)
@ -3649,7 +3647,6 @@ makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr); JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr);
Node *placeholder; Node *placeholder;
Node *coercion; Node *coercion;
int32 baseTypmod = returning->typmod;
jsctor->args = args; jsctor->args = args;
jsctor->func = fexpr; jsctor->func = fexpr;
@ -3687,17 +3684,6 @@ makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
placeholder = (Node *) cte; placeholder = (Node *) cte;
} }
/*
* Convert the source expression to text, because coerceJsonFuncExpr()
* will create an implicit cast to the RETURNING types with typmod and
* there are no implicit casts from json(b) to such types. For domains,
* the base type's typmod will be considered, so do so here too.
*/
if (get_typtype(returning->typid) == TYPTYPE_DOMAIN)
(void) getBaseTypeAndTypmod(returning->typid, &baseTypmod);
if (baseTypmod > 0)
placeholder = coerce_to_specific_type(pstate, placeholder, TEXTOID,
"JSON_CONSTRUCTOR()");
coercion = coerceJsonFuncExpr(pstate, placeholder, returning, true); coercion = coerceJsonFuncExpr(pstate, placeholder, returning, true);
if (coercion != placeholder) if (coercion != placeholder)