From: Aleksander Alekseev <a.alekseev@postgrespro.ru>
To: David Steele <david@pgmasters.net>
Cc: Nikita Glukhov <n.gluhov@postgrespro.ru>
Cc: Darafei Praliaskouski <me@komzpa.net>
Cc: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
Cc: pgsql-hackers@lists.postgresql.org
Subject: Re: Re: Cast jsonb to numeric, int, float, bool
Date: Tue, 27 Mar 2018 17:53:37 +0300
Message-ID: <20180327145337.GA20311@e733.localdomain> (raw)
In-Reply-To: <d75ed4fe-574b-41c7-080f-ed662fa5d9ba@pgmasters.net>
References: <0154d35a-24ae-f063-5273-9ffcdf1c7f2e@postgrespro.ru>
<9c013081-5dc0-1b87-a559-a0c2d405727a@postgrespro.ru>
<151985418868.6915.17223208131889314092.pgcf@coridan.postgresql.org>
<76b000ae-94d1-cd8f-a2e0-e536096469b0@postgrespro.ru>
<d75ed4fe-574b-41c7-080f-ed662fa5d9ba@pgmasters.net>
Hello everyone,
> Since this patch was updated after being set a Ready for Committer and
> there appear to be some open questions, I have set it to Needs Review.
I decided to take a look.
The patch didn't apply after changes made in fd1a421f, but I fixed that.
Also there were no tests. I fixed that too.
I believe it's "Ready for Committer".
--
Best regards,
Aleksander Alekseev
Attachments:jsonb-casts-v4.patch (text/plain, 6.1K) downloaddiff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.cindex 0f70180164..fb907f2a73 100644--- a/src/backend/utils/adt/jsonb.c+++ b/src/backend/utils/adt/jsonb.c@@ -1845,3 +1845,98 @@ 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_numeric(PG_FUNCTION_ARGS)+{+ Jsonb *in = PG_GETARG_JSONB_P(0);+ JsonbValue v;++ if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("jsonb value must be numeric")));++ PG_RETURN_NUMERIC(v.val.numeric);+}++Datum+jsonb_int4(PG_FUNCTION_ARGS)+{+ Jsonb *in = PG_GETARG_JSONB_P(0);+ JsonbValue v;++ if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("jsonb value must be numeric")));++ PG_RETURN_INT32(DatumGetInt32(DirectFunctionCall1(numeric_int4,+ NumericGetDatum(v.val.numeric))));+}++Datum+jsonb_float8(PG_FUNCTION_ARGS)+{+ Jsonb *in = PG_GETARG_JSONB_P(0);+ JsonbValue v;++ if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("jsonb value must be numeric")));++ PG_RETURN_FLOAT8(DatumGetFloat8(DirectFunctionCall1(numeric_float8,+ NumericGetDatum(v.val.numeric))));+}++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_RETURN_BOOL(v.val.boolean);+}diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.hindex b4478188ca..ea765df80e 100644--- a/src/include/catalog/pg_cast.h+++ b/src/include/catalog/pg_cast.h@@ -391,5 +391,10 @@ DATA(insert ( 1700 1700 1703 i f ));
/* json to/from jsonb */
DATA(insert ( 114 3802 0 a i ));
DATA(insert ( 3802 114 0 a i ));
+/* jsonb to numeric types */+DATA(insert ( 3802 1700 4001 e f ));+DATA(insert ( 3802 23 4002 e f ));+DATA(insert ( 3802 701 4003 e f ));+DATA(insert ( 3802 16 4004 e f ));
#endif /* PG_CAST_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.hindex bfc90098f8..0d5d33985d 100644--- a/src/include/catalog/pg_proc.h+++ b/src/include/catalog/pg_proc.h@@ -2475,6 +2475,16 @@ 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 = 4001 ( 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 = 4002 ( 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 = 4003 ( 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");+DATA(insert OID = 4004 ( 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");+
/* 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");
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.outindex 465195a317..f577ad6aa5 100644--- a/src/test/regress/expected/jsonb.out+++ b/src/test/regress/expected/jsonb.out@@ -4191,3 +4191,36 @@ 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 numericdiff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sqlindex 903e5ef67d..9c2158f8c6 100644--- a/src/test/regress/sql/jsonb.sql+++ b/src/test/regress/sql/jsonb.sql@@ -1105,3 +1105,13 @@ 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;
signature.asc (application/pgp-signature, 832B) download
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: a.alekseev@postgrespro.ru, david@pgmasters.net, n.gluhov@postgrespro.ru, me@komzpa.net, a.lubennikova@postgrespro.ru, pgsql-hackers@lists.postgresql.org
Subject: Re: Re: Cast jsonb to numeric, int, float, bool
In-Reply-To: <20180327145337.GA20311@e733.localdomain>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox