micromegas_datafusion_extensions/jsonb/
array_length.rs1use datafusion::arrow::array::{Array, DictionaryArray, GenericBinaryArray, Int64Array};
2use datafusion::arrow::datatypes::{DataType, Int32Type};
3use datafusion::common::{Result, internal_err};
4use datafusion::error::DataFusionError;
5use datafusion::logical_expr::{
6 ColumnarValue, ScalarFunctionArgs, ScalarUDF, ScalarUDFImpl, Signature, Volatility,
7};
8use jsonb::RawJsonb;
9use std::sync::Arc;
10
11#[derive(Debug, PartialEq, Eq, Hash)]
16pub struct JsonbArrayLength {
17 signature: Signature,
18}
19
20impl JsonbArrayLength {
21 pub fn new() -> Self {
22 Self {
23 signature: Signature::any(1, Volatility::Immutable),
24 }
25 }
26}
27
28impl Default for JsonbArrayLength {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34fn extract_array_length_from_jsonb(jsonb_bytes: &[u8]) -> Result<Option<i64>> {
35 let jsonb = RawJsonb::new(jsonb_bytes);
36 match jsonb.array_length() {
37 Ok(Some(len)) => Ok(Some(len as i64)),
38 Ok(None) => Ok(None),
39 Err(e) => Err(DataFusionError::External(e.into())),
40 }
41}
42
43impl ScalarUDFImpl for JsonbArrayLength {
44 fn name(&self) -> &str {
45 "jsonb_array_length"
46 }
47
48 fn signature(&self) -> &Signature {
49 &self.signature
50 }
51
52 fn return_type(&self, _args: &[DataType]) -> Result<DataType> {
53 Ok(DataType::Int64)
54 }
55
56 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
57 let args = ColumnarValue::values_to_arrays(&args.args)?;
58 if args.len() != 1 {
59 return internal_err!("wrong number of arguments to jsonb_array_length()");
60 }
61
62 match args[0].data_type() {
63 DataType::Binary => {
64 let binary_array = args[0]
65 .as_any()
66 .downcast_ref::<GenericBinaryArray<i32>>()
67 .ok_or_else(|| {
68 DataFusionError::Internal("error casting to binary array".into())
69 })?;
70
71 let mut builder = Int64Array::builder(binary_array.len());
72 for i in 0..binary_array.len() {
73 if binary_array.is_null(i) {
74 builder.append_null();
75 } else {
76 let jsonb_bytes = binary_array.value(i);
77 if let Some(value) = extract_array_length_from_jsonb(jsonb_bytes)? {
78 builder.append_value(value);
79 } else {
80 builder.append_null();
81 }
82 }
83 }
84 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
85 }
86 DataType::Dictionary(_, value_type)
87 if matches!(value_type.as_ref(), DataType::Binary) =>
88 {
89 let dict_array = args[0]
90 .as_any()
91 .downcast_ref::<DictionaryArray<Int32Type>>()
92 .ok_or_else(|| {
93 DataFusionError::Internal("error casting dictionary array".into())
94 })?;
95
96 let binary_values = dict_array
97 .values()
98 .as_any()
99 .downcast_ref::<GenericBinaryArray<i32>>()
100 .ok_or_else(|| {
101 DataFusionError::Internal("dictionary values are not a binary array".into())
102 })?;
103
104 let mut builder = Int64Array::builder(dict_array.len());
105 for i in 0..dict_array.len() {
106 if dict_array.is_null(i) {
107 builder.append_null();
108 } else {
109 let key_index = dict_array.keys().value(i) as usize;
110 if key_index < binary_values.len() {
111 let jsonb_bytes = binary_values.value(key_index);
112 if let Some(value) = extract_array_length_from_jsonb(jsonb_bytes)? {
113 builder.append_value(value);
114 } else {
115 builder.append_null();
116 }
117 } else {
118 return internal_err!(
119 "Dictionary key index out of bounds in jsonb_array_length"
120 );
121 }
122 }
123 }
124 Ok(ColumnarValue::Array(Arc::new(builder.finish())))
125 }
126 _ => internal_err!(
127 "jsonb_array_length: unsupported input type, expected Binary or Dictionary<Int32, Binary>"
128 ),
129 }
130 }
131}
132
133pub fn make_jsonb_array_length_udf() -> ScalarUDF {
135 ScalarUDF::new_from_impl(JsonbArrayLength::new())
136}