• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16935267080

13 Aug 2025 11:00AM UTC coverage: 24.312% (-63.3%) from 87.658%
16935267080

Pull #4226

github

web-flow
Merge 81b48c7fb into baa6ea202
Pull Request #4226: Support converting TimestampTZ to and from duckdb

0 of 2 new or added lines in 1 file covered. (0.0%)

20666 existing lines in 469 files now uncovered.

8726 of 35892 relevant lines covered (24.31%)

147.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/vortex-array/src/builders/struct_.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::any::Any;
5

6
use itertools::Itertools;
7
use vortex_dtype::{DType, Nullability, StructFields};
8
use vortex_error::{VortexExpect, VortexResult, vortex_bail};
9
use vortex_mask::Mask;
10
use vortex_scalar::StructScalar;
11

12
use crate::arrays::StructArray;
13
use crate::builders::lazy_validity_builder::LazyNullBufferBuilder;
14
use crate::builders::{ArrayBuilder, ArrayBuilderExt, builder_with_capacity};
15
use crate::{Array, ArrayRef, IntoArray, ToCanonical};
16

17
pub struct StructBuilder {
18
    builders: Vec<Box<dyn ArrayBuilder>>,
19
    validity: LazyNullBufferBuilder,
20
    struct_dtype: StructFields,
21
    nullability: Nullability,
22
    dtype: DType,
23
}
24

25
impl StructBuilder {
UNCOV
26
    pub fn with_capacity(
×
UNCOV
27
        struct_dtype: StructFields,
×
UNCOV
28
        nullability: Nullability,
×
UNCOV
29
        capacity: usize,
×
UNCOV
30
    ) -> Self {
×
UNCOV
31
        let builders = struct_dtype
×
UNCOV
32
            .fields()
×
UNCOV
33
            .map(|dt| builder_with_capacity(&dt, capacity))
×
UNCOV
34
            .collect();
×
35

UNCOV
36
        Self {
×
UNCOV
37
            builders,
×
UNCOV
38
            validity: LazyNullBufferBuilder::new(capacity),
×
UNCOV
39
            struct_dtype: struct_dtype.clone(),
×
UNCOV
40
            nullability,
×
UNCOV
41
            dtype: DType::Struct(struct_dtype, nullability),
×
UNCOV
42
        }
×
UNCOV
43
    }
×
44

UNCOV
45
    pub fn append_value(&mut self, struct_scalar: StructScalar) -> VortexResult<()> {
×
UNCOV
46
        if struct_scalar.dtype() != &DType::Struct(self.struct_dtype.clone(), self.nullability) {
×
47
            vortex_bail!(
×
48
                "Expected struct scalar with dtype {:?}, found {:?}",
×
49
                self.struct_dtype,
50
                struct_scalar.dtype()
×
51
            )
UNCOV
52
        }
×
53

UNCOV
54
        if let Some(fields) = struct_scalar.fields() {
×
UNCOV
55
            for (builder, field) in self.builders.iter_mut().zip_eq(fields) {
×
UNCOV
56
                builder.append_scalar(&field)?;
×
57
            }
UNCOV
58
            self.validity.append_non_null();
×
59
        } else {
60
            self.append_null()
×
61
        }
62

UNCOV
63
        Ok(())
×
UNCOV
64
    }
×
65
}
66

67
impl ArrayBuilder for StructBuilder {
68
    fn as_any(&self) -> &dyn Any {
×
69
        self
×
70
    }
×
71

72
    fn as_any_mut(&mut self) -> &mut dyn Any {
×
73
        self
×
74
    }
×
75

76
    fn dtype(&self) -> &DType {
×
77
        &self.dtype
×
78
    }
×
79

UNCOV
80
    fn len(&self) -> usize {
×
UNCOV
81
        self.validity.len()
×
UNCOV
82
    }
×
83

84
    fn append_zeros(&mut self, n: usize) {
×
85
        self.builders
×
86
            .iter_mut()
×
87
            .for_each(|builder| builder.append_zeros(n));
×
88
        self.validity.append_n_non_nulls(n);
×
89
    }
×
90

UNCOV
91
    fn append_nulls(&mut self, n: usize) {
×
UNCOV
92
        self.builders
×
UNCOV
93
            .iter_mut()
×
94
            // We push zero values into our children when appending a null in case the children are
95
            // themselves non-nullable.
UNCOV
96
            .for_each(|builder| builder.append_zeros(n));
×
UNCOV
97
        self.validity.append_null();
×
UNCOV
98
    }
×
99

100
    fn extend_from_array(&mut self, array: &dyn Array) -> VortexResult<()> {
×
101
        let array = array.to_struct()?;
×
102

103
        if array.dtype() != self.dtype() {
×
104
            vortex_bail!(
×
105
                "Cannot extend from array with different dtype: expected {}, found {}",
×
106
                self.dtype(),
×
107
                array.dtype()
×
108
            );
109
        }
×
110

111
        for (a, builder) in (0..array.struct_fields().nfields())
×
112
            .map(|i| &array.fields()[i])
×
113
            .zip_eq(self.builders.iter_mut())
×
114
        {
115
            a.append_to_builder(builder.as_mut())?;
×
116
        }
117

118
        self.validity.append_validity_mask(array.validity_mask()?);
×
119
        Ok(())
×
120
    }
×
121

122
    fn ensure_capacity(&mut self, capacity: usize) {
×
123
        self.builders.iter_mut().for_each(|builder| {
×
124
            builder.ensure_capacity(capacity);
×
125
        });
×
126
        self.validity.ensure_capacity(capacity);
×
127
    }
×
128

129
    fn set_validity(&mut self, validity: Mask) {
×
130
        self.validity = LazyNullBufferBuilder::new(validity.len());
×
131
        self.validity.append_validity_mask(validity);
×
132
    }
×
133

UNCOV
134
    fn finish(&mut self) -> ArrayRef {
×
UNCOV
135
        let len = self.len();
×
UNCOV
136
        let fields = self
×
UNCOV
137
            .builders
×
UNCOV
138
            .iter_mut()
×
UNCOV
139
            .map(|builder| builder.finish())
×
UNCOV
140
            .collect::<Vec<_>>();
×
141

UNCOV
142
        if fields.len() > 1 {
×
UNCOV
143
            let expected_length = fields[0].len();
×
UNCOV
144
            for (index, field) in fields[1..].iter().enumerate() {
×
UNCOV
145
                assert_eq!(
×
UNCOV
146
                    field.len(),
×
147
                    expected_length,
148
                    "Field {index} does not have expected length {expected_length}"
×
149
                );
150
            }
UNCOV
151
        }
×
152

UNCOV
153
        let validity = self.validity.finish_with_nullability(self.nullability);
×
154

UNCOV
155
        StructArray::try_new_with_dtype(fields, self.struct_dtype.clone(), len, validity)
×
UNCOV
156
            .vortex_expect("Fields must all have same length.")
×
UNCOV
157
            .into_array()
×
UNCOV
158
    }
×
159
}
160

161
#[cfg(test)]
162
mod tests {
163
    use std::sync::Arc;
164

165
    use vortex_dtype::PType::I32;
166
    use vortex_dtype::{DType, Nullability, StructFields};
167
    use vortex_scalar::Scalar;
168

169
    use crate::builders::ArrayBuilder;
170
    use crate::builders::struct_::StructBuilder;
171

172
    #[test]
173
    fn test_struct_builder() {
174
        let sdt = StructFields::new(
175
            vec![Arc::from("a"), Arc::from("b")].into(),
176
            vec![I32.into(), I32.into()],
177
        );
178
        let dtype = DType::Struct(sdt.clone(), Nullability::NonNullable);
179
        let mut builder = StructBuilder::with_capacity(sdt, Nullability::NonNullable, 0);
180

181
        builder
182
            .append_value(Scalar::struct_(dtype.clone(), vec![1.into(), 2.into()]).as_struct())
183
            .unwrap();
184

185
        let struct_ = builder.finish();
186
        assert_eq!(struct_.len(), 1);
187
        assert_eq!(struct_.dtype(), &dtype);
188
    }
189

190
    #[test]
191
    fn test_append_nullable_struct() {
192
        let sdt = StructFields::new(
193
            vec![Arc::from("a"), Arc::from("b")].into(),
194
            vec![I32.into(), I32.into()],
195
        );
196
        let dtype = DType::Struct(sdt.clone(), Nullability::Nullable);
197
        let mut builder = StructBuilder::with_capacity(sdt, Nullability::Nullable, 0);
198

199
        builder
200
            .append_value(Scalar::struct_(dtype.clone(), vec![1.into(), 2.into()]).as_struct())
201
            .unwrap();
202

203
        let struct_ = builder.finish();
204
        assert_eq!(struct_.len(), 1);
205
        assert_eq!(struct_.dtype(), &dtype);
206
    }
207
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc