datafusion_common/scalar/
struct_builder.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! [`ScalarStructBuilder`] for building [`ScalarValue::Struct`]
19
20use crate::error::_internal_err;
21use crate::{Result, ScalarValue};
22use arrow::array::{ArrayRef, StructArray};
23use arrow::datatypes::{DataType, Field, FieldRef, Fields};
24use std::sync::Arc;
25
26/// Builder for [`ScalarValue::Struct`].
27///
28/// See examples on [`ScalarValue`]
29#[derive(Debug, Default)]
30pub struct ScalarStructBuilder {
31    fields: Vec<FieldRef>,
32    arrays: Vec<ArrayRef>,
33}
34
35impl ScalarStructBuilder {
36    /// Create a new `ScalarStructBuilder`
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Return a new [`ScalarValue::Struct`] with a single `null` value.
42    ///
43    /// Note this is different from a struct where each of the specified fields
44    /// are null (e.g. `{a: NULL}`)
45    ///
46    /// # Example
47    ///
48    /// ```rust
49    /// # use arrow::datatypes::{DataType, Field};
50    /// # use datafusion_common::scalar::ScalarStructBuilder;
51    /// let fields = vec![
52    ///    Field::new("a", DataType::Int32, false),
53    /// ];
54    /// let sv = ScalarStructBuilder::new_null(fields);
55    /// // Note this is `NULL`, not `{a: NULL}`
56    /// assert_eq!(format!("{sv}"), "NULL");
57    ///```
58    ///
59    /// To create a struct where the *fields* are null, use `Self::new()` and
60    /// pass null values for each field:
61    ///
62    /// ```rust
63    /// # use arrow::datatypes::{DataType, Field};
64    /// # use datafusion_common::scalar::{ScalarStructBuilder, ScalarValue};
65    /// // make a nullable field
66    /// let field = Field::new("a", DataType::Int32, true);
67    /// // add a null value for the "a" field
68    /// let sv = ScalarStructBuilder::new()
69    ///   .with_scalar(field, ScalarValue::Int32(None))
70    ///   .build()
71    ///   .unwrap();
72    /// // value is not null, but field is
73    /// assert_eq!(format!("{sv}"), "{a:}");
74    /// ```
75    pub fn new_null(fields: impl IntoFields) -> ScalarValue {
76        DataType::Struct(fields.into()).try_into().unwrap()
77    }
78
79    /// Add the specified field and [`ArrayRef`] to the struct.
80    ///
81    /// Note the array should have a single row.
82    pub fn with_array(mut self, field: impl IntoFieldRef, value: ArrayRef) -> Self {
83        self.fields.push(field.into_field_ref());
84        self.arrays.push(value);
85        self
86    }
87
88    /// Add the specified field and `ScalarValue` to the struct.
89    pub fn with_scalar(self, field: impl IntoFieldRef, value: ScalarValue) -> Self {
90        // valid scalar value should not fail
91        let array = value.to_array().unwrap();
92        self.with_array(field, array)
93    }
94
95    /// Add a field with the specified name and value to the struct.
96    /// the field is created with the specified data type and as non nullable
97    pub fn with_name_and_scalar(self, name: &str, value: ScalarValue) -> Self {
98        let field = Field::new(name, value.data_type(), false);
99        self.with_scalar(field, value)
100    }
101
102    /// Return a [`ScalarValue::Struct`] with the fields and values added so far
103    ///
104    /// # Errors
105    ///
106    /// If the [`StructArray`] cannot be created (for example if there is a
107    /// mismatch between field types and arrays) or the arrays do not have
108    /// exactly one element.
109    pub fn build(self) -> Result<ScalarValue> {
110        let Self { fields, arrays } = self;
111
112        for array in &arrays {
113            if array.len() != 1 {
114                return _internal_err!(
115                    "Error building ScalarValue::Struct. \
116                Expected array with exactly one element, found array with {} elements",
117                    array.len()
118                );
119            }
120        }
121
122        let struct_array = StructArray::try_new(Fields::from(fields), arrays, None)?;
123        Ok(ScalarValue::Struct(Arc::new(struct_array)))
124    }
125}
126
127/// Trait for converting a type into a [`FieldRef`]
128///
129/// Used to avoid having to call `clone()` on a `FieldRef` when adding a field to
130/// a `ScalarStructBuilder`.
131///
132/// TODO potentially upstream this to arrow-rs so that we can
133/// use impl `Into<FieldRef>` instead
134pub trait IntoFieldRef {
135    fn into_field_ref(self) -> FieldRef;
136}
137
138impl IntoFieldRef for FieldRef {
139    fn into_field_ref(self) -> FieldRef {
140        self
141    }
142}
143
144impl IntoFieldRef for &FieldRef {
145    fn into_field_ref(self) -> FieldRef {
146        Arc::clone(self)
147    }
148}
149
150impl IntoFieldRef for Field {
151    fn into_field_ref(self) -> FieldRef {
152        FieldRef::new(self)
153    }
154}
155
156/// Trait for converting a type into a [`Fields`]
157///
158/// This avoids to avoid having to call clone() on an Arc'd `Fields` when adding
159/// a field to a `ScalarStructBuilder`
160///
161/// TODO potentially upstream this to arrow-rs so that we can
162/// use impl `Into<Fields>` instead
163pub trait IntoFields {
164    fn into(self) -> Fields;
165}
166
167impl IntoFields for Fields {
168    fn into(self) -> Fields {
169        self
170    }
171}
172
173impl IntoFields for &Fields {
174    fn into(self) -> Fields {
175        self.clone()
176    }
177}
178
179impl IntoFields for Vec<Field> {
180    fn into(self) -> Fields {
181        Fields::from(self)
182    }
183}