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::{Result, ScalarValue};
21use arrow::array::{ArrayRef, StructArray};
22use arrow::datatypes::{DataType, Field, FieldRef, Fields};
23use std::sync::Arc;
24
25/// Builder for [`ScalarValue::Struct`].
26///
27/// See examples on [`ScalarValue`]
28#[derive(Debug, Default)]
29pub struct ScalarStructBuilder {
30 fields: Vec<FieldRef>,
31 arrays: Vec<ArrayRef>,
32}
33
34impl ScalarStructBuilder {
35 /// Create a new `ScalarStructBuilder`
36 pub fn new() -> Self {
37 Self::default()
38 }
39
40 /// Return a new [`ScalarValue::Struct`] with a single `null` value.
41 ///
42 /// Note this is different from a struct where each of the specified fields
43 /// are null (e.g. `{a: NULL}`)
44 ///
45 /// # Example
46 ///
47 /// ```rust
48 /// # use arrow::datatypes::{DataType, Field};
49 /// # use datafusion_common::scalar::ScalarStructBuilder;
50 /// let fields = vec![
51 /// Field::new("a", DataType::Int32, false),
52 /// ];
53 /// let sv = ScalarStructBuilder::new_null(fields);
54 /// // Note this is `NULL`, not `{a: NULL}`
55 /// assert_eq!(format!("{sv}"), "NULL");
56 ///```
57 ///
58 /// To create a struct where the *fields* are null, use `Self::new()` and
59 /// pass null values for each field:
60 ///
61 /// ```rust
62 /// # use arrow::datatypes::{DataType, Field};
63 /// # use datafusion_common::scalar::{ScalarStructBuilder, ScalarValue};
64 /// // make a nullable field
65 /// let field = Field::new("a", DataType::Int32, true);
66 /// // add a null value for the "a" field
67 /// let sv = ScalarStructBuilder::new()
68 /// .with_scalar(field, ScalarValue::Int32(None))
69 /// .build()
70 /// .unwrap();
71 /// // value is not null, but field is
72 /// assert_eq!(format!("{sv}"), "{a:}");
73 /// ```
74 pub fn new_null(fields: impl IntoFields) -> ScalarValue {
75 DataType::Struct(fields.into()).try_into().unwrap()
76 }
77
78 /// Add the specified field and [`ArrayRef`] to the struct.
79 ///
80 /// Note the array should have a single row.
81 pub fn with_array(mut self, field: impl IntoFieldRef, value: ArrayRef) -> Self {
82 self.fields.push(field.into_field_ref());
83 self.arrays.push(value);
84 self
85 }
86
87 /// Add the specified field and `ScalarValue` to the struct.
88 pub fn with_scalar(self, field: impl IntoFieldRef, value: ScalarValue) -> Self {
89 // valid scalar value should not fail
90 let array = value.to_array().unwrap();
91 self.with_array(field, array)
92 }
93
94 /// Add a field with the specified name and value to the struct.
95 /// the field is created with the specified data type and as non nullable
96 pub fn with_name_and_scalar(self, name: &str, value: ScalarValue) -> Self {
97 let field = Field::new(name, value.data_type(), false);
98 self.with_scalar(field, value)
99 }
100
101 /// Return a [`ScalarValue::Struct`] with the fields and values added so far
102 ///
103 /// # Errors
104 ///
105 /// If the [`StructArray`] cannot be created (for example if there is a
106 /// mismatch between field types and arrays) or the arrays do not have
107 /// exactly one element.
108 pub fn build(self) -> Result<ScalarValue> {
109 let Self { fields, arrays } = self;
110
111 let struct_array =
112 StructArray::try_new_with_length(Fields::from(fields), arrays, None, 1)?;
113 Ok(ScalarValue::Struct(Arc::new(struct_array)))
114 }
115}
116
117/// Trait for converting a type into a [`FieldRef`]
118///
119/// Used to avoid having to call `clone()` on a `FieldRef` when adding a field to
120/// a `ScalarStructBuilder`.
121///
122/// TODO potentially upstream this to arrow-rs so that we can
123/// use impl `Into<FieldRef>` instead
124pub trait IntoFieldRef {
125 fn into_field_ref(self) -> FieldRef;
126}
127
128impl IntoFieldRef for FieldRef {
129 fn into_field_ref(self) -> FieldRef {
130 self
131 }
132}
133
134impl IntoFieldRef for &FieldRef {
135 fn into_field_ref(self) -> FieldRef {
136 Arc::clone(self)
137 }
138}
139
140impl IntoFieldRef for Field {
141 fn into_field_ref(self) -> FieldRef {
142 FieldRef::new(self)
143 }
144}
145
146/// Trait for converting a type into a [`Fields`]
147///
148/// This avoids to avoid having to call clone() on an Arc'd `Fields` when adding
149/// a field to a `ScalarStructBuilder`
150///
151/// TODO potentially upstream this to arrow-rs so that we can
152/// use impl `Into<Fields>` instead
153pub trait IntoFields {
154 fn into(self) -> Fields;
155}
156
157impl IntoFields for Fields {
158 fn into(self) -> Fields {
159 self
160 }
161}
162
163impl IntoFields for &Fields {
164 fn into(self) -> Fields {
165 self.clone()
166 }
167}
168
169impl IntoFields for Vec<Field> {
170 fn into(self) -> Fields {
171 Fields::from(self)
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 // Other cases are tested by doc tests
180 #[test]
181 fn test_empty_struct() {
182 let sv = ScalarStructBuilder::new().build().unwrap();
183 assert_eq!(format!("{sv}"), "{}");
184 }
185}