SQL/JSON: Rethink c2d93c3802b
authorAmit Langote <[email protected]>
Wed, 17 Jul 2024 08:10:57 +0000 (17:10 +0900)
committerAmit Langote <[email protected]>
Wed, 17 Jul 2024 08:13:48 +0000 (17:13 +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[email protected]

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)