From c2d93c3802b205d135d1ae1d7ac167d74e08a274 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Fri, 28 Jun 2024 21:37:14 +0900 Subject: SQL/JSON: Fix coercion of constructor outputs to types with typmod Ensure SQL/JSON constructor functions that allow specifying the target type using the RETURNING clause perform implicit cast to that type. This ensures that output values that exceed the specified length produce an error rather than being silently truncated. This behavior conforms to the SQL standard. Reported-by: Alvaro Herrera Reviewed-by: Jian He Discussion: https://postgr.es/m/202405271326.5a5rprki64aw%40alvherre.pgsql --- src/backend/parser/parse_expr.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src/backend/parser/parse_expr.c') diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 233b7b1cc99..df766cdec19 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -3583,6 +3583,7 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr, Node *res; int location; Oid exprtype = exprType(expr); + int32 baseTypmod = returning->typmod; /* if output type is not specified or equals to function type, return */ if (!OidIsValid(returning->typid) || returning->typid == exprtype) @@ -3611,10 +3612,19 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr, return (Node *) fexpr; } + /* + * For domains, consider the base type's typmod to decide whether to setup + * an implicit or explicit cast. + */ + 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, - returning->typid, returning->typmod, + returning->typid, baseTypmod, + baseTypmod > 0 ? COERCION_IMPLICIT : COERCION_EXPLICIT, + baseTypmod > 0 ? COERCE_IMPLICIT_CAST : COERCE_EXPLICIT_CAST, location); @@ -3640,6 +3650,7 @@ makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type, JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr); Node *placeholder; Node *coercion; + int32 baseTypmod = returning->typmod; jsctor->args = args; jsctor->func = fexpr; @@ -3677,6 +3688,17 @@ makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type, 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); if (coercion != placeholder) -- cgit v1.2.3