]> granicus.if.org Git - postgresql/commitdiff
Improve jsonb cast error message
authorTeodor Sigaev <teodor@sigaev.ru>
Wed, 9 May 2018 10:23:16 +0000 (13:23 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Wed, 9 May 2018 10:23:16 +0000 (13:23 +0300)
Initial variant of error message didn't follow style of another casting error
messages and wasn't informative. Per gripe from Robert Haas.

Reviewer: Tom Lane
Discussion: https://www.postgresql.org/message-id/flat/CA%2BTgmob08StTV9yu04D0idRFNMh%2BUoyKax5Otvrix7rEZC8rMw%40mail.gmail.com#CA+Tgmob08StTV9yu04D0idRFNMh+UoyKax5Otvrix7rEZC8rMw@mail.gmail.com

src/backend/utils/adt/jsonb.c
src/test/regress/expected/jsonb.out

index 9d2b89f90cf1abd5f7ac5f69704ad06a82dc4962..6940b11c29057e27182f40084b32163d3c689531 100644 (file)
@@ -1857,7 +1857,7 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
 /*
  * Extract scalar value from raw-scalar pseudo-array jsonb.
  */
-static JsonbValue *
+static bool
 JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
 {
        JsonbIterator *it;
@@ -1865,7 +1865,11 @@ JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
        JsonbValue      tmp;
 
        if (!JsonContainerIsArray(jbc) || !JsonContainerIsScalar(jbc))
-               return NULL;
+       {
+               /* inform caller about actual type of container */
+               res->type = (JsonContainerIsArray(jbc)) ? jbvArray : jbvObject;
+               return false;
+       }
 
        /*
         * A root scalar is stored as an array of one element, so we get the array
@@ -1887,7 +1891,40 @@ JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
        tok = JsonbIteratorNext(&it, &tmp, true);
        Assert(tok == WJB_DONE);
 
-       return res;
+       return true;
+}
+
+/*
+ * Emit correct, translatable cast error message
+ */
+static void
+cannotCastJsonbValue(enum jbvType type, const char *sqltype)
+{
+       static const struct
+       {
+               enum jbvType    type;
+               const char         *msg;
+       }
+               messages[] =
+       {
+               { jbvNull,              gettext_noop("cannot cast jsonb null to type %s") },
+               { jbvString,    gettext_noop("cannot cast jsonb string to type %s") },
+               { jbvNumeric,   gettext_noop("cannot cast jsonb numeric to type %s") },
+               { jbvBool,              gettext_noop("cannot cast jsonb boolean to type %s") },
+               { jbvArray,             gettext_noop("cannot cast jsonb array to type %s") },
+               { jbvObject,    gettext_noop("cannot cast jsonb object to type %s") },
+               { jbvBinary,    gettext_noop("cannot cast jsonb array or object to type %s") }
+       };
+       int i;
+
+       for(i=0; i<lengthof(messages); i++)
+               if (messages[i].type == type)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg(messages[i].msg, sqltype)));
+
+       /* should be unreachable */
+       elog(ERROR, "unknown jsonb type: %d", (int)type);
 }
 
 Datum
@@ -1897,9 +1934,7 @@ jsonb_bool(PG_FUNCTION_ARGS)
        JsonbValue      v;
 
        if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvBool)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb value must be boolean")));
+               cannotCastJsonbValue(v.type, "boolean");
 
        PG_FREE_IF_COPY(in, 0);
 
@@ -1914,9 +1949,7 @@ jsonb_numeric(PG_FUNCTION_ARGS)
        Numeric         retValue;
 
        if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb value must be numeric")));
+               cannotCastJsonbValue(v.type, "numeric");
 
        /*
         * v.val.numeric points into jsonb body, so we need to make a copy to
@@ -1937,9 +1970,7 @@ jsonb_int2(PG_FUNCTION_ARGS)
        Datum           retValue;
 
        if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb value must be numeric")));
+               cannotCastJsonbValue(v.type, "smallint");
 
        retValue = DirectFunctionCall1(numeric_int2,
                                                                   NumericGetDatum(v.val.numeric));
@@ -1957,9 +1988,7 @@ jsonb_int4(PG_FUNCTION_ARGS)
        Datum           retValue;
 
        if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb value must be numeric")));
+               cannotCastJsonbValue(v.type, "integer");
 
        retValue = DirectFunctionCall1(numeric_int4,
                                                                   NumericGetDatum(v.val.numeric));
@@ -1977,9 +2006,7 @@ jsonb_int8(PG_FUNCTION_ARGS)
        Datum           retValue;
 
        if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb value must be numeric")));
+               cannotCastJsonbValue(v.type, "bigint");
 
        retValue = DirectFunctionCall1(numeric_int8,
                                                                   NumericGetDatum(v.val.numeric));
@@ -1997,9 +2024,7 @@ jsonb_float4(PG_FUNCTION_ARGS)
        Datum           retValue;
 
        if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb value must be numeric")));
+               cannotCastJsonbValue(v.type, "real");
 
        retValue = DirectFunctionCall1(numeric_float4,
                                                                   NumericGetDatum(v.val.numeric));
@@ -2017,9 +2042,7 @@ jsonb_float8(PG_FUNCTION_ARGS)
        Datum           retValue;
 
        if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb value must be numeric")));
+               cannotCastJsonbValue(v.type, "double precision");
 
        retValue = DirectFunctionCall1(numeric_float8,
                                                                   NumericGetDatum(v.val.numeric));
index 593abd74c3ac270708fbc19a858b4a063ed3f239..3c37a8ed27d5a1aee6e316b0b31181d115db9929 100644 (file)
@@ -4321,7 +4321,7 @@ select 'true'::jsonb::bool;
 (1 row)
 
 select '[]'::jsonb::bool;
-ERROR:  jsonb value must be boolean
+ERROR:  cannot cast jsonb array to type boolean
 select '1.0'::jsonb::float;
  float8 
 --------
@@ -4329,7 +4329,7 @@ select '1.0'::jsonb::float;
 (1 row)
 
 select '[1.0]'::jsonb::float;
-ERROR:  jsonb value must be numeric
+ERROR:  cannot cast jsonb array to type double precision
 select '12345'::jsonb::int4;
  int4  
 -------
@@ -4337,7 +4337,7 @@ select '12345'::jsonb::int4;
 (1 row)
 
 select '"hello"'::jsonb::int4;
-ERROR:  jsonb value must be numeric
+ERROR:  cannot cast jsonb string to type integer
 select '12345'::jsonb::numeric;
  numeric 
 ---------
@@ -4345,7 +4345,7 @@ select '12345'::jsonb::numeric;
 (1 row)
 
 select '{}'::jsonb::numeric;
-ERROR:  jsonb value must be numeric
+ERROR:  cannot cast jsonb object to type numeric
 select '12345.05'::jsonb::numeric;
  numeric  
 ----------