]> granicus.if.org Git - postgresql/commitdiff
Add casts from jsonb
authorTeodor Sigaev <teodor@sigaev.ru>
Thu, 29 Mar 2018 13:33:56 +0000 (16:33 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Thu, 29 Mar 2018 13:33:56 +0000 (16:33 +0300)
Add explicit cast from scalar jsonb to all numeric and bool types. It would be
better to have cast from scalar jsonb to text too but there is already a cast
from jsonb to text as just text representation of json. There is no way to have
two different casts for the same type's pair.

Bump catalog version

Author: Anastasia Lubennikova with editorization by Nikita Glukhov and me
Review by: Aleksander Alekseev, Nikita Glukhov, Darafei Praliaskouski
Discussion: https://www.postgresql.org/message-id/flat/0154d35a-24ae-f063-5273-9ffcdf1c7f2e@postgrespro.ru

src/backend/utils/adt/jsonb.c
src/include/catalog/catversion.h
src/include/catalog/pg_cast.h
src/include/catalog/pg_proc.h
src/test/regress/expected/jsonb.out
src/test/regress/sql/jsonb.sql

index 0f701801641aa8bc696f8917f8491be303faab75..80d23cc0524cdb8ac6ce50bd91a1c7539a2367d7 100644 (file)
@@ -1845,3 +1845,178 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
 
        PG_RETURN_POINTER(out);
 }
+
+
+/*
+ * Extract scalar value from raw-scalar pseudo-array jsonb.
+ */
+static JsonbValue *
+JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
+{
+       JsonbIterator *it;
+       JsonbIteratorToken tok PG_USED_FOR_ASSERTS_ONLY;
+       JsonbValue      tmp;
+
+       if (!JsonContainerIsArray(jbc) || !JsonContainerIsScalar(jbc))
+               return NULL;
+
+       /*
+        * A root scalar is stored as an array of one element, so we get the
+        * array and then its first (and only) member.
+        */
+       it = JsonbIteratorInit(jbc);
+
+       tok = JsonbIteratorNext(&it, &tmp, true);
+       Assert(tok == WJB_BEGIN_ARRAY);
+       Assert(tmp.val.array.nElems == 1 && tmp.val.array.rawScalar);
+
+       tok = JsonbIteratorNext(&it, res, true);
+       Assert (tok == WJB_ELEM);
+       Assert(IsAJsonbScalar(res));
+
+       tok = JsonbIteratorNext(&it, &tmp, true);
+       Assert (tok == WJB_END_ARRAY);
+
+       tok = JsonbIteratorNext(&it, &tmp, true);
+       Assert(tok == WJB_DONE);
+
+       return res;
+}
+
+Datum
+jsonb_bool(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
+       JsonbValue      v;
+
+       if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvBool)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("jsonb value must be boolean")));
+
+       PG_FREE_IF_COPY(in, 0);
+
+       PG_RETURN_BOOL(v.val.boolean);
+}
+
+Datum
+jsonb_numeric(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
+       JsonbValue      v;
+       Numeric         retValue;
+
+       if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("jsonb value must be numeric")));
+
+       /*
+        * v.val.numeric points into jsonb body, so we need to make a copy to return
+        */
+       retValue = DatumGetNumericCopy(NumericGetDatum(v.val.numeric));
+
+       PG_FREE_IF_COPY(in, 0);
+
+       PG_RETURN_NUMERIC(retValue);
+}
+
+Datum
+jsonb_int2(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
+       JsonbValue  v;
+       Datum           retValue;
+
+       if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("jsonb value must be numeric")));
+
+       retValue = DirectFunctionCall1(numeric_int2,
+                                                                  NumericGetDatum(v.val.numeric));
+
+       PG_FREE_IF_COPY(in, 0);
+
+       PG_RETURN_DATUM(retValue);
+}
+
+Datum
+jsonb_int4(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
+       JsonbValue  v;
+       Datum           retValue;
+
+       if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("jsonb value must be numeric")));
+
+       retValue = DirectFunctionCall1(numeric_int4,
+                                                                  NumericGetDatum(v.val.numeric));
+
+       PG_FREE_IF_COPY(in, 0);
+
+       PG_RETURN_DATUM(retValue);
+}
+
+Datum
+jsonb_int8(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
+       JsonbValue  v;
+       Datum           retValue;
+
+       if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("jsonb value must be numeric")));
+
+       retValue = DirectFunctionCall1(numeric_int8,
+                                                                  NumericGetDatum(v.val.numeric));
+
+       PG_FREE_IF_COPY(in, 0);
+
+       PG_RETURN_DATUM(retValue);
+}
+
+Datum
+jsonb_float4(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
+       JsonbValue      v;
+       Datum           retValue;
+
+       if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("jsonb value must be numeric")));
+
+       retValue = DirectFunctionCall1(numeric_float4,
+                                                                  NumericGetDatum(v.val.numeric));
+
+       PG_FREE_IF_COPY(in, 0);
+
+       PG_RETURN_DATUM(retValue);
+}
+
+Datum
+jsonb_float8(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
+       JsonbValue      v;
+       Datum           retValue;
+
+       if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("jsonb value must be numeric")));
+
+       retValue = DirectFunctionCall1(numeric_float8,
+                                                                  NumericGetDatum(v.val.numeric));
+
+       PG_FREE_IF_COPY(in, 0);
+
+       PG_RETURN_DATUM(retValue);
+}
index bfc2bd125d731ee6e075d4cfc83567d60a8f1ea9..d4a7b23f80121a93abacc5792f586d628fb06121 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201803271
+#define CATALOG_VERSION_NO     201803291
 
 #endif
index b4478188caba8f5b4320a5efa4d3c5415fbb594b..c47fb5bd0ddf09c85ee51186e9c8c3389a926448 100644 (file)
@@ -392,4 +392,13 @@ DATA(insert ( 1700 1700 1703 i f ));
 DATA(insert (  114 3802    0 a i ));
 DATA(insert ( 3802     114    0 a i ));
 
+/* jsonb to numeric and bool types */
+DATA(insert ( 3802     16       3556 e f ));
+DATA(insert ( 3802     1700 3449 e f ));
+DATA(insert ( 3802     21   3450 e f ));
+DATA(insert ( 3802     23   3451 e f ));
+DATA(insert ( 3802     20   3452 e f ));
+DATA(insert ( 3802     700  3453 e f ));
+DATA(insert ( 3802     701  2580 e f ));
+
 #endif                                                 /* PG_CAST_H */
index bfc90098f8671a1bac7eb4866ee54d578974f668..ec50afcdf000be1f9af6ecf476776f2d6f20f9ba 100644 (file)
@@ -2475,6 +2475,22 @@ DESCR("convert int2 to numeric");
 DATA(insert OID = 1783 ( int2                                  PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 21 "1700" _null_ _null_ _null_ _null_ _null_ numeric_int2 _null_ _null_ _null_ ));
 DESCR("convert numeric to int2");
 
+
+DATA(insert OID = 3556 ( bool                          PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 16 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_bool  _null_ _null_ _null_ ));
+DESCR("convert jsonb to boolean");
+DATA(insert OID = 3449 ( numeric                       PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 1700 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_numeric  _null_ _null_ _null_ ));
+DESCR("convert jsonb to numeric");
+DATA(insert OID = 3450 ( int2                          PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 21 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_int2  _null_ _null_ _null_ ));
+DESCR("convert jsonb to int2");
+DATA(insert OID = 3451 ( int4                          PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 23 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_int4  _null_ _null_ _null_ ));
+DESCR("convert jsonb to int4");
+DATA(insert OID = 3452 ( int8                          PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 20 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_int8  _null_ _null_ _null_ ));
+DESCR("convert jsonb to int8");
+DATA(insert OID = 3453 ( float4                                PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 700 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_float4  _null_ _null_ _null_ ));
+DESCR("convert jsonb to float4");
+DATA(insert OID = 2580 ( float8                                PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 701 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_float8  _null_ _null_ _null_ ));
+DESCR("convert jsonb to float8");
+
 /* formatting */
 DATA(insert OID = 1770 ( to_char                       PGNSP PGUID 12 1 0 0 0 f f f t f s s 2 0 25 "1184 25" _null_ _null_ _null_ _null_  _null_ timestamptz_to_char _null_ _null_ _null_ ));
 DESCR("format timestamp with time zone to text");
index 465195a3172a40137b9d4715a54bc24ea0d425d2..f8d6e6f7ccd17104752ed5dee7393a7eed6aaf8b 100644 (file)
@@ -4191,3 +4191,108 @@ select ts_headline('[]'::jsonb, tsquery('aaa & bbb'));
  []
 (1 row)
 
+-- casts
+select 'true'::jsonb::bool;
+ bool 
+------
+ t
+(1 row)
+
+select '[]'::jsonb::bool;
+ERROR:  jsonb value must be boolean
+select '1.0'::jsonb::float;
+ float8 
+--------
+      1
+(1 row)
+
+select '[1.0]'::jsonb::float;
+ERROR:  jsonb value must be numeric
+select '12345'::jsonb::int4;
+ int4  
+-------
+ 12345
+(1 row)
+
+select '"hello"'::jsonb::int4;
+ERROR:  jsonb value must be numeric
+select '12345'::jsonb::numeric;
+ numeric 
+---------
+   12345
+(1 row)
+
+select '{}'::jsonb::numeric;
+ERROR:  jsonb value must be numeric
+select '12345.05'::jsonb::numeric;
+ numeric  
+----------
+ 12345.05
+(1 row)
+
+select '12345.05'::jsonb::float4;
+ float4 
+--------
+  12345
+(1 row)
+
+select '12345.05'::jsonb::float8;
+  float8  
+----------
+ 12345.05
+(1 row)
+
+select '12345.05'::jsonb::int2;
+ int2  
+-------
+ 12345
+(1 row)
+
+select '12345.05'::jsonb::int4;
+ int4  
+-------
+ 12345
+(1 row)
+
+select '12345.05'::jsonb::int8;
+ int8  
+-------
+ 12345
+(1 row)
+
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::numeric;
+                       numeric                        
+------------------------------------------------------
+ 12345.0000000000000000000000000000000000000000000005
+(1 row)
+
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::float4;
+ float4 
+--------
+  12345
+(1 row)
+
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::float8;
+ float8 
+--------
+  12345
+(1 row)
+
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::int2;
+ int2  
+-------
+ 12345
+(1 row)
+
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::int4;
+ int4  
+-------
+ 12345
+(1 row)
+
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::int8;
+ int8  
+-------
+ 12345
+(1 row)
+
index 903e5ef67d868c5b60c7a94a5c7793e8a90ad5d4..2439f949bd51793a34d087bfa62f98e14c11c0a2 100644 (file)
@@ -1105,3 +1105,25 @@ select ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1":
 select ts_headline('null'::jsonb, tsquery('aaa & bbb'));
 select ts_headline('{}'::jsonb, tsquery('aaa & bbb'));
 select ts_headline('[]'::jsonb, tsquery('aaa & bbb'));
+
+-- casts
+select 'true'::jsonb::bool;
+select '[]'::jsonb::bool;
+select '1.0'::jsonb::float;
+select '[1.0]'::jsonb::float;
+select '12345'::jsonb::int4;
+select '"hello"'::jsonb::int4;
+select '12345'::jsonb::numeric;
+select '{}'::jsonb::numeric;
+select '12345.05'::jsonb::numeric;
+select '12345.05'::jsonb::float4;
+select '12345.05'::jsonb::float8;
+select '12345.05'::jsonb::int2;
+select '12345.05'::jsonb::int4;
+select '12345.05'::jsonb::int8;
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::numeric;
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::float4;
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::float8;
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::int2;
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::int4;
+select '12345.0000000000000000000000000000000000000000000005'::jsonb::int8;