SQL/JSON: Rethink c2d93c3802b
authorAmit Langote <amitlan@postgresql.org>
Wed, 17 Jul 2024 08:10:57 +0000 (17:10 +0900)
committerAmit Langote <amitlan@postgresql.org>
Wed, 17 Jul 2024 08:14:01 +0000 (17:14 +0900)
This essentially reverts c2d93c3802b except tests. The problem with
c2d93c3802b 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 c2d93c3802b 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

src/backend/parser/parse_expr.c

index 45c019627cc0057d45b7e20c0224e1e1e52296c6..8577f278065b77efac6054032b8ee9cc6b5a830d 100644 (file)
@@ -3582,7 +3582,6 @@ 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)
@@ -3612,19 +3611,18 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr,
        }
 
        /*
-        * For domains, consider the base type's typmod to decide whether to setup
-        * an implicit or explicit cast.
+        * For other cases, try to coerce expression to the output type using
+        * 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,
-                                                               returning->typid, baseTypmod,
-                                                               baseTypmod > 0 ? COERCION_IMPLICIT :
-                                                               COERCION_EXPLICIT,
-                                                               baseTypmod > 0 ? COERCE_IMPLICIT_CAST :
-                                                               COERCE_EXPLICIT_CAST,
+                                                               returning->typid, returning->typmod,
+                                                               COERCION_ASSIGNMENT,
+                                                               COERCE_IMPLICIT_CAST,
                                                                location);
 
        if (!res && report_error)
@@ -3649,7 +3647,6 @@ makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
        JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr);
        Node       *placeholder;
        Node       *coercion;
-       int32           baseTypmod = returning->typmod;
 
        jsctor->args = args;
        jsctor->func = fexpr;
@@ -3687,17 +3684,6 @@ 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)