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

vortex-data / vortex / 16331938722

16 Jul 2025 10:49PM UTC coverage: 80.702% (-0.9%) from 81.557%
16331938722

push

github

web-flow
feat: build with stable rust (#3881)

120 of 173 new or added lines in 28 files covered. (69.36%)

174 existing lines in 102 files now uncovered.

41861 of 51871 relevant lines covered (80.7%)

157487.71 hits per line

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

96.6
/vortex-array/src/arrays/struct_/compute/mod.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
mod cast;
5
mod filter;
6
mod mask;
7

8
use itertools::Itertools;
9
use vortex_dtype::Nullability::NonNullable;
10
use vortex_error::VortexResult;
11
use vortex_scalar::Scalar;
12

13
use crate::arrays::StructVTable;
14
use crate::arrays::struct_::StructArray;
15
use crate::compute::{
16
    IsConstantKernel, IsConstantKernelAdapter, IsConstantOpts, MinMaxKernel, MinMaxKernelAdapter,
17
    MinMaxResult, TakeKernel, TakeKernelAdapter, fill_null, is_constant_opts, take,
18
};
19
use crate::validity::Validity;
20
use crate::vtable::ValidityHelper;
21
use crate::{Array, ArrayRef, IntoArray, register_kernel};
22

23
impl TakeKernel for StructVTable {
24
    fn take(&self, array: &StructArray, indices: &dyn Array) -> VortexResult<ArrayRef> {
4✔
25
        // If the struct array is empty then the indices must be all null, otherwise it will access
26
        // an out of bounds element
27
        if array.is_empty() {
4✔
28
            return StructArray::try_new_with_dtype(
×
29
                array.fields().to_vec(),
×
30
                array.struct_fields().clone(),
×
31
                indices.len(),
×
32
                Validity::AllInvalid,
×
33
            )
34
            .map(StructArray::into_array);
×
35
        }
4✔
36
        // The validity is applied to the struct validity,
37
        let inner_indices = &fill_null(
4✔
38
            indices,
4✔
39
            &Scalar::default_value(indices.dtype().with_nullability(NonNullable)),
4✔
UNCOV
40
        )?;
×
41
        StructArray::try_new_with_dtype(
4✔
42
            array
4✔
43
                .fields()
4✔
44
                .iter()
4✔
45
                .map(|field| take(field, inner_indices))
4✔
46
                .try_collect()?,
4✔
47
            array.struct_fields().clone(),
4✔
48
            indices.len(),
4✔
49
            array.validity().take(indices)?,
4✔
50
        )
51
        .map(|a| a.into_array())
4✔
52
    }
4✔
53
}
54

55
register_kernel!(TakeKernelAdapter(StructVTable).lift());
56

57
impl MinMaxKernel for StructVTable {
58
    fn min_max(&self, _array: &StructArray) -> VortexResult<Option<MinMaxResult>> {
4,708✔
59
        // TODO(joe): Implement struct min max
60
        Ok(None)
4,708✔
61
    }
4,708✔
62
}
63

64
register_kernel!(MinMaxKernelAdapter(StructVTable).lift());
65

66
impl IsConstantKernel for StructVTable {
67
    fn is_constant(
136✔
68
        &self,
136✔
69
        array: &StructArray,
136✔
70
        opts: &IsConstantOpts,
136✔
71
    ) -> VortexResult<Option<bool>> {
136✔
72
        let children = array.children();
136✔
73
        if children.is_empty() {
136✔
74
            return Ok(None);
4✔
75
        }
132✔
76

77
        for child in children.iter() {
306✔
78
            match is_constant_opts(child, opts)? {
306✔
79
                // Un-determined
80
                None => return Ok(None),
×
81
                Some(false) => return Ok(Some(false)),
99✔
82
                Some(true) => {}
207✔
83
            }
84
        }
85

86
        Ok(Some(true))
33✔
87
    }
136✔
88
}
89

90
register_kernel!(IsConstantKernelAdapter(StructVTable).lift());
91

92
#[cfg(test)]
93
mod tests {
94

95
    use Nullability::{NonNullable, Nullable};
96
    use vortex_buffer::buffer;
97
    use vortex_dtype::{DType, FieldNames, Nullability, PType, StructFields};
98
    use vortex_mask::Mask;
99
    use vortex_scalar::Scalar;
100

101
    use crate::arrays::{BoolArray, BooleanBuffer, PrimitiveArray, StructArray, VarBinArray};
102
    use crate::compute::conformance::mask::test_mask;
103
    use crate::compute::{cast, filter, take};
104
    use crate::validity::Validity;
105
    use crate::{Array, IntoArray as _};
106

107
    #[test]
108
    fn filter_empty_struct() {
1✔
109
        let struct_arr =
1✔
110
            StructArray::try_new(vec![].into(), vec![], 10, Validity::NonNullable).unwrap();
1✔
111
        let mask = vec![
1✔
112
            false, true, false, true, false, true, false, true, false, true,
113
        ];
114
        let filtered = filter(struct_arr.as_ref(), &Mask::from_iter(mask)).unwrap();
1✔
115
        assert_eq!(filtered.len(), 5);
1✔
116
    }
1✔
117

118
    #[test]
119
    fn take_empty_struct() {
1✔
120
        let struct_arr =
1✔
121
            StructArray::try_new(vec![].into(), vec![], 10, Validity::NonNullable).unwrap();
1✔
122
        let indices = PrimitiveArray::from_option_iter([Some(1), None]);
1✔
123
        let taken = take(struct_arr.as_ref(), indices.as_ref()).unwrap();
1✔
124
        assert_eq!(taken.len(), 2);
1✔
125

126
        assert_eq!(
1✔
127
            taken.scalar_at(0).unwrap(),
1✔
128
            Scalar::struct_(
1✔
129
                DType::Struct(StructFields::new(FieldNames::default(), vec![]), Nullable),
1✔
130
                vec![]
1✔
131
            )
132
        );
133
        assert_eq!(
1✔
134
            taken.scalar_at(1).unwrap(),
1✔
135
            Scalar::null(DType::Struct(
1✔
136
                StructFields::new(FieldNames::default(), vec![]),
1✔
137
                Nullable
1✔
138
            ))
1✔
139
        );
140
    }
1✔
141

142
    #[test]
143
    fn take_field_struct() {
1✔
144
        let struct_arr =
1✔
145
            StructArray::from_fields(&[("a", PrimitiveArray::from_iter(0..10).to_array())])
1✔
146
                .unwrap();
1✔
147
        let indices = PrimitiveArray::from_option_iter([Some(1), None]);
1✔
148
        let taken = take(struct_arr.as_ref(), indices.as_ref()).unwrap();
1✔
149
        assert_eq!(taken.len(), 2);
1✔
150

151
        assert_eq!(
1✔
152
            taken.scalar_at(0).unwrap(),
1✔
153
            Scalar::struct_(
1✔
154
                struct_arr.dtype().union_nullability(Nullable),
1✔
155
                vec![Scalar::primitive(1, NonNullable)],
1✔
156
            )
157
        );
158
        assert_eq!(
1✔
159
            taken.scalar_at(1).unwrap(),
1✔
160
            Scalar::null(struct_arr.dtype().union_nullability(Nullable),)
1✔
161
        );
162
    }
1✔
163

164
    #[test]
165
    fn filter_empty_struct_with_empty_filter() {
1✔
166
        let struct_arr =
1✔
167
            StructArray::try_new(vec![].into(), vec![], 0, Validity::NonNullable).unwrap();
1✔
168
        let filtered = filter(struct_arr.as_ref(), &Mask::from_iter::<[bool; 0]>([])).unwrap();
1✔
169
        assert_eq!(filtered.len(), 0);
1✔
170
    }
1✔
171

172
    #[test]
173
    fn test_mask_empty_struct() {
1✔
174
        test_mask(
1✔
175
            StructArray::try_new(vec![].into(), vec![], 5, Validity::NonNullable)
1✔
176
                .unwrap()
1✔
177
                .as_ref(),
1✔
178
        );
179
    }
1✔
180

181
    #[test]
182
    fn test_mask_complex_struct() {
1✔
183
        let xs = buffer![0i64, 1, 2, 3, 4].into_array();
1✔
184
        let ys = VarBinArray::from_iter(
1✔
185
            [Some("a"), Some("b"), None, Some("d"), None],
1✔
186
            DType::Utf8(Nullable),
1✔
187
        )
188
        .into_array();
1✔
189
        let zs =
1✔
190
            BoolArray::from_iter([Some(true), Some(true), None, None, Some(false)]).into_array();
1✔
191

192
        test_mask(
1✔
193
            StructArray::try_new(
1✔
194
                ["xs", "ys", "zs"].into(),
1✔
195
                vec![
1✔
196
                    StructArray::try_new(
1✔
197
                        ["left", "right"].into(),
1✔
198
                        vec![xs.clone(), xs],
1✔
199
                        5,
1✔
200
                        Validity::NonNullable,
1✔
201
                    )
1✔
202
                    .unwrap()
1✔
203
                    .into_array(),
1✔
204
                    ys,
1✔
205
                    zs,
1✔
206
                ],
1✔
207
                5,
1✔
208
                Validity::NonNullable,
1✔
209
            )
1✔
210
            .unwrap()
1✔
211
            .as_ref(),
1✔
212
        );
213
    }
1✔
214

215
    #[test]
216
    fn test_cast_empty_struct() {
1✔
217
        let array = StructArray::try_new(FieldNames::default(), vec![], 5, Validity::NonNullable)
1✔
218
            .unwrap()
1✔
219
            .into_array();
1✔
220
        let non_nullable_dtype = DType::Struct(
1✔
221
            StructFields::new(FieldNames::default(), vec![]),
1✔
222
            NonNullable,
1✔
223
        );
1✔
224
        let casted = cast(&array, &non_nullable_dtype).unwrap();
1✔
225
        assert_eq!(casted.dtype(), &non_nullable_dtype);
1✔
226

227
        let nullable_dtype =
1✔
228
            DType::Struct(StructFields::new(FieldNames::default(), vec![]), Nullable);
1✔
229
        let casted = cast(&array, &nullable_dtype).unwrap();
1✔
230
        assert_eq!(casted.dtype(), &nullable_dtype);
1✔
231
    }
1✔
232

233
    #[test]
234
    fn test_cast_cannot_change_name_order() {
1✔
235
        let array = StructArray::try_new(
1✔
236
            ["xs", "ys", "zs"].into(),
1✔
237
            vec![
1✔
238
                buffer![1u8].into_array(),
1✔
239
                buffer![1u8].into_array(),
1✔
240
                buffer![1u8].into_array(),
1✔
241
            ],
242
            1,
243
            Validity::NonNullable,
1✔
244
        )
245
        .unwrap();
1✔
246

247
        let tu8 = DType::Primitive(PType::U8, NonNullable);
1✔
248

249
        let result = cast(
1✔
250
            array.as_ref(),
1✔
251
            &DType::Struct(
1✔
252
                StructFields::new(
1✔
253
                    FieldNames::from(["ys", "xs", "zs"]),
1✔
254
                    vec![tu8.clone(), tu8.clone(), tu8],
1✔
255
                ),
1✔
256
                NonNullable,
1✔
257
            ),
1✔
258
        );
259
        assert!(
1✔
260
            result.as_ref().is_err_and(|err| {
1✔
261
                err.to_string()
1✔
262
                    .contains("cannot cast {xs=u8, ys=u8, zs=u8} to {ys=u8, xs=u8, zs=u8}")
1✔
263
            }),
1✔
264
            "{result:?}"
265
        );
266
    }
1✔
267

268
    #[test]
269
    fn test_cast_complex_struct() {
1✔
270
        let xs = PrimitiveArray::from_option_iter([Some(0i64), Some(1), Some(2), Some(3), Some(4)]);
1✔
271
        let ys = VarBinArray::from_vec(vec!["a", "b", "c", "d", "e"], DType::Utf8(Nullable));
1✔
272
        let zs = BoolArray::new(
1✔
273
            BooleanBuffer::from_iter([true, true, false, false, true]),
1✔
274
            Validity::AllValid,
1✔
275
        );
276
        let fully_nullable_array = StructArray::try_new(
1✔
277
            ["xs", "ys", "zs"].into(),
1✔
278
            vec![
1✔
279
                StructArray::try_new(
1✔
280
                    ["left", "right"].into(),
1✔
281
                    vec![xs.to_array(), xs.to_array()],
1✔
282
                    5,
1✔
283
                    Validity::AllValid,
1✔
284
                )
1✔
285
                .unwrap()
1✔
286
                .into_array(),
1✔
287
                ys.into_array(),
1✔
288
                zs.into_array(),
1✔
289
            ],
290
            5,
291
            Validity::AllValid,
1✔
292
        )
293
        .unwrap()
1✔
294
        .into_array();
1✔
295

296
        let top_level_non_nullable = fully_nullable_array.dtype().as_nonnullable();
1✔
297
        let casted = cast(&fully_nullable_array, &top_level_non_nullable).unwrap();
1✔
298
        assert_eq!(casted.dtype(), &top_level_non_nullable);
1✔
299

300
        let non_null_xs_right = DType::Struct(
1✔
301
            StructFields::new(
1✔
302
                ["xs", "ys", "zs"].into(),
1✔
303
                vec![
1✔
304
                    DType::Struct(
1✔
305
                        StructFields::new(
1✔
306
                            ["left", "right"].into(),
1✔
307
                            vec![
1✔
308
                                DType::Primitive(PType::I64, NonNullable),
1✔
309
                                DType::Primitive(PType::I64, Nullable),
1✔
310
                            ],
1✔
311
                        ),
1✔
312
                        Nullable,
1✔
313
                    ),
1✔
314
                    DType::Utf8(Nullable),
1✔
315
                    DType::Bool(Nullable),
1✔
316
                ],
1✔
317
            ),
1✔
318
            Nullable,
1✔
319
        );
1✔
320
        let casted = cast(&fully_nullable_array, &non_null_xs_right).unwrap();
1✔
321
        assert_eq!(casted.dtype(), &non_null_xs_right);
1✔
322

323
        let non_null_xs = DType::Struct(
1✔
324
            StructFields::new(
1✔
325
                ["xs", "ys", "zs"].into(),
1✔
326
                vec![
1✔
327
                    DType::Struct(
1✔
328
                        StructFields::new(
1✔
329
                            ["left", "right"].into(),
1✔
330
                            vec![
1✔
331
                                DType::Primitive(PType::I64, Nullable),
1✔
332
                                DType::Primitive(PType::I64, Nullable),
1✔
333
                            ],
1✔
334
                        ),
1✔
335
                        NonNullable,
1✔
336
                    ),
1✔
337
                    DType::Utf8(Nullable),
1✔
338
                    DType::Bool(Nullable),
1✔
339
                ],
1✔
340
            ),
1✔
341
            Nullable,
1✔
342
        );
1✔
343
        let casted = cast(&fully_nullable_array, &non_null_xs).unwrap();
1✔
344
        assert_eq!(casted.dtype(), &non_null_xs);
1✔
345
    }
1✔
346
}
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