micromegas_datafusion_extensions/jsonb/
cast.rs1use datafusion::arrow::array::{
2 Array, DictionaryArray, Float64Array, GenericBinaryArray, Int64Array, StringDictionaryBuilder,
3};
4use datafusion::arrow::datatypes::{DataType, Int32Type};
5use datafusion::common::{Result, internal_err};
6use datafusion::error::DataFusionError;
7use datafusion::logical_expr::{
8 ColumnarValue, ScalarFunctionArgs, ScalarUDF, ScalarUDFImpl, Signature, Volatility,
9};
10use jsonb::RawJsonb;
11use std::sync::Arc;
12
13#[derive(Debug, PartialEq, Eq, Hash)]
18pub struct JsonbAsString {
19 signature: Signature,
20}
21
22impl JsonbAsString {
23 pub fn new() -> Self {
24 Self {
25 signature: Signature::any(1, Volatility::Immutable),
26 }
27 }
28}
29
30impl Default for JsonbAsString {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36fn extract_string_from_jsonb(jsonb_bytes: &[u8]) -> Result<Option<String>> {
37 let jsonb = RawJsonb::new(jsonb_bytes);
38 match jsonb.as_str() {
39 Ok(Some(value)) => Ok(Some(value.to_string())),
40 Ok(None) => Ok(None),
41 Err(e) => Err(DataFusionError::External(e.into())),
42 }
43}
44
45impl ScalarUDFImpl for JsonbAsString {
46 fn name(&self) -> &str {
47 "jsonb_as_string"
48 }
49
50 fn signature(&self) -> &Signature {
51 &self.signature
52 }
53
54 fn return_type(&self, _args: &[DataType]) -> Result<DataType> {
55 Ok(DataType::Dictionary(
56 Box::new(DataType::Int32),
57 Box::new(DataType::Utf8),
58 ))
59 }
60
61 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
62 let args = ColumnarValue::values_to_arrays(&args.args)?;
63 if args.len() != 1 {
64 return internal_err!("wrong number of arguments to jsonb_as_string()");
65 }
66
67 match args[0].data_type() {
68 DataType::Binary => {
69 let binary_array = args[0]
70 .as_any()
71 .downcast_ref::<GenericBinaryArray<i32>>()
72 .ok_or_else(|| {
73 DataFusionError::Internal("error casting to binary array".into())
74 })?;
75
76 let mut dict_builder = StringDictionaryBuilder::<Int32Type>::new();
77 for i in 0..binary_array.len() {
78 if binary_array.is_null(i) {
79 dict_builder.append_null();
80 } else {
81 let jsonb_bytes = binary_array.value(i);
82 if let Some(value) = extract_string_from_jsonb(jsonb_bytes)? {
83 dict_builder.append_value(value);
84 } else {
85 dict_builder.append_null();
86 }
87 }
88 }
89 Ok(ColumnarValue::Array(Arc::new(dict_builder.finish())))
90 }
91 DataType::Dictionary(_, value_type)
92 if matches!(value_type.as_ref(), DataType::Binary) =>
93 {
94 let dict_array = args[0]
95 .as_any()
96 .downcast_ref::<DictionaryArray<Int32Type>>()
97 .ok_or_else(|| {
98 DataFusionError::Internal("error casting dictionary array".into())
99 })?;
100
101 let binary_values = dict_array
102 .values()
103 .as_any()
104 .downcast_ref::<GenericBinaryArray<i32>>()
105 .ok_or_else(|| {
106 DataFusionError::Internal("dictionary values are not a binary array".into())
107 })?;
108
109 let mut dict_builder = StringDictionaryBuilder::<Int32Type>::new();
110 for i in 0..dict_array.len() {
111 if dict_array.is_null(i) {
112 dict_builder.append_null();
113 } else {
114 let key_index = dict_array.keys().value(i) as usize;
115 if key_index < binary_values.len() {
116 let jsonb_bytes = binary_values.value(key_index);
117 if let Some(value) = extract_string_from_jsonb(jsonb_bytes)? {
118 dict_builder.append_value(value);
119 } else {
120 dict_builder.append_null();
121 }
122 } else {
123 return internal_err!(
124 "Dictionary key index out of bounds in jsonb_as_string"
125 );
126 }
127 }
128 }
129 Ok(ColumnarValue::Array(Arc::new(dict_builder.finish())))
130 }
131 _ => internal_err!(
132 "jsonb_as_string: unsupported input type, expected Binary or Dictionary<Int32, Binary>"
133 ),
134 }
135 }
136}
137
138pub fn make_jsonb_as_string_udf() -> ScalarUDF {
140 ScalarUDF::new_from_impl(JsonbAsString::new())
141}
142
143#[derive(Debug, PartialEq, Eq, Hash)]
148pub struct JsonbAsF64 {
149 signature: Signature,
150}
151
152impl JsonbAsF64 {
153 pub fn new() -> Self {
154 Self {
155 signature: Signature::any(1, Volatility::Immutable),
156 }
157 }
158}
159
160impl Default for JsonbAsF64 {
161 fn default() -> Self {
162 Self::new()
163 }
164}
165
166fn extract_f64_from_jsonb(jsonb_bytes: &[u8]) -> Result<Option<f64>> {
167 let jsonb = RawJsonb::new(jsonb_bytes);
168 match jsonb.as_f64() {
169 Ok(value) => Ok(value),
170 Err(e) => Err(DataFusionError::External(e.into())),
171 }
172}
173
174impl ScalarUDFImpl for JsonbAsF64 {
175 fn name(&self) -> &str {
176 "jsonb_as_f64"
177 }
178
179 fn signature(&self) -> &Signature {
180 &self.signature
181 }
182
183 fn return_type(&self, _args: &[DataType]) -> Result<DataType> {
184 Ok(DataType::Float64)
185 }
186
187 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
188 let args = ColumnarValue::values_to_arrays(&args.args)?;
189 if args.len() != 1 {
190 return internal_err!("wrong number of arguments to jsonb_as_f64()");
191 }
192
193 match args[0].data_type() {
194 DataType::Binary => {
195 let binary_array = args[0]
196 .as_any()
197 .downcast_ref::<GenericBinaryArray<i32>>()
198 .ok_or_else(|| {
199 DataFusionError::Internal("error casting to binary array".into())
200 })?;
201
202 let mut builder = Float64Array::builder(binary_array.len());
203 for i in 0..binary_array.len() {
204 if binary_array.is_null(i) {
205 builder.append_null();
206 } else {
207 let jsonb_bytes = binary_array.value(i);
208 if let Some(value) = extract_f64_from_jsonb(jsonb_bytes)? {
209 builder.append_value(value);
210 } else {
211 builder.append_null();
212 }
213 }
214 }
215 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
216 }
217 DataType::Dictionary(_, value_type)
218 if matches!(value_type.as_ref(), DataType::Binary) =>
219 {
220 let dict_array = args[0]
221 .as_any()
222 .downcast_ref::<DictionaryArray<Int32Type>>()
223 .ok_or_else(|| {
224 DataFusionError::Internal("error casting dictionary array".into())
225 })?;
226
227 let binary_values = dict_array
228 .values()
229 .as_any()
230 .downcast_ref::<GenericBinaryArray<i32>>()
231 .ok_or_else(|| {
232 DataFusionError::Internal("dictionary values are not a binary array".into())
233 })?;
234
235 let mut builder = Float64Array::builder(dict_array.len());
236 for i in 0..dict_array.len() {
237 if dict_array.is_null(i) {
238 builder.append_null();
239 } else {
240 let key_index = dict_array.keys().value(i) as usize;
241 if key_index < binary_values.len() {
242 let jsonb_bytes = binary_values.value(key_index);
243 if let Some(value) = extract_f64_from_jsonb(jsonb_bytes)? {
244 builder.append_value(value);
245 } else {
246 builder.append_null();
247 }
248 } else {
249 return internal_err!(
250 "Dictionary key index out of bounds in jsonb_as_f64"
251 );
252 }
253 }
254 }
255 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
256 }
257 _ => internal_err!(
258 "jsonb_as_f64: unsupported input type, expected Binary or Dictionary<Int32, Binary>"
259 ),
260 }
261 }
262}
263
264pub fn make_jsonb_as_f64_udf() -> ScalarUDF {
266 ScalarUDF::new_from_impl(JsonbAsF64::new())
267}
268
269#[derive(Debug, PartialEq, Eq, Hash)]
274pub struct JsonbAsI64 {
275 signature: Signature,
276}
277
278impl JsonbAsI64 {
279 pub fn new() -> Self {
280 Self {
281 signature: Signature::any(1, Volatility::Immutable),
282 }
283 }
284}
285
286impl Default for JsonbAsI64 {
287 fn default() -> Self {
288 Self::new()
289 }
290}
291
292fn extract_i64_from_jsonb(jsonb_bytes: &[u8]) -> Result<Option<i64>> {
293 let jsonb = RawJsonb::new(jsonb_bytes);
294 match jsonb.as_i64() {
295 Ok(value) => Ok(value),
296 Err(e) => Err(DataFusionError::External(e.into())),
297 }
298}
299
300impl ScalarUDFImpl for JsonbAsI64 {
301 fn name(&self) -> &str {
302 "jsonb_as_i64"
303 }
304
305 fn signature(&self) -> &Signature {
306 &self.signature
307 }
308
309 fn return_type(&self, _args: &[DataType]) -> Result<DataType> {
310 Ok(DataType::Int64)
311 }
312
313 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
314 let args = ColumnarValue::values_to_arrays(&args.args)?;
315 if args.len() != 1 {
316 return internal_err!("wrong number of arguments to jsonb_as_i64()");
317 }
318
319 match args[0].data_type() {
320 DataType::Binary => {
321 let binary_array = args[0]
322 .as_any()
323 .downcast_ref::<GenericBinaryArray<i32>>()
324 .ok_or_else(|| {
325 DataFusionError::Internal("error casting to binary array".into())
326 })?;
327
328 let mut builder = Int64Array::builder(binary_array.len());
329 for i in 0..binary_array.len() {
330 if binary_array.is_null(i) {
331 builder.append_null();
332 } else {
333 let jsonb_bytes = binary_array.value(i);
334 if let Some(value) = extract_i64_from_jsonb(jsonb_bytes)? {
335 builder.append_value(value);
336 } else {
337 builder.append_null();
338 }
339 }
340 }
341 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
342 }
343 DataType::Dictionary(_, value_type)
344 if matches!(value_type.as_ref(), DataType::Binary) =>
345 {
346 let dict_array = args[0]
347 .as_any()
348 .downcast_ref::<DictionaryArray<Int32Type>>()
349 .ok_or_else(|| {
350 DataFusionError::Internal("error casting dictionary array".into())
351 })?;
352
353 let binary_values = dict_array
354 .values()
355 .as_any()
356 .downcast_ref::<GenericBinaryArray<i32>>()
357 .ok_or_else(|| {
358 DataFusionError::Internal("dictionary values are not a binary array".into())
359 })?;
360
361 let mut builder = Int64Array::builder(dict_array.len());
362 for i in 0..dict_array.len() {
363 if dict_array.is_null(i) {
364 builder.append_null();
365 } else {
366 let key_index = dict_array.keys().value(i) as usize;
367 if key_index < binary_values.len() {
368 let jsonb_bytes = binary_values.value(key_index);
369 if let Some(value) = extract_i64_from_jsonb(jsonb_bytes)? {
370 builder.append_value(value);
371 } else {
372 builder.append_null();
373 }
374 } else {
375 return internal_err!(
376 "Dictionary key index out of bounds in jsonb_as_i64"
377 );
378 }
379 }
380 }
381 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
382 }
383 _ => internal_err!(
384 "jsonb_as_i64: unsupported input type, expected Binary or Dictionary<Int32, Binary>"
385 ),
386 }
387 }
388}
389
390pub fn make_jsonb_as_i64_udf() -> ScalarUDF {
392 ScalarUDF::new_from_impl(JsonbAsI64::new())
393}