Skip to main content

datafusion_variant/
variant_list_insert.rs

1use std::sync::Arc;
2
3use arrow::array::StructArray;
4use arrow_schema::{DataType, Field, Fields};
5use datafusion::{
6    common::exec_err,
7    error::{DataFusionError, Result},
8    logical_expr::{
9        ColumnarValue, ReturnFieldArgs, ScalarUDFImpl, Signature, TypeSignature, Volatility,
10    },
11    scalar::ScalarValue,
12};
13use parquet_variant::{Variant, VariantBuilder};
14use parquet_variant_compute::{VariantArray, VariantType};
15
16use crate::shared::{ensure, try_parse_variant_scalar};
17
18#[derive(Debug, Hash, PartialEq, Eq)]
19pub struct VariantListInsert {
20    signature: Signature,
21}
22
23impl Default for VariantListInsert {
24    fn default() -> Self {
25        Self {
26            signature: Signature::new(TypeSignature::Any(2), Volatility::Immutable),
27        }
28    }
29}
30
31impl ScalarUDFImpl for VariantListInsert {
32    fn as_any(&self) -> &dyn std::any::Any {
33        self
34    }
35
36    fn name(&self) -> &str {
37        "variant_list_insert"
38    }
39
40    fn signature(&self) -> &Signature {
41        &self.signature
42    }
43
44    fn return_type(&self, _arg_types: &[arrow_schema::DataType]) -> Result<arrow_schema::DataType> {
45        Err(DataFusionError::Internal(
46            "implemented return_field_from_args instead".into(),
47        ))
48    }
49
50    fn return_field_from_args(&self, _args: ReturnFieldArgs) -> Result<Arc<Field>> {
51        let data_type = DataType::Struct(Fields::from(vec![
52            Field::new("metadata", DataType::BinaryView, false),
53            Field::new("value", DataType::BinaryView, false),
54        ]));
55
56        Ok(Arc::new(
57            Field::new(self.name(), data_type, true).with_extension_type(VariantType),
58        ))
59    }
60
61    fn invoke_with_args(
62        &self,
63        args: datafusion::logical_expr::ScalarFunctionArgs,
64    ) -> Result<ColumnarValue> {
65        let argument_fields = args.arg_fields;
66        let argument_values = args.args;
67
68        ensure(
69            argument_fields.len() == argument_values.len(),
70            "argument fields and values must be of same length",
71        )?;
72
73        let [variant_list_to_update, element_to_append] = argument_values.as_slice() else {
74            return exec_err!("expected 2 arguments");
75        };
76
77        let all_arguments_variant_field = argument_fields
78            .iter()
79            .all(|f| matches!(f.extension_type(), VariantType));
80
81        ensure(
82            all_arguments_variant_field,
83            "expected all arguments to have the VariantType extension",
84        )?;
85
86        match (variant_list_to_update, element_to_append) {
87            (
88                ColumnarValue::Scalar(scalar_variant_list),
89                ColumnarValue::Scalar(scalar_element_to_append),
90            ) => {
91                let variant_list = try_parse_variant_scalar(scalar_variant_list)?;
92                let variant_list = variant_list.value(0);
93
94                let variant_to_insert = try_parse_variant_scalar(scalar_element_to_append)?;
95                let variant_to_insert = variant_to_insert.value(0);
96
97                let out: StructArray = {
98                    let (m, v) = create_variant_list_with_new_elements(
99                        variant_list,
100                        [variant_to_insert].into_iter(),
101                    )?;
102
103                    VariantArray::from_iter([Variant::new(m.as_ref(), v.as_ref())]).into()
104                };
105
106                Ok(ColumnarValue::Scalar(ScalarValue::Struct(Arc::new(out))))
107            }
108            (
109                ColumnarValue::Array(variant_lists),
110                ColumnarValue::Scalar(scalar_element_to_append),
111            ) => {
112                let variant_array = VariantArray::try_new(&variant_lists)?;
113
114                let variant_to_insert = try_parse_variant_scalar(scalar_element_to_append)?;
115                let variant_to_insert = variant_to_insert.value(0);
116
117                // note: grr lifetimes...
118                let buffers = variant_array
119                    .iter()
120                    .map(|v| {
121                        v.map(|v| {
122                            create_variant_list_with_new_elements(
123                                v,
124                                [variant_to_insert.clone()].into_iter(),
125                            )
126                        })
127                        .transpose()
128                    })
129                    .collect::<Result<Vec<_>>>()?;
130
131                let variants = buffers
132                    .iter()
133                    .map(|opt| {
134                        opt.as_ref()
135                            .map(|(m, v)| Variant::new(m.as_ref(), v.as_ref()))
136                    })
137                    .collect::<Vec<_>>();
138
139                let out: StructArray = VariantArray::from_iter(variants).into();
140
141                Ok(ColumnarValue::Array(Arc::new(out) as _))
142            }
143
144            (ColumnarValue::Array(variant_lists), ColumnarValue::Array(elements_to_append)) => {
145                let variant_lists = VariantArray::try_new(&variant_lists)?;
146                let elements_to_append = VariantArray::try_new(&elements_to_append)?;
147
148                ensure(
149                    variant_lists.len() == elements_to_append.len(),
150                    "expected arguments to have the same length",
151                )?;
152
153                let mut buffers = Vec::with_capacity(variant_lists.len());
154
155                for (variant_list_to_update, element_to_append) in
156                    variant_lists.iter().zip(elements_to_append.iter())
157                {
158                    match (variant_list_to_update, element_to_append) {
159                        (Some(variant_list), Some(element_to_append)) => {
160                            let (m, v) = create_variant_list_with_new_elements(
161                                variant_list,
162                                [element_to_append].into_iter(),
163                            )?;
164
165                            buffers.push(Some((m, v)));
166                        }
167                        (None, None) => buffers.push(None),
168                        (Some(variant_list), None) => {
169                            let mut v = VariantBuilder::new();
170                            v.append_value(variant_list);
171
172                            buffers.push(Some(v.finish()))
173                        }
174                        (None, Some(element_to_append)) => {
175                            let mut v = VariantBuilder::new();
176                            let mut l = v.new_list();
177
178                            l.append_value(element_to_append);
179                            l.finish();
180
181                            buffers.push(Some(v.finish()));
182                        }
183                    };
184                }
185
186                let variants = buffers
187                    .iter()
188                    .map(|opt| {
189                        opt.as_ref()
190                            .map(|(m, v)| Variant::new(m.as_ref(), v.as_ref()))
191                    })
192                    .collect::<Vec<_>>();
193
194                let out: StructArray = VariantArray::from_iter(variants).into();
195
196                Ok(ColumnarValue::Array(Arc::new(out) as _))
197            }
198            (ColumnarValue::Scalar(_), ColumnarValue::Array(_)) => {
199                exec_err!("unsupported argument")
200            }
201        }
202    }
203}
204
205// note: I wonder if we can abstract this away
206// it would be good to profile and see if this pocket of code is slow
207fn create_variant_list_with_new_elements<'m, 'v>(
208    variant_list: Variant,
209    elements_to_insert: impl Iterator<Item = Variant<'m, 'v>>,
210) -> Result<(Vec<u8>, Vec<u8>)> {
211    let Variant::List(variant_list) = variant_list else {
212        return exec_err!("expected variant list");
213    };
214
215    // note: I wonder if we can abstract this away
216    // it would be good to profile and see if this pocket of code is slow
217    let mut v = VariantBuilder::new();
218    let mut l = v.new_list();
219
220    l.extend(variant_list.iter());
221
222    for v in elements_to_insert {
223        l.append_value(v);
224    }
225
226    l.finish();
227
228    Ok(v.finish())
229}