How about "cannot cast jsonb $json_type to $sql_type" where $json_type
is the type inside the jsonb (e.g. string, number, boolean, array,
object)?

Yes, that sounds pretty good.

Does anybody have an objections to patch?

--
Teodor Sigaev                                   E-mail: teo...@sigaev.ru
                                                   WWW: http://www.sigaev.ru/
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 9d2b89f90c..9f72984fac 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -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;
+}
+
+/*
+ * Map jsonb types to human-readable names
+ */
+static char*
+JsonbTypeName(enum jbvType type)
+{
+	static struct
+	{
+		enum jbvType	type;
+		char		   *typename;
+	}
+		names[] =
+	{
+		{ jbvNull, "null" },
+		{ jbvString, "string" },
+		{ jbvNumeric, "numeric" },
+		{ jbvBool, "boolean" },
+		{ jbvArray, "array" },
+		{ jbvObject, "object" },
+		{ jbvBinary, "array or object" }
+	};
+	int i;
+
+	for(i=0; i<lengthof(names); i++)
+		if (names[i].type == type)
+			return names[i].typename;
+
+	/* should be unreachable */
+	elog(ERROR, "unknown jsonb type: %d", (int)type);
+
+	return NULL;
 }
 
 Datum
@@ -1899,7 +1936,7 @@ jsonb_bool(PG_FUNCTION_ARGS)
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvBool)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("jsonb value must be boolean")));
+				 errmsg("cannot cast jsonb %s to boolean", JsonbTypeName(v.type))));
 
 	PG_FREE_IF_COPY(in, 0);
 
@@ -1916,7 +1953,7 @@ jsonb_numeric(PG_FUNCTION_ARGS)
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("jsonb value must be numeric")));
+				 errmsg("cannot cast jsonb %s to numeric", JsonbTypeName(v.type))));
 
 	/*
 	 * v.val.numeric points into jsonb body, so we need to make a copy to
@@ -1939,7 +1976,7 @@ jsonb_int2(PG_FUNCTION_ARGS)
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("jsonb value must be numeric")));
+				 errmsg("cannot cast jsonb %s to int2", JsonbTypeName(v.type))));
 
 	retValue = DirectFunctionCall1(numeric_int2,
 								   NumericGetDatum(v.val.numeric));
@@ -1959,7 +1996,7 @@ jsonb_int4(PG_FUNCTION_ARGS)
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("jsonb value must be numeric")));
+				 errmsg("cannot cast jsonb %s to int4", JsonbTypeName(v.type))));
 
 	retValue = DirectFunctionCall1(numeric_int4,
 								   NumericGetDatum(v.val.numeric));
@@ -1979,7 +2016,7 @@ jsonb_int8(PG_FUNCTION_ARGS)
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("jsonb value must be numeric")));
+				 errmsg("cannot cast jsonb %s to int8", JsonbTypeName(v.type))));
 
 	retValue = DirectFunctionCall1(numeric_int8,
 								   NumericGetDatum(v.val.numeric));
@@ -1999,7 +2036,7 @@ jsonb_float4(PG_FUNCTION_ARGS)
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("jsonb value must be numeric")));
+				 errmsg("cannot cast jsonb %s to float4", JsonbTypeName(v.type))));
 
 	retValue = DirectFunctionCall1(numeric_float4,
 								   NumericGetDatum(v.val.numeric));
@@ -2019,7 +2056,7 @@ jsonb_float8(PG_FUNCTION_ARGS)
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("jsonb value must be numeric")));
+				 errmsg("cannot cast jsonb %s to float8", JsonbTypeName(v.type))));
 
 	retValue = DirectFunctionCall1(numeric_float8,
 								   NumericGetDatum(v.val.numeric));
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index f705417b09..0be1fc97fc 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -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 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 float8
 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 int4
 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 numeric
 select '12345.05'::jsonb::numeric;
  numeric  
 ----------

Reply via email to