• 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/arrow/compute/to_arrow/mod.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
mod canonical;
5
mod temporal;
6
mod varbin;
7

8
use std::any::Any;
9
use std::sync::LazyLock;
10

11
use arcref::ArcRef;
12
use arrow_array::ArrayRef as ArrowArrayRef;
13
use arrow_schema::DataType;
14
use vortex_dtype::DType;
15
use vortex_dtype::arrow::FromArrowType;
16
use vortex_error::{VortexError, VortexExpect, VortexResult, vortex_bail, vortex_err};
17

18
use crate::Array;
19
use crate::arrow::array::{ArrowArray, ArrowVTable};
20
use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Options, Output};
21
use crate::vtable::VTable;
22

UNCOV
23
static TO_ARROW_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
×
UNCOV
24
    let compute = ComputeFn::new("to_arrow".into(), ArcRef::new_ref(&ToArrow));
×
25

26
    // Register the kernels we ship ourselves
UNCOV
27
    compute.register_kernel(ArcRef::new_ref(&canonical::ToArrowCanonical));
×
UNCOV
28
    compute.register_kernel(ArcRef::new_ref(&temporal::ToArrowTemporal));
×
29

UNCOV
30
    for kernel in inventory::iter::<ToArrowKernelRef> {
×
UNCOV
31
        compute.register_kernel(kernel.0.clone());
×
UNCOV
32
    }
×
UNCOV
33
    compute
×
UNCOV
34
});
×
35

36
/// Convert a Vortex array to an Arrow array with the encoding's preferred `DataType`.
37
///
38
/// For example, a `VarBinArray` will be converted to an Arrow `VarBin` array, instead of the
39
/// canonical `VarBinViewArray`.
40
///
41
/// Warning: do not use this to convert a Vortex [`crate::stream::ArrayStream`] since each array
42
/// may have a different preferred Arrow type. Use [`to_arrow`] instead.
43
pub fn to_arrow_preferred(array: &dyn Array) -> VortexResult<ArrowArrayRef> {
×
44
    to_arrow_opts(array, &ToArrowOptions { arrow_type: None })
×
45
}
×
46

47
/// Convert a Vortex array to an Arrow array of the given type.
UNCOV
48
pub fn to_arrow(array: &dyn Array, arrow_type: &DataType) -> VortexResult<ArrowArrayRef> {
×
UNCOV
49
    to_arrow_opts(
×
UNCOV
50
        array,
×
UNCOV
51
        &ToArrowOptions {
×
UNCOV
52
            arrow_type: Some(arrow_type.clone()),
×
UNCOV
53
        },
×
54
    )
UNCOV
55
}
×
56

UNCOV
57
pub fn to_arrow_opts(array: &dyn Array, options: &ToArrowOptions) -> VortexResult<ArrowArrayRef> {
×
UNCOV
58
    let arrow = TO_ARROW_FN
×
UNCOV
59
        .invoke(&InvocationArgs {
×
UNCOV
60
            inputs: &[array.into()],
×
UNCOV
61
            options,
×
UNCOV
62
        })?
×
UNCOV
63
        .unwrap_array()?
×
UNCOV
64
        .as_opt::<ArrowVTable>()
×
UNCOV
65
        .ok_or_else(|| vortex_err!("ToArrow compute kernels must return a Vortex ArrowArray"))?
×
UNCOV
66
        .inner()
×
UNCOV
67
        .clone();
×
68

UNCOV
69
    if let Some(arrow_type) = &options.arrow_type
×
UNCOV
70
        && arrow.data_type() != arrow_type
×
71
    {
72
        vortex_bail!(
×
73
            "Arrow array type mismatch: expected {:?}, got {:?}",
×
74
            &options.arrow_type,
×
75
            arrow.data_type()
×
76
        );
UNCOV
77
    }
×
78

UNCOV
79
    Ok(arrow)
×
UNCOV
80
}
×
81

82
pub struct ToArrowOptions {
83
    /// The Arrow data type to convert to, if specified.
84
    pub arrow_type: Option<DataType>,
85
}
86

87
impl Options for ToArrowOptions {
UNCOV
88
    fn as_any(&self) -> &dyn Any {
×
UNCOV
89
        self
×
UNCOV
90
    }
×
91
}
92

93
struct ToArrow;
94

95
impl ComputeFnVTable for ToArrow {
UNCOV
96
    fn invoke(
×
UNCOV
97
        &self,
×
UNCOV
98
        args: &InvocationArgs,
×
UNCOV
99
        kernels: &[ArcRef<dyn Kernel>],
×
UNCOV
100
    ) -> VortexResult<Output> {
×
UNCOV
101
        let ToArrowArgs { array, arrow_type } = ToArrowArgs::try_from(args)?;
×
102

UNCOV
103
        for kernel in kernels {
×
UNCOV
104
            if let Some(output) = kernel.invoke(args)? {
×
UNCOV
105
                return Ok(output);
×
UNCOV
106
            }
×
107
        }
UNCOV
108
        if let Some(output) = array.invoke(&TO_ARROW_FN, args)? {
×
109
            return Ok(output);
×
UNCOV
110
        }
×
111

112
        // Fall back to canonicalizing and then converting.
UNCOV
113
        if !array.is_canonical() {
×
UNCOV
114
            let canonical_array = array.to_canonical()?;
×
UNCOV
115
            let arrow_array = to_arrow_opts(
×
UNCOV
116
                canonical_array.as_ref(),
×
UNCOV
117
                &ToArrowOptions {
×
UNCOV
118
                    arrow_type: arrow_type.cloned(),
×
UNCOV
119
                },
×
120
            )?;
×
UNCOV
121
            return Ok(ArrowArray::new(arrow_array, array.dtype().nullability())
×
UNCOV
122
                .to_array()
×
UNCOV
123
                .into());
×
124
        }
×
125

126
        vortex_bail!(
×
127
            "Failed to convert array {} to Arrow {:?}",
×
128
            array.encoding_id(),
×
129
            arrow_type
130
        );
UNCOV
131
    }
×
132

UNCOV
133
    fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
×
UNCOV
134
        let ToArrowArgs { array, arrow_type } = ToArrowArgs::try_from(args)?;
×
UNCOV
135
        Ok(arrow_type
×
UNCOV
136
            .map(|arrow_type| DType::from_arrow((arrow_type, array.dtype().nullability())))
×
UNCOV
137
            .unwrap_or_else(|| array.dtype().clone()))
×
UNCOV
138
    }
×
139

UNCOV
140
    fn return_len(&self, args: &InvocationArgs) -> VortexResult<usize> {
×
UNCOV
141
        let ToArrowArgs { array, .. } = ToArrowArgs::try_from(args)?;
×
UNCOV
142
        Ok(array.len())
×
UNCOV
143
    }
×
144

UNCOV
145
    fn is_elementwise(&self) -> bool {
×
UNCOV
146
        false
×
UNCOV
147
    }
×
148
}
149

150
pub struct ToArrowArgs<'a> {
151
    array: &'a dyn Array,
152
    arrow_type: Option<&'a DataType>,
153
}
154

155
impl<'a> TryFrom<&InvocationArgs<'a>> for ToArrowArgs<'a> {
156
    type Error = VortexError;
157

UNCOV
158
    fn try_from(value: &InvocationArgs<'a>) -> Result<Self, Self::Error> {
×
UNCOV
159
        if value.inputs.len() != 1 {
×
160
            vortex_bail!("Expected 1 input, found {}", value.inputs.len());
×
UNCOV
161
        }
×
UNCOV
162
        let array = value.inputs[0]
×
UNCOV
163
            .array()
×
UNCOV
164
            .ok_or_else(|| vortex_err!("Expected input 0 to be an array"))?;
×
UNCOV
165
        let options = value
×
UNCOV
166
            .options
×
UNCOV
167
            .as_any()
×
UNCOV
168
            .downcast_ref::<ToArrowOptions>()
×
UNCOV
169
            .vortex_expect("Expected options to be ToArrowOptions");
×
170

UNCOV
171
        Ok(ToArrowArgs {
×
UNCOV
172
            array,
×
UNCOV
173
            arrow_type: options.arrow_type.as_ref(),
×
UNCOV
174
        })
×
UNCOV
175
    }
×
176
}
177

178
pub struct ToArrowKernelRef(pub ArcRef<dyn Kernel>);
179
inventory::collect!(ToArrowKernelRef);
180

181
pub trait ToArrowKernel: VTable {
182
    fn to_arrow(
183
        &self,
184
        arr: &Self::Array,
185
        arrow_type: Option<&DataType>,
186
    ) -> VortexResult<Option<ArrowArrayRef>>;
187
}
188

189
#[derive(Debug)]
190
pub struct ToArrowKernelAdapter<V: VTable>(pub V);
191

192
impl<V: VTable + ToArrowKernel> ToArrowKernelAdapter<V> {
193
    pub const fn lift(&'static self) -> ToArrowKernelRef {
×
194
        ToArrowKernelRef(ArcRef::new_ref(self))
×
195
    }
×
196
}
197

198
impl<V: VTable + ToArrowKernel> Kernel for ToArrowKernelAdapter<V> {
UNCOV
199
    fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
×
UNCOV
200
        let inputs = ToArrowArgs::try_from(args)?;
×
UNCOV
201
        let Some(array) = inputs.array.as_opt::<V>() else {
×
UNCOV
202
            return Ok(None);
×
203
        };
204

UNCOV
205
        let Some(arrow_array) = V::to_arrow(&self.0, array, inputs.arrow_type)? else {
×
UNCOV
206
            return Ok(None);
×
207
        };
208

UNCOV
209
        Ok(Some(
×
UNCOV
210
            ArrowArray::new(arrow_array, array.dtype().nullability())
×
UNCOV
211
                .to_array()
×
UNCOV
212
                .into(),
×
UNCOV
213
        ))
×
UNCOV
214
    }
×
215
}
216

217
#[cfg(test)]
218
mod tests {
219
    use std::sync::Arc;
220

221
    use arrow_array::types::Int32Type;
222
    use arrow_array::{ArrayRef, PrimitiveArray, StringViewArray, StructArray};
223
    use arrow_buffer::NullBuffer;
224

225
    use super::to_arrow;
226
    use crate::{IntoArray, arrays};
227

228
    #[test]
229
    fn test_to_arrow() {
230
        let array = arrays::StructArray::from_fields(
231
            vec![
232
                (
233
                    "a",
234
                    arrays::PrimitiveArray::from_option_iter(vec![Some(1), None, Some(2)])
235
                        .into_array(),
236
                ),
237
                (
238
                    "b",
239
                    arrays::VarBinViewArray::from_iter_str(vec!["a", "b", "c"]).into_array(),
240
                ),
241
            ]
242
            .as_slice(),
243
        )
244
        .unwrap();
245

246
        let arrow_array: ArrayRef = Arc::new(
247
            StructArray::try_from(vec![
248
                (
249
                    "a",
250
                    Arc::new(PrimitiveArray::<Int32Type>::from_iter_values_with_nulls(
251
                        vec![1, 0, 2],
252
                        Some(NullBuffer::from(vec![true, false, true])),
253
                    )) as ArrayRef,
254
                ),
255
                (
256
                    "b",
257
                    Arc::new(StringViewArray::from(vec![Some("a"), Some("b"), Some("c")])),
258
                ),
259
            ])
260
            .unwrap(),
261
        );
262

263
        assert_eq!(
264
            &to_arrow(array.as_ref(), &array.dtype().to_arrow_dtype().unwrap()).unwrap(),
265
            &arrow_array
266
        );
267
    }
268
}
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