mz_expr/scalar/func/impls/
jsonb.rs1use std::fmt;
11
12use mz_lowertest::MzReflect;
13use mz_repr::adt::jsonb::{Jsonb, JsonbRef};
14use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale};
15use mz_repr::{Datum, Row, RowPacker, SqlColumnType, SqlScalarType, strconv};
16use serde::{Deserialize, Serialize};
17
18use crate::EvalError;
19use crate::scalar::func::EagerUnaryFunc;
20use crate::scalar::func::impls::numeric::*;
21
22sqlfunc!(
23 #[sqlname = "jsonb_to_text"]
24 #[preserves_uniqueness = false]
25 #[inverse = to_unary!(super::CastStringToJsonb)]
26 fn cast_jsonb_to_string<'a>(a: JsonbRef<'a>) -> String {
27 let mut buf = String::new();
28 strconv::format_jsonb(&mut buf, a);
29 buf
30 }
31);
32
33sqlfunc!(
34 #[sqlname = "jsonb_to_smallint"]
35 #[is_monotone = true]
36 fn cast_jsonb_to_int16<'a>(a: JsonbRef<'a>) -> Result<i16, EvalError> {
37 match a.into_datum() {
38 Datum::Numeric(a) => cast_numeric_to_int16(a.into_inner()),
39 datum => Err(EvalError::InvalidJsonbCast {
40 from: jsonb_typeof(JsonbRef::from_datum(datum)).into(),
41 to: "smallint".into(),
42 }),
43 }
44 }
45);
46
47sqlfunc!(
48 #[sqlname = "jsonb_to_integer"]
49 #[is_monotone = true]
50 fn cast_jsonb_to_int32<'a>(a: JsonbRef<'a>) -> Result<i32, EvalError> {
51 match a.into_datum() {
52 Datum::Numeric(a) => cast_numeric_to_int32(a.into_inner()),
53 datum => Err(EvalError::InvalidJsonbCast {
54 from: jsonb_typeof(JsonbRef::from_datum(datum)).into(),
55 to: "integer".into(),
56 }),
57 }
58 }
59);
60
61sqlfunc!(
62 #[sqlname = "jsonb_to_bigint"]
63 #[is_monotone = true]
64 fn cast_jsonb_to_int64<'a>(a: JsonbRef<'a>) -> Result<i64, EvalError> {
65 match a.into_datum() {
66 Datum::Numeric(a) => cast_numeric_to_int64(a.into_inner()),
67 datum => Err(EvalError::InvalidJsonbCast {
68 from: jsonb_typeof(JsonbRef::from_datum(datum)).into(),
69 to: "bigint".into(),
70 }),
71 }
72 }
73);
74
75sqlfunc!(
76 #[sqlname = "jsonb_to_real"]
77 #[is_monotone = true]
78 fn cast_jsonb_to_float32<'a>(a: JsonbRef<'a>) -> Result<f32, EvalError> {
79 match a.into_datum() {
80 Datum::Numeric(a) => cast_numeric_to_float32(a.into_inner()),
81 datum => Err(EvalError::InvalidJsonbCast {
82 from: jsonb_typeof(JsonbRef::from_datum(datum)).into(),
83 to: "real".into(),
84 }),
85 }
86 }
87);
88
89sqlfunc!(
90 #[sqlname = "jsonb_to_double"]
91 #[is_monotone = true]
92 fn cast_jsonb_to_float64<'a>(a: JsonbRef<'a>) -> Result<f64, EvalError> {
93 match a.into_datum() {
94 Datum::Numeric(a) => cast_numeric_to_float64(a.into_inner()),
95 datum => Err(EvalError::InvalidJsonbCast {
96 from: jsonb_typeof(JsonbRef::from_datum(datum)).into(),
97 to: "double precision".into(),
98 }),
99 }
100 }
101);
102
103#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
104pub struct CastJsonbToNumeric(pub Option<NumericMaxScale>);
105
106impl<'a> EagerUnaryFunc<'a> for CastJsonbToNumeric {
107 type Input = JsonbRef<'a>;
108 type Output = Result<Numeric, EvalError>;
109
110 fn call(&self, a: JsonbRef<'a>) -> Result<Numeric, EvalError> {
111 match a.into_datum() {
112 Datum::Numeric(mut num) => match self.0 {
113 None => Ok(num.into_inner()),
114 Some(scale) => {
115 if numeric::rescale(&mut num.0, scale.into_u8()).is_err() {
116 return Err(EvalError::NumericFieldOverflow);
117 };
118 Ok(num.into_inner())
119 }
120 },
121 datum => Err(EvalError::InvalidJsonbCast {
122 from: jsonb_typeof(JsonbRef::from_datum(datum)).into(),
123 to: "numeric".into(),
124 }),
125 }
126 }
127
128 fn output_type(&self, input: SqlColumnType) -> SqlColumnType {
129 SqlScalarType::Numeric { max_scale: self.0 }.nullable(input.nullable)
130 }
131
132 fn is_monotone(&self) -> bool {
133 true
134 }
135}
136
137impl fmt::Display for CastJsonbToNumeric {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 f.write_str("jsonb_to_numeric")
140 }
141}
142
143sqlfunc!(
144 #[sqlname = "jsonb_to_boolean"]
145 #[is_monotone = true]
146 fn cast_jsonb_to_bool<'a>(a: JsonbRef<'a>) -> Result<bool, EvalError> {
147 match a.into_datum() {
148 Datum::True => Ok(true),
149 Datum::False => Ok(false),
150 datum => Err(EvalError::InvalidJsonbCast {
151 from: jsonb_typeof(JsonbRef::from_datum(datum)).into(),
152 to: "boolean".into(),
153 }),
154 }
155 }
156);
157
158sqlfunc!(
159 #[sqlname = "jsonbable_to_jsonb"]
160 fn cast_jsonbable_to_jsonb<'a>(a: JsonbRef<'a>) -> JsonbRef<'a> {
161 match a.into_datum() {
162 Datum::Numeric(n) => {
163 let n = n.into_inner();
164 let datum = if n.is_finite() {
165 Datum::from(n)
166 } else if n.is_nan() {
167 Datum::String("NaN")
168 } else if n.is_negative() {
169 Datum::String("-Infinity")
170 } else {
171 Datum::String("Infinity")
172 };
173 JsonbRef::from_datum(datum)
174 }
175 datum => JsonbRef::from_datum(datum),
176 }
177 }
178);
179
180sqlfunc!(
181 fn jsonb_array_length<'a>(a: JsonbRef<'a>) -> Result<Option<i32>, EvalError> {
182 match a.into_datum() {
183 Datum::List(list) => {
184 let count = list.iter().count();
185 match i32::try_from(count) {
186 Ok(len) => Ok(Some(len)),
187 Err(_) => Err(EvalError::Int32OutOfRange(count.to_string().into())),
188 }
189 }
190 _ => Ok(None),
191 }
192 }
193);
194
195sqlfunc!(
196 fn jsonb_typeof<'a>(a: JsonbRef<'a>) -> &'a str {
197 match a.into_datum() {
198 Datum::Map(_) => "object",
199 Datum::List(_) => "array",
200 Datum::String(_) => "string",
201 Datum::Numeric(_) => "number",
202 Datum::True | Datum::False => "boolean",
203 Datum::JsonNull => "null",
204 d => panic!("Not jsonb: {:?}", d),
205 }
206 }
207);
208
209sqlfunc!(
210 fn jsonb_strip_nulls<'a>(a: JsonbRef<'a>) -> Jsonb {
211 fn strip_nulls(a: Datum, row: &mut RowPacker) {
212 match a {
213 Datum::Map(dict) => row.push_dict_with(|row| {
214 for (k, v) in dict.iter() {
215 match v {
216 Datum::JsonNull => (),
217 _ => {
218 row.push(Datum::String(k));
219 strip_nulls(v, row);
220 }
221 }
222 }
223 }),
224 Datum::List(list) => row.push_list_with(|row| {
225 for elem in list.iter() {
226 strip_nulls(elem, row);
227 }
228 }),
229 _ => row.push(a),
230 }
231 }
232 let mut row = Row::default();
233 strip_nulls(a.into_datum(), &mut row.packer());
234 Jsonb::from_row(row)
235 }
236);
237
238sqlfunc!(
239 fn jsonb_pretty<'a>(a: JsonbRef<'a>) -> String {
240 let mut buf = String::new();
241 strconv::format_jsonb_pretty(&mut buf, a);
242 buf
243 }
244);