Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion e2e_test/batch/types/jsonb.slt.part
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,78 @@ select '2.5'::jsonb::smallint, '3.5'::jsonb::smallint;
----
2 4

statement error cannot cast jsonb null to type boolean
query T
select 'null'::jsonb::bool;
----
NULL

query T
select null::jsonb::bool;
----
NULL

query R
select 'null'::jsonb::double precision;
----
NULL

query R
select '1.0'::jsonb::real;
----
1

query T
select 'null'::jsonb::real;
----
NULL

statement error cannot cast jsonb array to type number
select '[1.0]'::jsonb::real;

query I
select '12345'::jsonb::smallint;
----
12345

query T
select 'null'::jsonb::smallint;
----
NULL

statement error cannot cast jsonb string to type number
select '"hello"'::jsonb::smallint;

query I
select '12345'::jsonb::int;
----
12345

query T
select 'null'::jsonb::int;
----
NULL

statement error cannot cast jsonb string to type number
select '"hello"'::jsonb::int;

query I
select '12345'::jsonb::bigint;
----
12345

query T
select 'null'::jsonb::bigint;
----
NULL

statement error cannot cast jsonb string to type number
select '"hello"'::jsonb::bigint;

query T
select 'null'::jsonb::decimal;
----
NULL

# Example of accessing the boolean nested inside object and array
query T
select ('{"k2":[2,true,4]}'::jsonb -> 'k2' -> -2)::bool;
Expand Down
25 changes: 18 additions & 7 deletions src/expr/impl/src/scalar/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,14 @@ pub fn to_int256<T: TryInto<Int256>>(elem: T) -> Result<Int256> {
}

#[function("cast(jsonb) -> boolean")]
pub fn jsonb_to_bool(v: JsonbRef<'_>) -> Result<bool> {
v.as_bool().map_err(|e| ExprError::Parse(e.into()))
pub fn jsonb_to_bool(v: JsonbRef<'_>) -> Result<Option<bool>> {
if v.is_jsonb_null() {
Ok(None)
} else {
v.as_bool()
.map(Some)
.map_err(|e| ExprError::Parse(e.into()))
}
}

/// Note that PostgreSQL casts JSON numbers from arbitrary precision `numeric` but we use `f64`.
Expand All @@ -78,11 +84,16 @@ pub fn jsonb_to_bool(v: JsonbRef<'_>) -> Result<bool> {
#[function("cast(jsonb) -> decimal")]
#[function("cast(jsonb) -> float4")]
#[function("cast(jsonb) -> float8")]
pub fn jsonb_to_number<T: TryFrom<F64>>(v: JsonbRef<'_>) -> Result<T> {
v.as_number()
.map_err(|e| ExprError::Parse(e.into()))?
.try_into()
.map_err(|_| ExprError::NumericOutOfRange)
pub fn jsonb_to_number<T: TryFrom<F64>>(v: JsonbRef<'_>) -> Result<Option<T>> {
if v.is_jsonb_null() {
Ok(None)
} else {
v.as_number()
.map_err(|e| ExprError::Parse(e.into()))?
.try_into()
.map(Some)
.map_err(|_| ExprError::NumericOutOfRange)
}
}

#[function("cast(int4) -> int2")]
Expand Down
66 changes: 66 additions & 0 deletions src/tests/regress/data/expected/jsonb.out
Original file line number Diff line number Diff line change
Expand Up @@ -5413,6 +5413,12 @@ select 'true'::jsonb::bool AS v;
t
(1 row)

select 'null'::jsonb::bool AS v;
v
---

(1 row)

select '[]'::jsonb::bool AS v;
ERROR: cannot cast jsonb array to type boolean
select '1.0'::jsonb::float AS v;
Expand All @@ -5421,22 +5427,82 @@ select '1.0'::jsonb::float AS v;
1
(1 row)

select 'null'::jsonb::float AS v;
v
---

(1 row)

select '[1.0]'::jsonb::float AS v;
ERROR: cannot cast jsonb array to type double precision
select '1.0'::jsonb::float4 AS v;
v
---
1
(1 row)

select 'null'::jsonb::float4 AS v;
v
---

(1 row)

select '[1.0]'::jsonb::float4 AS v;
ERROR: cannot cast jsonb array to type real
select '12345'::jsonb::int2 AS v;
v
------
12345
(1 row)

select 'null'::jsonb::int2 AS v;
v
---

(1 row)

select '"hello"'::jsonb::int2 AS v;
ERROR: cannot cast jsonb string to type smallint
select '12345'::jsonb::int4 AS v;
v
-------
12345
(1 row)

select 'null'::jsonb::int4 AS v;
v
---

(1 row)

select '"hello"'::jsonb::int4 AS v;
ERROR: cannot cast jsonb string to type integer
select '12345'::jsonb::int8 AS v;
v
------
12345
(1 row)

select 'null'::jsonb::int8 AS v;
v
---

(1 row)

select '"hello"'::jsonb::int8 AS v;
ERROR: cannot cast jsonb string to type bigint
select '12345'::jsonb::numeric AS v;
v
-------
12345
(1 row)

select 'null'::jsonb::numeric AS v;
v
---

(1 row)

select '{}'::jsonb::numeric AS v;
ERROR: cannot cast jsonb object to type numeric
select '12345.05'::jsonb::numeric AS v;
Expand Down
13 changes: 13 additions & 0 deletions src/tests/regress/data/sql/jsonb.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1464,12 +1464,25 @@ select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"');

-- casts
select 'true'::jsonb::bool AS v;
--@ select 'null'::jsonb::bool AS v;
select '[]'::jsonb::bool AS v;
select '1.0'::jsonb::float AS v;
--@ select 'null'::jsonb::float AS v;
select '[1.0]'::jsonb::float AS v;
--@ select '1.0'::jsonb::float4 AS v;
--@ select 'null'::jsonb::float4 AS v;
--@ select '[1.0]'::jsonb::float4 AS v;
--@ select '12345'::jsonb::int2 AS v;
--@ select 'null'::jsonb::int2 AS v;
--@ select '"hello"'::jsonb::int2 AS v;
select '12345'::jsonb::int4 AS v;
--@ select 'null'::jsonb::int4 AS v;
select '"hello"'::jsonb::int4 AS v;
--@ select '12345'::jsonb::int8 AS v;
--@ select 'null'::jsonb::int8 AS v;
--@ select '"hello"'::jsonb::int8 AS v;
select '12345'::jsonb::numeric AS v;
--@ select 'null'::jsonb::numeric AS v;
select '{}'::jsonb::numeric AS v;
select '12345.05'::jsonb::numeric AS v;
select '12345.05'::jsonb::float4 AS v;
Expand Down
Loading